@tomorrowevening/hermes 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/hermes.js +2530 -718
- package/dist/hermes.umd.cjs +88 -11
- package/dist/images/android-chrome-192x192.png +0 -0
- package/dist/images/android-chrome-512x512.png +0 -0
- package/dist/images/apple-touch-icon.png +0 -0
- package/dist/images/favicon-16x16.png +0 -0
- package/dist/images/favicon-32x32.png +0 -0
- package/dist/images/favicon.ico +0 -0
- package/dist/images/milkyWay/dark-s_nx.jpg +0 -0
- package/dist/images/milkyWay/dark-s_ny.jpg +0 -0
- package/dist/images/milkyWay/dark-s_nz.jpg +0 -0
- package/dist/images/milkyWay/dark-s_px.jpg +0 -0
- package/dist/images/milkyWay/dark-s_py.jpg +0 -0
- package/dist/images/milkyWay/dark-s_pz.jpg +0 -0
- package/dist/images/site.webmanifest +1 -0
- package/dist/images/uv_grid_opengl.jpg +0 -0
- package/dist/index-0a798fe4.js +6862 -0
- package/dist/index-7bad599d.css +1 -0
- package/dist/index.html +18 -0
- package/dist/models/Flair.fbx +0 -0
- package/dist/models/Thriller2.fbx +0 -0
- package/dist/models/Thriller4.fbx +0 -0
- package/dist/style.css +1 -1
- package/package.json +8 -3
- package/src/core/Application.ts +28 -36
- package/src/core/RemoteController.ts +148 -98
- package/src/core/remote/BaseRemote.ts +3 -3
- package/src/core/remote/RemoteComponents.ts +5 -8
- package/src/core/remote/RemoteTheatre.ts +56 -54
- package/src/core/remote/RemoteThree.ts +77 -0
- package/src/core/remote/RemoteTweakpane.ts +71 -78
- package/src/core/types.ts +14 -4
- package/src/editor/Editor.tsx +8 -6
- package/src/editor/components/Draggable.tsx +20 -20
- package/src/editor/components/DraggableItem.tsx +6 -6
- package/src/editor/components/Dropdown.tsx +22 -14
- package/src/editor/components/DropdownItem.tsx +19 -19
- package/src/editor/components/NavButton.tsx +1 -1
- package/src/editor/components/content.ts +2 -0
- package/src/editor/components/icons/CloseIcon.tsx +1 -1
- package/src/editor/components/icons/DragIcon.tsx +1 -1
- package/src/editor/global.ts +9 -4
- package/src/editor/sceneHierarchy/Accordion.tsx +40 -0
- package/src/editor/sceneHierarchy/ChildObject.tsx +17 -17
- package/src/editor/sceneHierarchy/ContainerObject.tsx +7 -8
- package/src/editor/sceneHierarchy/SceneHierarchy.tsx +52 -49
- package/src/editor/sceneHierarchy/ToggleBtn.tsx +26 -0
- package/src/editor/sceneHierarchy/inspector/Inspector.tsx +82 -0
- package/src/editor/sceneHierarchy/inspector/InspectorField.tsx +178 -0
- package/src/editor/sceneHierarchy/inspector/InspectorGroup.tsx +55 -0
- package/src/editor/sceneHierarchy/inspector/MultiView/CameraWindow.tsx +61 -0
- package/src/editor/sceneHierarchy/inspector/MultiView/InfiniteGridHelper.ts +24 -0
- package/src/editor/sceneHierarchy/inspector/MultiView/InfiniteGridMaterial.ts +127 -0
- package/src/editor/sceneHierarchy/inspector/MultiView/MultiView.scss +93 -0
- package/src/editor/sceneHierarchy/inspector/MultiView/MultiView.tsx +450 -0
- package/src/editor/sceneHierarchy/inspector/SceneInspector.tsx +72 -0
- package/src/editor/sceneHierarchy/inspector/inspector.scss +150 -0
- package/src/editor/sceneHierarchy/inspector/utils/InspectCamera.tsx +75 -0
- package/src/editor/sceneHierarchy/inspector/utils/InspectLight.tsx +62 -0
- package/src/editor/sceneHierarchy/inspector/utils/InspectMaterial.tsx +340 -0
- package/src/editor/sceneHierarchy/inspector/utils/InspectTransform.tsx +124 -0
- package/src/editor/sceneHierarchy/types.ts +116 -5
- package/src/editor/sceneHierarchy/utils.ts +207 -11
- package/src/editor/scss/_debug.scss +9 -19
- package/src/editor/scss/_dropdown.scss +1 -0
- package/src/editor/scss/_sceneHierarchy.scss +148 -48
- package/src/editor/scss/index.scss +13 -6
- package/src/editor/utils.ts +42 -5
- package/src/example/CustomEditor.tsx +40 -0
- package/src/example/components/App.tsx +164 -0
- package/src/example/constants.ts +40 -9
- package/src/example/main.tsx +5 -45
- package/src/example/three/CustomMaterial.ts +58 -0
- package/src/example/three/ExampleScene.ts +176 -0
- package/src/example/three/FBXAnimation.ts +39 -0
- package/src/index.ts +20 -14
- package/types/core/Application.d.ts +7 -13
- package/types/core/remote/RemoteComponents.d.ts +0 -2
- package/types/core/remote/RemoteThree.d.ts +12 -0
- package/types/core/remote/RemoteTweakpane.d.ts +3 -3
- package/types/core/types.d.ts +4 -3
- package/types/editor/Editor.d.ts +2 -1
- package/types/editor/components/content.d.ts +2 -0
- package/types/editor/global.d.ts +7 -2
- package/types/editor/sceneHierarchy/Accordion.d.ts +10 -0
- package/types/editor/sceneHierarchy/SceneHierarchy.d.ts +5 -5
- package/types/editor/sceneHierarchy/ToggleBtn.d.ts +7 -0
- package/types/editor/sceneHierarchy/inspector/Inspector.d.ts +3 -0
- package/types/editor/sceneHierarchy/inspector/InspectorField.d.ts +13 -0
- package/types/editor/sceneHierarchy/inspector/InspectorGroup.d.ts +7 -0
- package/types/editor/sceneHierarchy/inspector/MultiView/CameraWindow.d.ts +16 -0
- package/types/editor/sceneHierarchy/inspector/MultiView/InfiniteGridHelper.d.ts +7 -0
- package/types/editor/sceneHierarchy/inspector/MultiView/InfiniteGridMaterial.d.ts +13 -0
- package/types/editor/sceneHierarchy/inspector/MultiView/MultiView.d.ts +11 -0
- package/types/editor/sceneHierarchy/inspector/SceneInspector.d.ts +7 -0
- package/types/editor/sceneHierarchy/inspector/utils/InspectCamera.d.ts +3 -0
- package/types/editor/sceneHierarchy/inspector/utils/InspectLight.d.ts +3 -0
- package/types/editor/sceneHierarchy/inspector/utils/InspectMaterial.d.ts +8 -0
- package/types/editor/sceneHierarchy/inspector/utils/InspectTransform.d.ts +3 -0
- package/types/editor/sceneHierarchy/types.d.ts +98 -7
- package/types/editor/sceneHierarchy/utils.d.ts +7 -1
- package/types/editor/utils.d.ts +3 -0
- package/types/example/CustomEditor.d.ts +1 -0
- package/types/example/constants.d.ts +15 -3
- package/types/example/three/CustomMaterial.d.ts +5 -0
- package/types/example/three/ExampleScene.d.ts +18 -0
- package/types/example/three/FBXAnimation.d.ts +6 -0
- package/types/index.d.ts +5 -0
- package/src/example/App.tsx +0 -88
- package/types/core/remote/RemoteDebug.d.ts +0 -23
- package/types/debug/Editor.d.ts +0 -8
- package/types/debug/components/Draggable.d.ts +0 -2
- package/types/debug/components/DraggableItem.d.ts +0 -2
- package/types/debug/components/Dropdown.d.ts +0 -2
- package/types/debug/components/DropdownItem.d.ts +0 -2
- package/types/debug/components/NavButton.d.ts +0 -5
- package/types/debug/components/icons/CloseIcon.d.ts +0 -2
- package/types/debug/components/icons/DragIcon.d.ts +0 -2
- package/types/debug/components/types.d.ts +0 -31
- package/types/debug/global.d.ts +0 -9
- package/types/debug/sceneHierarchy/ChildObject.d.ts +0 -2
- package/types/debug/sceneHierarchy/ContainerObject.d.ts +0 -2
- package/types/debug/sceneHierarchy/SceneHierarchy.d.ts +0 -13
- package/types/debug/sceneHierarchy/types.d.ts +0 -8
- package/types/debug/sceneHierarchy/utils.d.ts +0 -2
- package/types/debug/utils.d.ts +0 -4
- package/types/library.d.ts +0 -14
- /package/src/example/{App.css → components/App.css} +0 -0
- /package/types/example/{App.d.ts → components/App.d.ts} +0 -0
@@ -1,24 +1,20 @@
|
|
1
1
|
// Libs
|
2
|
-
import { useState } from 'react'
|
3
|
-
import { Object3D } from 'three'
|
2
|
+
import { useState } from 'react';
|
3
|
+
import { Object3D } from 'three';
|
4
4
|
// Models
|
5
|
-
import {
|
6
|
-
import { ChildObjectProps } from './types'
|
5
|
+
import { ChildObjectProps } from './types';
|
7
6
|
// Utils
|
8
|
-
import { determineIcon } from './utils'
|
7
|
+
import { determineIcon } from './utils';
|
9
8
|
|
10
9
|
export default function ChildObject(props: ChildObjectProps) {
|
11
|
-
const [open, setOpen] = useState(
|
10
|
+
const [open, setOpen] = useState(props.child.children.length > 0);
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
const hasChildren = props.child.children.length > 0;
|
13
|
+
const children: Array<any> = [];
|
15
14
|
if (props.child.children.length > 0) {
|
16
|
-
hasChildren = true
|
17
|
-
const children: Array<any> = []
|
18
15
|
props.child.children.map((child: Object3D) => {
|
19
|
-
children.push(<ChildObject child={child} key={Math.random()} />)
|
20
|
-
})
|
21
|
-
container = <div className={`container ${!open ? 'closed' : ''}`}>{children}</div>
|
16
|
+
children.push(<ChildObject child={child} key={Math.random()} three={props.three} />);
|
17
|
+
});
|
22
18
|
}
|
23
19
|
|
24
20
|
return (
|
@@ -31,7 +27,7 @@ export default function ChildObject(props: ChildObjectProps) {
|
|
31
27
|
backgroundPositionX: open ? '-14px' : '2px',
|
32
28
|
}}
|
33
29
|
onClick={() => {
|
34
|
-
setOpen(!open)
|
30
|
+
setOpen(!open);
|
35
31
|
}}
|
36
32
|
></button>
|
37
33
|
) : null}
|
@@ -41,7 +37,7 @@ export default function ChildObject(props: ChildObjectProps) {
|
|
41
37
|
left: hasChildren ? '20px' : '5px',
|
42
38
|
}}
|
43
39
|
onClick={() => {
|
44
|
-
|
40
|
+
props.three.getObject(props.child.uuid);
|
45
41
|
}}
|
46
42
|
>
|
47
43
|
{props.child.name.length > 0
|
@@ -50,7 +46,11 @@ export default function ChildObject(props: ChildObjectProps) {
|
|
50
46
|
</button>
|
51
47
|
<div className={`icon ${determineIcon(props.child)}`}></div>
|
52
48
|
</div>
|
53
|
-
{
|
49
|
+
<div className={open ? 'open' : ''}>
|
50
|
+
<div className="container">
|
51
|
+
{children}
|
52
|
+
</div>
|
53
|
+
</div>
|
54
54
|
</div>
|
55
|
-
)
|
55
|
+
);
|
56
56
|
}
|
@@ -1,12 +1,11 @@
|
|
1
|
-
import { Object3D } from 'three'
|
2
|
-
import ChildObject from './ChildObject'
|
3
|
-
import type { ChildObjectProps } from './types'
|
1
|
+
import { Object3D } from 'three';
|
2
|
+
import ChildObject from './ChildObject';
|
3
|
+
import type { ChildObjectProps } from './types';
|
4
4
|
|
5
5
|
export default function ContainerObject(props: ChildObjectProps) {
|
6
|
-
const children: Array<any> = []
|
6
|
+
const children: Array<any> = [];
|
7
7
|
props.child.children.map((child: Object3D) => {
|
8
|
-
children.push(<ChildObject child={child} key={Math.random()} />)
|
9
|
-
})
|
10
|
-
|
11
|
-
return <div className="scene">{children}</div>
|
8
|
+
children.push(<ChildObject child={child} key={Math.random()} three={props.three} />);
|
9
|
+
});
|
10
|
+
return <div className={`scene ${props.class !== undefined ? props.class : ''}`}>{children}</div>;
|
12
11
|
}
|
@@ -1,77 +1,80 @@
|
|
1
1
|
// Libs
|
2
|
-
import { Component, ReactNode } from 'react'
|
2
|
+
import { Component, ReactNode } from 'react';
|
3
3
|
// Models
|
4
|
-
import { debugDispatcher, ToolEvents } from '../global'
|
4
|
+
import { debugDispatcher, ToolEvents } from '../global';
|
5
5
|
// Components
|
6
|
-
import '../scss/_sceneHierarchy.scss'
|
7
|
-
import
|
8
|
-
import
|
6
|
+
import '../scss/_sceneHierarchy.scss';
|
7
|
+
import Accordion from './Accordion';
|
8
|
+
import ContainerObject from './ContainerObject';
|
9
|
+
import Inspector from './inspector/Inspector';
|
10
|
+
import { SceneHierarchyState } from './types';
|
11
|
+
import RemoteThree from '@/core/remote/RemoteThree';
|
9
12
|
|
10
|
-
export default class SceneHierarchy extends Component {
|
11
|
-
|
12
|
-
|
13
|
+
export default class SceneHierarchy extends Component<SceneHierarchyState> {
|
14
|
+
private three: RemoteThree;
|
15
|
+
|
16
|
+
constructor(props: SceneHierarchyState) {
|
17
|
+
super(props);
|
13
18
|
this.state = {
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
scene: props.scene !== undefined ? props.scene : null,
|
20
|
+
};
|
21
|
+
this.three = props.three;
|
22
|
+
debugDispatcher.addEventListener(ToolEvents.SET_SCENE, this.setScene);
|
23
|
+
}
|
17
24
|
|
18
|
-
|
19
|
-
|
25
|
+
componentDidMount(): void {
|
26
|
+
this.onRefresh();
|
20
27
|
}
|
21
28
|
|
22
29
|
componentWillUnmount(): void {
|
23
|
-
debugDispatcher.removeEventListener(ToolEvents.
|
24
|
-
debugDispatcher.removeEventListener(ToolEvents.SET_SCENE, this.onSetScene)
|
30
|
+
debugDispatcher.removeEventListener(ToolEvents.SET_SCENE, this.setScene);
|
25
31
|
}
|
26
32
|
|
27
33
|
render(): ReactNode {
|
28
|
-
const
|
34
|
+
const hasScene = this.componentState.scene !== null;
|
35
|
+
const HierarchyName = 'Hierarchy' + (hasScene ? `: ${this.componentState.scene?.name}` : '');
|
29
36
|
return (
|
30
|
-
<div id="SceneHierarchy">
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
<div id="SceneHierarchy" key="SceneHierarchy">
|
38
|
+
{(
|
39
|
+
<>
|
40
|
+
{hasScene && (
|
41
|
+
<Accordion
|
42
|
+
label={HierarchyName}
|
43
|
+
button={(
|
44
|
+
<button className='icon refresh hideText' onClick={this.onRefresh}>
|
45
|
+
Refresh
|
46
|
+
</button>
|
47
|
+
)}
|
48
|
+
open={true}
|
49
|
+
>
|
50
|
+
<ContainerObject child={this.componentState.scene!} three={this.three} />
|
51
|
+
</Accordion>
|
52
|
+
)}
|
53
|
+
|
54
|
+
<Accordion label='Inspector'>
|
55
|
+
<Inspector key="Inspector" three={this.three} />
|
56
|
+
</Accordion>
|
57
|
+
</>
|
58
|
+
)}
|
45
59
|
</div>
|
46
|
-
)
|
60
|
+
);
|
47
61
|
}
|
48
62
|
|
49
63
|
// Private
|
50
64
|
|
51
|
-
private onUpdate = () => {
|
52
|
-
//
|
53
|
-
}
|
54
|
-
|
55
|
-
private toggleOpen = () => {
|
56
|
-
this.setState(()=> ({
|
57
|
-
open: !this.componentState.open,
|
58
|
-
}))
|
59
|
-
}
|
60
|
-
|
61
65
|
private onRefresh = () => {
|
62
|
-
|
63
|
-
}
|
66
|
+
this.three.getScene();
|
67
|
+
};
|
64
68
|
|
65
|
-
private
|
66
|
-
console.log('SceneHierarchy::onSetScene', evt)
|
69
|
+
private setScene = (evt: any) => {
|
67
70
|
this.setState(() => ({
|
68
71
|
scene: evt.value
|
69
|
-
}))
|
70
|
-
}
|
72
|
+
}));
|
73
|
+
};
|
71
74
|
|
72
75
|
// Getters / Setters
|
73
76
|
|
74
77
|
get componentState(): SceneHierarchyState {
|
75
|
-
return this.state as SceneHierarchyState
|
78
|
+
return this.state as SceneHierarchyState;
|
76
79
|
}
|
77
80
|
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { useState } from "react";
|
2
|
+
|
3
|
+
type ToggleBtnProps = {
|
4
|
+
expanded: boolean
|
5
|
+
label: string
|
6
|
+
onClick: (expanded: boolean) => void;
|
7
|
+
}
|
8
|
+
|
9
|
+
export default function ToggleBtn(props: ToggleBtnProps) {
|
10
|
+
const [expanded, setExpanded] = useState(props.expanded);
|
11
|
+
return (
|
12
|
+
<button
|
13
|
+
className='toggleBtn'
|
14
|
+
onClick={() => {
|
15
|
+
const value = !expanded;
|
16
|
+
props.onClick(value);
|
17
|
+
setExpanded(value);
|
18
|
+
}}
|
19
|
+
style={{
|
20
|
+
backgroundPositionY: `${expanded ? 1 : -10}px`
|
21
|
+
}}
|
22
|
+
>
|
23
|
+
<p>{props.label}</p>
|
24
|
+
</button>
|
25
|
+
);
|
26
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import { useEffect, useState } from "react";
|
2
|
+
import { CoreComponentProps, RemoteObject } from "../types";
|
3
|
+
import { ToolEvents, debugDispatcher } from "../../global";
|
4
|
+
import './inspector.scss';
|
5
|
+
import InspectorField from './InspectorField';
|
6
|
+
// Utils
|
7
|
+
import { InspectCamera } from "./utils/InspectCamera";
|
8
|
+
import { InspectMaterial } from "./utils/InspectMaterial";
|
9
|
+
import { InspectTransform } from "./utils/InspectTransform";
|
10
|
+
import { InspectLight } from "./utils/InspectLight";
|
11
|
+
|
12
|
+
export default function Inspector(props: CoreComponentProps) {
|
13
|
+
const [lastRefresh, setLastRefresh] = useState(-1);
|
14
|
+
const [currentObject, setCurrentObject] = useState<RemoteObject>({
|
15
|
+
name: '',
|
16
|
+
uuid: '',
|
17
|
+
type: '',
|
18
|
+
visible: false,
|
19
|
+
matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
20
|
+
});
|
21
|
+
|
22
|
+
useEffect(() => {
|
23
|
+
function onSelectItem(evt: any) {
|
24
|
+
const obj = evt.value as RemoteObject;
|
25
|
+
setCurrentObject(obj);
|
26
|
+
setLastRefresh(Date.now());
|
27
|
+
}
|
28
|
+
|
29
|
+
debugDispatcher.addEventListener(ToolEvents.SET_OBJECT, onSelectItem);
|
30
|
+
return () => {
|
31
|
+
debugDispatcher.removeEventListener(ToolEvents.SET_OBJECT, onSelectItem);
|
32
|
+
};
|
33
|
+
}, []);
|
34
|
+
|
35
|
+
return (
|
36
|
+
<div id="Inspector" className={props.class} key={lastRefresh}>
|
37
|
+
{currentObject.uuid.length > 0 && (
|
38
|
+
<>
|
39
|
+
{/* Core */}
|
40
|
+
<InspectorField
|
41
|
+
type="string"
|
42
|
+
title="Name"
|
43
|
+
prop="name"
|
44
|
+
value={currentObject.name}
|
45
|
+
disabled={true}
|
46
|
+
/>
|
47
|
+
<InspectorField
|
48
|
+
type="string"
|
49
|
+
title="Type"
|
50
|
+
prop="type"
|
51
|
+
value={currentObject.type}
|
52
|
+
disabled={true}
|
53
|
+
/>
|
54
|
+
<InspectorField
|
55
|
+
type="string"
|
56
|
+
title="UUID"
|
57
|
+
prop="uuid"
|
58
|
+
value={currentObject.uuid}
|
59
|
+
disabled={true}
|
60
|
+
/>
|
61
|
+
<InspectorField
|
62
|
+
type="boolean"
|
63
|
+
title="Visible"
|
64
|
+
prop="visible"
|
65
|
+
value={currentObject.visible}
|
66
|
+
onChange={(key: string, value: any) => {
|
67
|
+
props.three.updateObject(currentObject.uuid, key, value);
|
68
|
+
}}
|
69
|
+
/>
|
70
|
+
{/* Transform */}
|
71
|
+
{InspectTransform(currentObject, props.three)}
|
72
|
+
{/* Camera */}
|
73
|
+
{currentObject.type.search('Camera') > -1 ? InspectCamera(currentObject, props.three) : null}
|
74
|
+
{/* Light */}
|
75
|
+
{currentObject.type.search('Light') > -1 ? InspectLight(currentObject, props.three) : null}
|
76
|
+
{/* Material */}
|
77
|
+
{currentObject.material !== undefined ? InspectMaterial(currentObject, props.three) : null}
|
78
|
+
</>
|
79
|
+
)}
|
80
|
+
</div>
|
81
|
+
);
|
82
|
+
}
|
@@ -0,0 +1,178 @@
|
|
1
|
+
import { colorToHex } from "@/editor/utils";
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
3
|
+
import { noImage } from "@/editor/components/content";
|
4
|
+
import { uploadLocalImage } from "./utils/InspectMaterial";
|
5
|
+
|
6
|
+
export type InspectorFieldType = 'string' | 'number' | 'boolean' | 'range' | 'color' | 'button' | 'image'
|
7
|
+
|
8
|
+
export interface InspectorFieldProps {
|
9
|
+
title: string
|
10
|
+
type: InspectorFieldType
|
11
|
+
prop?: string
|
12
|
+
value?: any
|
13
|
+
min?: number
|
14
|
+
max?: number
|
15
|
+
step?: number
|
16
|
+
disabled?: boolean
|
17
|
+
onChange?: (prop: string, value: any) => void
|
18
|
+
}
|
19
|
+
|
20
|
+
export default function InspectorField(props: InspectorFieldProps) {
|
21
|
+
let propsValue = props.value;
|
22
|
+
if (propsValue !== undefined) {
|
23
|
+
if (propsValue.isColor !== undefined) {
|
24
|
+
propsValue = colorToHex(props.value);
|
25
|
+
}
|
26
|
+
}
|
27
|
+
const [fieldValue, setFieldValue] = useState(propsValue);
|
28
|
+
const labelRef = useRef<HTMLLabelElement>(null);
|
29
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
30
|
+
const imgRefRef = useRef<HTMLImageElement>(null);
|
31
|
+
|
32
|
+
// Mouse dragging
|
33
|
+
useEffect(() => {
|
34
|
+
let mouseDown = false;
|
35
|
+
let mouseStart = -1;
|
36
|
+
let valueStart = 0;
|
37
|
+
let value = Number(fieldValue);
|
38
|
+
|
39
|
+
const onMouseDown = (evt: MouseEvent) => {
|
40
|
+
mouseDown = true;
|
41
|
+
valueStart = value;
|
42
|
+
mouseStart = evt.clientX;
|
43
|
+
};
|
44
|
+
|
45
|
+
const onMouseMove = (evt: MouseEvent) => {
|
46
|
+
if (!mouseDown) return;
|
47
|
+
const deltaAmt = props.step !== undefined ? props.step : 1;
|
48
|
+
const delta = (evt.clientX - mouseStart) * deltaAmt;
|
49
|
+
value = Number((valueStart + delta).toFixed(4));
|
50
|
+
if (inputRef.current !== null) inputRef.current.value = value.toString();
|
51
|
+
if (props.onChange !== undefined) props.onChange(props.prop !== undefined ? props.prop : props.title, value);
|
52
|
+
};
|
53
|
+
|
54
|
+
const onMouseUp = () => {
|
55
|
+
mouseDown = false;
|
56
|
+
};
|
57
|
+
|
58
|
+
const onRightClick = () => {
|
59
|
+
mouseDown = false;
|
60
|
+
};
|
61
|
+
|
62
|
+
const useMouse = props.type === 'number';
|
63
|
+
if (useMouse) {
|
64
|
+
labelRef.current?.addEventListener('mousedown', onMouseDown, false);
|
65
|
+
document.addEventListener('mouseup', onMouseUp, false);
|
66
|
+
document.addEventListener('mousemove', onMouseMove, false);
|
67
|
+
document.addEventListener('contextmenu', onRightClick, false);
|
68
|
+
}
|
69
|
+
return () => {
|
70
|
+
if (useMouse) {
|
71
|
+
labelRef.current?.removeEventListener('mousedown', onMouseDown);
|
72
|
+
document.removeEventListener('mouseup', onMouseUp);
|
73
|
+
document.removeEventListener('mousemove', onMouseMove);
|
74
|
+
document.removeEventListener('contextmenu', onRightClick);
|
75
|
+
}
|
76
|
+
};
|
77
|
+
}, [fieldValue]);
|
78
|
+
|
79
|
+
const textfield = props.type === 'string' && (fieldValue.length > 100 || fieldValue.search('\n') > -1);
|
80
|
+
const block = textfield || props.type === 'image';
|
81
|
+
|
82
|
+
const onChange = (evt: any) => {
|
83
|
+
let value = evt.target.value;
|
84
|
+
if (props.type === 'boolean') value = evt.target.checked;
|
85
|
+
setFieldValue(value);
|
86
|
+
if (props.onChange !== undefined) props.onChange(props.prop !== undefined ? props.prop : props.title, value);
|
87
|
+
};
|
88
|
+
|
89
|
+
return (
|
90
|
+
<div className={`field ${block ? 'block' : ''}`}>
|
91
|
+
{props.type !== 'button' && (
|
92
|
+
<label key="fieldLabel" ref={labelRef}>{props.title}</label>
|
93
|
+
)}
|
94
|
+
|
95
|
+
{props.type === 'string' && !textfield && (
|
96
|
+
<input
|
97
|
+
type="text"
|
98
|
+
disabled={props.disabled}
|
99
|
+
onChange={onChange}
|
100
|
+
value={fieldValue}
|
101
|
+
/>
|
102
|
+
)}
|
103
|
+
|
104
|
+
{props.type === 'string' && textfield && (
|
105
|
+
<textarea
|
106
|
+
cols={50}
|
107
|
+
rows={10}
|
108
|
+
disabled={true}
|
109
|
+
onChange={onChange}
|
110
|
+
value={fieldValue}
|
111
|
+
/>
|
112
|
+
)}
|
113
|
+
|
114
|
+
{props.type === 'boolean' && (
|
115
|
+
<input
|
116
|
+
type="checkbox"
|
117
|
+
disabled={props.disabled}
|
118
|
+
onChange={onChange}
|
119
|
+
checked={fieldValue}
|
120
|
+
/>
|
121
|
+
)}
|
122
|
+
|
123
|
+
{props.type === 'number' && (
|
124
|
+
<input
|
125
|
+
ref={inputRef}
|
126
|
+
type="number"
|
127
|
+
value={fieldValue}
|
128
|
+
min={props.min}
|
129
|
+
max={props.max}
|
130
|
+
step={props.step}
|
131
|
+
onChange={onChange}
|
132
|
+
/>
|
133
|
+
)}
|
134
|
+
|
135
|
+
{props.type === 'range' && (
|
136
|
+
<>
|
137
|
+
<input type="text" value={fieldValue.toString()} onChange={onChange} className="min" />
|
138
|
+
<input
|
139
|
+
disabled={props.disabled}
|
140
|
+
type="range"
|
141
|
+
value={fieldValue}
|
142
|
+
min={props.min}
|
143
|
+
max={props.max}
|
144
|
+
step={props.step}
|
145
|
+
onChange={onChange}
|
146
|
+
/>
|
147
|
+
</>
|
148
|
+
)}
|
149
|
+
|
150
|
+
{props.type === 'color' && (
|
151
|
+
<>
|
152
|
+
<input type="text" value={fieldValue.toString()} onChange={onChange} className="color" />
|
153
|
+
<input type="color" value={fieldValue} onChange={onChange} />
|
154
|
+
</>
|
155
|
+
)}
|
156
|
+
|
157
|
+
{props.type === 'button' && (
|
158
|
+
<button
|
159
|
+
onClick={() => {
|
160
|
+
if (props.onChange !== undefined) props.onChange(props.prop !== undefined ? props.prop : props.title, true);
|
161
|
+
}}
|
162
|
+
>
|
163
|
+
{props.title}
|
164
|
+
</button>
|
165
|
+
)}
|
166
|
+
|
167
|
+
{props.type === 'image' && (
|
168
|
+
<img ref={imgRefRef} onClick={() => {
|
169
|
+
uploadLocalImage()
|
170
|
+
.then((value: string) => {
|
171
|
+
imgRefRef.current!.src = value;
|
172
|
+
if (props.onChange !== undefined) props.onChange(props.prop !== undefined ? props.prop : props.title, value);
|
173
|
+
});
|
174
|
+
}} src={fieldValue.length > 0 ? fieldValue : noImage} />
|
175
|
+
)}
|
176
|
+
</div>
|
177
|
+
);
|
178
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import Accordion from '../Accordion';
|
2
|
+
import InspectorField, { InspectorFieldProps } from './InspectorField';
|
3
|
+
|
4
|
+
export interface InspectorGroupProps {
|
5
|
+
title: string
|
6
|
+
expanded?: boolean
|
7
|
+
items: InspectorFieldProps[] | InspectorGroupProps[]
|
8
|
+
}
|
9
|
+
|
10
|
+
function isGroup(obj: any): obj is InspectorGroupProps {
|
11
|
+
return 'items' in obj;
|
12
|
+
}
|
13
|
+
|
14
|
+
export default function InspectorGroup(props: InspectorGroupProps) {
|
15
|
+
// console.log('Group:', props.title);
|
16
|
+
function onChange(label: string, value: any) {
|
17
|
+
console.log('onChange:', label, value);
|
18
|
+
}
|
19
|
+
|
20
|
+
const children: any[] = [];
|
21
|
+
props.items.forEach((child: InspectorFieldProps | InspectorGroupProps) => {
|
22
|
+
if (isGroup(child)) {
|
23
|
+
children.push(
|
24
|
+
<InspectorGroup title={child.title} items={child.items} key={Math.random()} />
|
25
|
+
);
|
26
|
+
} else {
|
27
|
+
children.push(
|
28
|
+
<InspectorField
|
29
|
+
key={Math.random()}
|
30
|
+
title={child.title}
|
31
|
+
prop={child.prop}
|
32
|
+
value={child.value}
|
33
|
+
type={child.type}
|
34
|
+
min={child.min}
|
35
|
+
max={child.max}
|
36
|
+
step={child.step}
|
37
|
+
disabled={child.disabled}
|
38
|
+
onChange={(prop: string, value: any) => {
|
39
|
+
if (child.onChange !== undefined) {
|
40
|
+
child.onChange(prop, value);
|
41
|
+
} else {
|
42
|
+
onChange(prop, value);
|
43
|
+
}
|
44
|
+
}}
|
45
|
+
/>
|
46
|
+
);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
|
50
|
+
return (
|
51
|
+
<Accordion label={props.title} open={false}>
|
52
|
+
{children}
|
53
|
+
</Accordion>
|
54
|
+
);
|
55
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { ForwardedRef, forwardRef, useState } from 'react';
|
2
|
+
import { Camera } from 'three';
|
3
|
+
|
4
|
+
interface DropdownProps {
|
5
|
+
index: number
|
6
|
+
onSelect: (value: string) => void;
|
7
|
+
options: string[];
|
8
|
+
up?: boolean;
|
9
|
+
}
|
10
|
+
|
11
|
+
export const Dropdown = (props: DropdownProps) => {
|
12
|
+
const [isOpen, setIsOpen] = useState(false);
|
13
|
+
const [selectedOption, setSelectedOption] = useState(props.options[props.index]);
|
14
|
+
|
15
|
+
const handleToggle = () => {
|
16
|
+
setIsOpen(!isOpen);
|
17
|
+
};
|
18
|
+
|
19
|
+
const handleSelect = (option: any) => {
|
20
|
+
if (option !== selectedOption) {
|
21
|
+
props.onSelect(option);
|
22
|
+
setSelectedOption(option);
|
23
|
+
}
|
24
|
+
setIsOpen(false);
|
25
|
+
};
|
26
|
+
|
27
|
+
return (
|
28
|
+
<div className={`dropdown ${props.up === true ? 'up' : ''}`}>
|
29
|
+
<div className="dropdown-toggle" onClick={handleToggle}>
|
30
|
+
{selectedOption}
|
31
|
+
</div>
|
32
|
+
{isOpen && (
|
33
|
+
<ul className="dropdown-menu">
|
34
|
+
{props.options.map((option) => (
|
35
|
+
<li key={option} onClick={() => handleSelect(option)}>
|
36
|
+
{option}
|
37
|
+
</li>
|
38
|
+
))}
|
39
|
+
</ul>
|
40
|
+
)}
|
41
|
+
</div>
|
42
|
+
);
|
43
|
+
};
|
44
|
+
|
45
|
+
interface CameraWindowProps {
|
46
|
+
camera: Camera
|
47
|
+
onSelect: (value: string) => void;
|
48
|
+
options: string[];
|
49
|
+
}
|
50
|
+
|
51
|
+
const CameraWindow = forwardRef(function CameraWindow(props: CameraWindowProps, ref: ForwardedRef<HTMLDivElement>) {
|
52
|
+
const index = props.options.indexOf(props.camera.name);
|
53
|
+
return (
|
54
|
+
<div className='CameraWindow'>
|
55
|
+
<div ref={ref} className='clickable'></div>
|
56
|
+
<Dropdown index={index} options={props.options} onSelect={props.onSelect} up={true} />
|
57
|
+
</div>
|
58
|
+
);
|
59
|
+
});
|
60
|
+
|
61
|
+
export default CameraWindow;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { Mesh, PlaneGeometry } from 'three';
|
2
|
+
import InfiniteGridMaterial from './InfiniteGridMaterial';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Copied from:
|
6
|
+
* https://github.com/theatre-js/theatre/blob/main/packages/r3f/src/extension/InfiniteGridHelper/index.ts
|
7
|
+
*/
|
8
|
+
|
9
|
+
export default class InfiniteGridHelper extends Mesh {
|
10
|
+
gridMaterial: InfiniteGridMaterial;
|
11
|
+
|
12
|
+
constructor() {
|
13
|
+
const material = new InfiniteGridMaterial();
|
14
|
+
super(new PlaneGeometry(2, 2), material);
|
15
|
+
this.gridMaterial = material;
|
16
|
+
this.frustumCulled = false;
|
17
|
+
this.name = 'InfiniteGridHelper';
|
18
|
+
this.position.y = 0.1;
|
19
|
+
}
|
20
|
+
|
21
|
+
update() {
|
22
|
+
this.gridMaterial.needsUpdate = true;
|
23
|
+
}
|
24
|
+
}
|