@tomorrowevening/hermes 0.0.1 → 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.
Files changed (130) hide show
  1. package/README.md +7 -0
  2. package/dist/hermes.js +2530 -718
  3. package/dist/hermes.umd.cjs +88 -11
  4. package/dist/images/android-chrome-192x192.png +0 -0
  5. package/dist/images/android-chrome-512x512.png +0 -0
  6. package/dist/images/apple-touch-icon.png +0 -0
  7. package/dist/images/favicon-16x16.png +0 -0
  8. package/dist/images/favicon-32x32.png +0 -0
  9. package/dist/images/favicon.ico +0 -0
  10. package/dist/images/milkyWay/dark-s_nx.jpg +0 -0
  11. package/dist/images/milkyWay/dark-s_ny.jpg +0 -0
  12. package/dist/images/milkyWay/dark-s_nz.jpg +0 -0
  13. package/dist/images/milkyWay/dark-s_px.jpg +0 -0
  14. package/dist/images/milkyWay/dark-s_py.jpg +0 -0
  15. package/dist/images/milkyWay/dark-s_pz.jpg +0 -0
  16. package/dist/images/site.webmanifest +1 -0
  17. package/dist/images/uv_grid_opengl.jpg +0 -0
  18. package/dist/index-0a798fe4.js +6862 -0
  19. package/dist/index-7bad599d.css +1 -0
  20. package/dist/index.html +18 -0
  21. package/dist/models/Flair.fbx +0 -0
  22. package/dist/models/Thriller2.fbx +0 -0
  23. package/dist/models/Thriller4.fbx +0 -0
  24. package/dist/style.css +1 -1
  25. package/package.json +9 -4
  26. package/src/core/Application.ts +28 -36
  27. package/src/core/RemoteController.ts +148 -98
  28. package/src/core/remote/BaseRemote.ts +3 -3
  29. package/src/core/remote/RemoteComponents.ts +5 -8
  30. package/src/core/remote/RemoteTheatre.ts +56 -54
  31. package/src/core/remote/RemoteThree.ts +77 -0
  32. package/src/core/remote/RemoteTweakpane.ts +71 -78
  33. package/src/core/types.ts +14 -4
  34. package/src/editor/Editor.tsx +8 -6
  35. package/src/editor/components/Draggable.tsx +20 -20
  36. package/src/editor/components/DraggableItem.tsx +6 -6
  37. package/src/editor/components/Dropdown.tsx +22 -14
  38. package/src/editor/components/DropdownItem.tsx +19 -19
  39. package/src/editor/components/NavButton.tsx +1 -1
  40. package/src/editor/components/content.ts +2 -0
  41. package/src/editor/components/icons/CloseIcon.tsx +1 -1
  42. package/src/editor/components/icons/DragIcon.tsx +1 -1
  43. package/src/editor/global.ts +9 -4
  44. package/src/editor/sceneHierarchy/Accordion.tsx +40 -0
  45. package/src/editor/sceneHierarchy/ChildObject.tsx +17 -17
  46. package/src/editor/sceneHierarchy/ContainerObject.tsx +7 -8
  47. package/src/editor/sceneHierarchy/SceneHierarchy.tsx +52 -49
  48. package/src/editor/sceneHierarchy/ToggleBtn.tsx +26 -0
  49. package/src/editor/sceneHierarchy/inspector/Inspector.tsx +82 -0
  50. package/src/editor/sceneHierarchy/inspector/InspectorField.tsx +178 -0
  51. package/src/editor/sceneHierarchy/inspector/InspectorGroup.tsx +55 -0
  52. package/src/editor/sceneHierarchy/inspector/MultiView/CameraWindow.tsx +61 -0
  53. package/src/editor/sceneHierarchy/inspector/MultiView/InfiniteGridHelper.ts +24 -0
  54. package/src/editor/sceneHierarchy/inspector/MultiView/InfiniteGridMaterial.ts +127 -0
  55. package/src/editor/sceneHierarchy/inspector/MultiView/MultiView.scss +93 -0
  56. package/src/editor/sceneHierarchy/inspector/MultiView/MultiView.tsx +450 -0
  57. package/src/editor/sceneHierarchy/inspector/SceneInspector.tsx +72 -0
  58. package/src/editor/sceneHierarchy/inspector/inspector.scss +150 -0
  59. package/src/editor/sceneHierarchy/inspector/utils/InspectCamera.tsx +75 -0
  60. package/src/editor/sceneHierarchy/inspector/utils/InspectLight.tsx +62 -0
  61. package/src/editor/sceneHierarchy/inspector/utils/InspectMaterial.tsx +340 -0
  62. package/src/editor/sceneHierarchy/inspector/utils/InspectTransform.tsx +124 -0
  63. package/src/editor/sceneHierarchy/types.ts +116 -5
  64. package/src/editor/sceneHierarchy/utils.ts +207 -11
  65. package/src/editor/scss/_debug.scss +9 -19
  66. package/src/editor/scss/_dropdown.scss +1 -0
  67. package/src/editor/scss/_sceneHierarchy.scss +148 -48
  68. package/src/editor/scss/index.scss +13 -6
  69. package/src/editor/utils.ts +42 -5
  70. package/src/example/CustomEditor.tsx +40 -0
  71. package/src/example/components/App.tsx +164 -0
  72. package/src/example/constants.ts +40 -9
  73. package/src/example/main.tsx +5 -45
  74. package/src/example/three/CustomMaterial.ts +58 -0
  75. package/src/example/three/ExampleScene.ts +176 -0
  76. package/src/example/three/FBXAnimation.ts +39 -0
  77. package/src/index.ts +22 -0
  78. package/types/core/Application.d.ts +7 -13
  79. package/types/core/remote/RemoteComponents.d.ts +0 -2
  80. package/types/core/remote/RemoteThree.d.ts +12 -0
  81. package/types/core/remote/RemoteTweakpane.d.ts +3 -3
  82. package/types/core/types.d.ts +4 -3
  83. package/types/editor/Editor.d.ts +2 -1
  84. package/types/editor/components/content.d.ts +2 -0
  85. package/types/editor/global.d.ts +7 -2
  86. package/types/editor/sceneHierarchy/Accordion.d.ts +10 -0
  87. package/types/editor/sceneHierarchy/SceneHierarchy.d.ts +5 -5
  88. package/types/editor/sceneHierarchy/ToggleBtn.d.ts +7 -0
  89. package/types/editor/sceneHierarchy/inspector/Inspector.d.ts +3 -0
  90. package/types/editor/sceneHierarchy/inspector/InspectorField.d.ts +13 -0
  91. package/types/editor/sceneHierarchy/inspector/InspectorGroup.d.ts +7 -0
  92. package/types/editor/sceneHierarchy/inspector/MultiView/CameraWindow.d.ts +16 -0
  93. package/types/editor/sceneHierarchy/inspector/MultiView/InfiniteGridHelper.d.ts +7 -0
  94. package/types/editor/sceneHierarchy/inspector/MultiView/InfiniteGridMaterial.d.ts +13 -0
  95. package/types/editor/sceneHierarchy/inspector/MultiView/MultiView.d.ts +11 -0
  96. package/types/editor/sceneHierarchy/inspector/SceneInspector.d.ts +7 -0
  97. package/types/editor/sceneHierarchy/inspector/utils/InspectCamera.d.ts +3 -0
  98. package/types/editor/sceneHierarchy/inspector/utils/InspectLight.d.ts +3 -0
  99. package/types/editor/sceneHierarchy/inspector/utils/InspectMaterial.d.ts +8 -0
  100. package/types/editor/sceneHierarchy/inspector/utils/InspectTransform.d.ts +3 -0
  101. package/types/editor/sceneHierarchy/types.d.ts +98 -7
  102. package/types/editor/sceneHierarchy/utils.d.ts +7 -1
  103. package/types/editor/utils.d.ts +3 -0
  104. package/types/example/CustomEditor.d.ts +1 -0
  105. package/types/example/constants.d.ts +15 -3
  106. package/types/example/three/CustomMaterial.d.ts +5 -0
  107. package/types/example/three/ExampleScene.d.ts +18 -0
  108. package/types/example/three/FBXAnimation.d.ts +6 -0
  109. package/types/{library.d.ts → index.d.ts} +5 -0
  110. package/src/example/App.tsx +0 -88
  111. package/src/library.ts +0 -16
  112. package/types/core/remote/RemoteDebug.d.ts +0 -23
  113. package/types/debug/Editor.d.ts +0 -8
  114. package/types/debug/components/Draggable.d.ts +0 -2
  115. package/types/debug/components/DraggableItem.d.ts +0 -2
  116. package/types/debug/components/Dropdown.d.ts +0 -2
  117. package/types/debug/components/DropdownItem.d.ts +0 -2
  118. package/types/debug/components/NavButton.d.ts +0 -5
  119. package/types/debug/components/icons/CloseIcon.d.ts +0 -2
  120. package/types/debug/components/icons/DragIcon.d.ts +0 -2
  121. package/types/debug/components/types.d.ts +0 -31
  122. package/types/debug/global.d.ts +0 -9
  123. package/types/debug/sceneHierarchy/ChildObject.d.ts +0 -2
  124. package/types/debug/sceneHierarchy/ContainerObject.d.ts +0 -2
  125. package/types/debug/sceneHierarchy/SceneHierarchy.d.ts +0 -13
  126. package/types/debug/sceneHierarchy/types.d.ts +0 -8
  127. package/types/debug/sceneHierarchy/utils.d.ts +0 -2
  128. package/types/debug/utils.d.ts +0 -4
  129. /package/src/example/{App.css → components/App.css} +0 -0
  130. /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 { debugDispatcher, ToolEvents } from '../global'
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(false)
10
+ const [open, setOpen] = useState(props.child.children.length > 0);
12
11
 
13
- let container = null
14
- let hasChildren = false
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
- debugDispatcher.dispatchEvent({ type: ToolEvents.INSPECT_ITEM, value: props.child })
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
- {container}
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 ContainerObject from './ContainerObject'
8
- import { SceneHierarchyState } from './types'
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
- constructor(props: object | SceneHierarchyState) {
12
- super(props)
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
- open: false,
15
- scene: null,
16
- } as SceneHierarchyState
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
- debugDispatcher.addEventListener(ToolEvents.REFRESH_SCENE, this.onUpdate)
19
- debugDispatcher.addEventListener(ToolEvents.SET_SCENE, this.onSetScene)
25
+ componentDidMount(): void {
26
+ this.onRefresh();
20
27
  }
21
28
 
22
29
  componentWillUnmount(): void {
23
- debugDispatcher.removeEventListener(ToolEvents.REFRESH_SCENE, this.onUpdate)
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 headerTitle = this.componentState.scene !== null ? `Hierarchy: ${this.componentState.scene.name}` : 'Hierarchy'
34
+ const hasScene = this.componentState.scene !== null;
35
+ const HierarchyName = 'Hierarchy' + (hasScene ? `: ${this.componentState.scene?.name}` : '');
29
36
  return (
30
- <div id="SceneHierarchy">
31
- <div className="header">
32
- <button
33
- className="status"
34
- style={{
35
- backgroundPositionX: this.componentState.open ? '-14px' : '2px',
36
- }}
37
- onClick={this.toggleOpen}
38
- ></button>
39
- <span>{headerTitle}</span>
40
- <button className="refresh hideText" onClick={this.onRefresh}>
41
- Refresh
42
- </button>
43
- </div>
44
- {this.componentState.scene !== null && this.componentState.open ? <ContainerObject child={this.componentState.scene} /> : null}
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
- debugDispatcher.dispatchEvent({ type: ToolEvents.INSPECT_ITEM, value: this.componentState.scene })
63
- }
66
+ this.three.getScene();
67
+ };
64
68
 
65
- private onSetScene = (evt: any) => {
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
+ }