@tomorrowevening/hermes 0.0.0

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 (94) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +10 -0
  3. package/dist/hermes.js +1202 -0
  4. package/dist/hermes.umd.cjs +29 -0
  5. package/dist/images/debug/icon_camera.png +0 -0
  6. package/dist/images/debug/icon_folder.png +0 -0
  7. package/dist/images/debug/icon_interactive.png +0 -0
  8. package/dist/images/debug/icon_lights.png +0 -0
  9. package/dist/images/debug/icon_refresh.png +0 -0
  10. package/dist/images/debug/icon_ui.png +0 -0
  11. package/dist/images/debug/icon_utils.png +0 -0
  12. package/dist/images/debug/icon_world.png +0 -0
  13. package/dist/style.css +1 -0
  14. package/package.json +63 -0
  15. package/src/core/Application.ts +69 -0
  16. package/src/core/RemoteController.ts +135 -0
  17. package/src/core/remote/BaseRemote.ts +16 -0
  18. package/src/core/remote/RemoteComponents.ts +32 -0
  19. package/src/core/remote/RemoteTheatre.ts +117 -0
  20. package/src/core/remote/RemoteTweakpane.ts +176 -0
  21. package/src/core/types.ts +43 -0
  22. package/src/editor/Editor.tsx +15 -0
  23. package/src/editor/components/Draggable.tsx +40 -0
  24. package/src/editor/components/DraggableItem.tsx +22 -0
  25. package/src/editor/components/Dropdown.tsx +30 -0
  26. package/src/editor/components/DropdownItem.tsx +64 -0
  27. package/src/editor/components/NavButton.tsx +11 -0
  28. package/src/editor/components/icons/CloseIcon.tsx +7 -0
  29. package/src/editor/components/icons/DragIcon.tsx +9 -0
  30. package/src/editor/components/types.ts +41 -0
  31. package/src/editor/global.ts +13 -0
  32. package/src/editor/sceneHierarchy/ChildObject.tsx +56 -0
  33. package/src/editor/sceneHierarchy/ContainerObject.tsx +12 -0
  34. package/src/editor/sceneHierarchy/SceneHierarchy.tsx +77 -0
  35. package/src/editor/sceneHierarchy/types.ts +10 -0
  36. package/src/editor/sceneHierarchy/utils.ts +25 -0
  37. package/src/editor/scss/_debug.scss +68 -0
  38. package/src/editor/scss/_draggable.scss +43 -0
  39. package/src/editor/scss/_dropdown.scss +83 -0
  40. package/src/editor/scss/_sceneHierarchy.scss +170 -0
  41. package/src/editor/scss/_theme.scss +9 -0
  42. package/src/editor/scss/index.scss +58 -0
  43. package/src/editor/utils.ts +20 -0
  44. package/src/example/App.css +6 -0
  45. package/src/example/App.tsx +88 -0
  46. package/src/example/constants.ts +13 -0
  47. package/src/example/index.scss +37 -0
  48. package/src/example/main.tsx +75 -0
  49. package/src/library.ts +16 -0
  50. package/src/vite-env.d.ts +1 -0
  51. package/types/core/Application.d.ts +21 -0
  52. package/types/core/RemoteController.d.ts +2 -0
  53. package/types/core/remote/BaseRemote.d.ts +6 -0
  54. package/types/core/remote/RemoteComponents.d.ts +7 -0
  55. package/types/core/remote/RemoteDebug.d.ts +23 -0
  56. package/types/core/remote/RemoteTheatre.d.ts +16 -0
  57. package/types/core/remote/RemoteTweakpane.d.ts +23 -0
  58. package/types/core/types.d.ts +13 -0
  59. package/types/debug/Editor.d.ts +8 -0
  60. package/types/debug/components/Draggable.d.ts +2 -0
  61. package/types/debug/components/DraggableItem.d.ts +2 -0
  62. package/types/debug/components/Dropdown.d.ts +2 -0
  63. package/types/debug/components/DropdownItem.d.ts +2 -0
  64. package/types/debug/components/NavButton.d.ts +5 -0
  65. package/types/debug/components/icons/CloseIcon.d.ts +2 -0
  66. package/types/debug/components/icons/DragIcon.d.ts +2 -0
  67. package/types/debug/components/types.d.ts +31 -0
  68. package/types/debug/global.d.ts +9 -0
  69. package/types/debug/sceneHierarchy/ChildObject.d.ts +2 -0
  70. package/types/debug/sceneHierarchy/ContainerObject.d.ts +2 -0
  71. package/types/debug/sceneHierarchy/SceneHierarchy.d.ts +13 -0
  72. package/types/debug/sceneHierarchy/types.d.ts +8 -0
  73. package/types/debug/sceneHierarchy/utils.d.ts +2 -0
  74. package/types/debug/utils.d.ts +4 -0
  75. package/types/editor/Editor.d.ts +8 -0
  76. package/types/editor/components/Draggable.d.ts +2 -0
  77. package/types/editor/components/DraggableItem.d.ts +2 -0
  78. package/types/editor/components/Dropdown.d.ts +2 -0
  79. package/types/editor/components/DropdownItem.d.ts +2 -0
  80. package/types/editor/components/NavButton.d.ts +5 -0
  81. package/types/editor/components/icons/CloseIcon.d.ts +2 -0
  82. package/types/editor/components/icons/DragIcon.d.ts +2 -0
  83. package/types/editor/components/types.d.ts +31 -0
  84. package/types/editor/global.d.ts +9 -0
  85. package/types/editor/sceneHierarchy/ChildObject.d.ts +2 -0
  86. package/types/editor/sceneHierarchy/ContainerObject.d.ts +2 -0
  87. package/types/editor/sceneHierarchy/SceneHierarchy.d.ts +13 -0
  88. package/types/editor/sceneHierarchy/types.d.ts +8 -0
  89. package/types/editor/sceneHierarchy/utils.d.ts +2 -0
  90. package/types/editor/utils.d.ts +4 -0
  91. package/types/example/App.d.ts +3 -0
  92. package/types/example/constants.d.ts +3 -0
  93. package/types/example/main.d.ts +1 -0
  94. package/types/library.d.ts +14 -0
@@ -0,0 +1,176 @@
1
+ // Libs
2
+ import { Pane } from 'tweakpane'
3
+ import * as EssentialsPlugin from '@tweakpane/plugin-essentials'
4
+ // Core
5
+ import Application from '../Application'
6
+ import BaseRemote from './BaseRemote'
7
+ import { noop } from '../types'
8
+ import type { DataUpdateCallback, VoidCallback } from '../types'
9
+
10
+ export default class RemoteTweakpane extends BaseRemote {
11
+ appTab: any = undefined
12
+ systemTab: any = undefined
13
+ utilsTab: any = undefined
14
+ bindCBs: Map<string, DataUpdateCallback>
15
+ buttonCBs: Map<string, VoidCallback>
16
+
17
+ protected pane?: Pane | undefined = undefined
18
+ protected appCallbacks = 0
19
+ protected editorCallbacks = 0
20
+
21
+ constructor(app: Application) {
22
+ super(app)
23
+ this.bindCBs = new Map()
24
+ this.buttonCBs = new Map()
25
+
26
+ if (app.editor) this.createGUI()
27
+ }
28
+
29
+ protected createGUI() {
30
+ this.pane = new Pane({ title: 'GUI' })
31
+ this.pane.registerPlugin(EssentialsPlugin)
32
+ const guiElement = this.pane.element.parentElement as HTMLElement
33
+ guiElement.style.left = '50%'
34
+ guiElement.style.top = '0'
35
+ guiElement.style.maxHeight = '100%'
36
+ guiElement.style.overflowX = 'hidden'
37
+ guiElement.style.overflowY = 'auto'
38
+ guiElement.style.transform = 'translateX(-50%)'
39
+ guiElement.style.width = '300px'
40
+ guiElement.style.zIndex = '100'
41
+
42
+ // @ts-ignore
43
+ const tabs = this.pane.addTab({
44
+ pages: [{ title: 'App' }, { title: 'System' }, { title: 'Tools' }],
45
+ })
46
+ this.appTab = tabs.pages[0]
47
+ this.systemTab = tabs.pages[1]
48
+ this.utilsTab = tabs.pages[2]
49
+ }
50
+
51
+ override dispose(): void {
52
+ this.bindCBs.clear()
53
+ this.buttonCBs.clear()
54
+ this.appCallbacks = 0
55
+ this.editorCallbacks = 0
56
+
57
+ if (this.app.editor) {
58
+ this.appTab?.dispose()
59
+ this.systemTab?.dispose()
60
+ this.utilsTab?.dispose()
61
+ this.pane?.dispose()
62
+ this.appTab = undefined
63
+ this.systemTab = undefined
64
+ this.utilsTab = undefined
65
+ this.pane = undefined
66
+ }
67
+ }
68
+
69
+ addFolder(name: string, params: any = undefined, parent: any = undefined) {
70
+ if (this.app.editor) {
71
+ if (this.pane === undefined) this.createGUI()
72
+
73
+ const container = parent !== undefined ? parent : this.appTab
74
+ return container.addFolder({
75
+ title: name,
76
+ ...params,
77
+ })
78
+ } else {
79
+ this.app.send({
80
+ event: 'addFolder',
81
+ data: {
82
+ name,
83
+ params,
84
+ parent
85
+ }
86
+ })
87
+ }
88
+ }
89
+
90
+ get bindID(): string {
91
+ return `debug_${Math.max(this.appCallbacks, this.editorCallbacks)}`
92
+ }
93
+
94
+ // Binding
95
+
96
+ bind(obj: any, name: string, params: any, parent: any = undefined) {
97
+ const bindID = this.bindID
98
+ const callback = params.onChange !== undefined ? params.onChange : noop
99
+ this.bindCBs.set(bindID, callback)
100
+
101
+ if (this.app.editor) {
102
+ if (this.pane === undefined) this.createGUI()
103
+
104
+ const container = parent !== undefined ? parent : this.appTab
105
+ container
106
+ .addBinding(obj, name, params)
107
+ .on('change', (evt: any) => {
108
+ this.app.send({
109
+ event: 'updateBind',
110
+ data: {
111
+ id: bindID,
112
+ value: evt.value,
113
+ }
114
+ })
115
+ })
116
+ this.editorCallbacks++
117
+ } else {
118
+ this.app.send({
119
+ event: 'bindObject',
120
+ data: {
121
+ id: bindID,
122
+ name,
123
+ params,
124
+ parent
125
+ }
126
+ })
127
+ this.appCallbacks++
128
+ }
129
+ }
130
+
131
+ triggerBind(id: string, data: any) {
132
+ const cb = this.bindCBs.get(id)
133
+ if (cb !== undefined) cb(data)
134
+ else console.warn(`No callback for: ${id}`, data)
135
+ }
136
+
137
+ // Buttons
138
+
139
+ button(name: string, callback: VoidCallback, parent: any = undefined) {
140
+ const bindID = this.bindID
141
+ this.buttonCBs.set(bindID, callback)
142
+
143
+ if (this.app.editor) {
144
+ if (this.pane === undefined) this.createGUI()
145
+
146
+ const container = parent !== undefined ? parent : this.appTab
147
+ container
148
+ .addButton({ title: name })
149
+ .on('click', () => {
150
+ this.app.send({
151
+ event: 'clickButton',
152
+ data: {
153
+ id: bindID,
154
+ }
155
+ })
156
+ })
157
+ this.editorCallbacks++
158
+ } else {
159
+ this.app.send({
160
+ event: 'addButton',
161
+ data: {
162
+ id: bindID,
163
+ name,
164
+ callback,
165
+ parent
166
+ }
167
+ })
168
+ this.appCallbacks++
169
+ }
170
+ }
171
+
172
+ triggerButton(id: string) {
173
+ const cb = this.buttonCBs.get(id)
174
+ if (cb !== undefined) cb()
175
+ }
176
+ }
@@ -0,0 +1,43 @@
1
+ // Interfaces
2
+
3
+ export interface BroadcastData {
4
+ event: EditorEvent
5
+ data: any
6
+ }
7
+
8
+ // Types
9
+
10
+ export type ApplicationMode = 'listener' | 'editor'
11
+
12
+ export type VoidCallback = () => void
13
+
14
+ export type DataUpdateCallback = (data: any) => void
15
+
16
+ export type EditorEvent =
17
+ // Theatre
18
+ | 'setSheet'
19
+ | 'setSheetObject'
20
+ | 'updateSheetObject'
21
+ | 'updateTimeline'
22
+ // GUI
23
+ | 'addFolder'
24
+ | 'bindObject'
25
+ | 'updateBind'
26
+ | 'addButton'
27
+ | 'clickButton'
28
+ // Components
29
+ | 'selectComponent'
30
+ | 'draggableListUpdate'
31
+
32
+ export type VoidFunc = () => void
33
+
34
+ export type BroadcastCallback = (data: BroadcastData) => void
35
+
36
+ export type TheatreUpdateCallback = (data: any) => void
37
+
38
+ // Consts
39
+
40
+ export const noop = () => {}
41
+
42
+ export const defaultTheatreCallback: TheatreUpdateCallback = () => {}
43
+
@@ -0,0 +1,15 @@
1
+ import './scss/index.scss'
2
+
3
+ type EditorProps = {
4
+ components?: JSX.Element | JSX.Element[]
5
+ children?: JSX.Element | JSX.Element[]
6
+ }
7
+
8
+ export default function Editor(props: EditorProps) {
9
+ return (
10
+ <div className="editor">
11
+ <div className="navBar">{props.children}</div>
12
+ {props.components}
13
+ </div>
14
+ )
15
+ }
@@ -0,0 +1,40 @@
1
+ // Libs
2
+ import { useState } from 'react'
3
+ import { Reorder } from 'framer-motion'
4
+ // Components
5
+ import NavButton from './NavButton'
6
+ import DraggableItem from './DraggableItem'
7
+ import { DraggableProps } from './types'
8
+
9
+ export default function Draggable(props: DraggableProps) {
10
+ const [expanded, setExpanded] = useState(false)
11
+ const [list, setList] = useState<string[]>(props.options)
12
+
13
+ const updateList = (updated: string[]) => {
14
+ props.onDragComplete(updated)
15
+ setList(updated)
16
+ }
17
+
18
+ const onDelete = (index: number) => {
19
+ const newArray = [...list]
20
+ newArray.splice(index, 1)
21
+ updateList(newArray)
22
+ }
23
+
24
+ const elements: any[] = []
25
+ list.forEach((value: string, index: number) => {
26
+ elements.push(<DraggableItem key={value} index={index} title={value} onDelete={onDelete} />)
27
+ })
28
+
29
+ let ddClassName = 'dropdown draggable'
30
+ if (props.subdropdown) ddClassName += ' subdropdown'
31
+
32
+ return (
33
+ <div className={ddClassName} onMouseEnter={() => setExpanded(true)} onMouseLeave={() => setExpanded(false)}>
34
+ <NavButton title={props.title} />
35
+ <Reorder.Group axis="y" values={list} onReorder={updateList} style={{ visibility: expanded ? 'visible' : 'hidden' }}>
36
+ {elements}
37
+ </Reorder.Group>
38
+ </div>
39
+ )
40
+ }
@@ -0,0 +1,22 @@
1
+ // Libs
2
+ import { Reorder } from 'framer-motion'
3
+ // Components
4
+ import CloseIcon from './icons/CloseIcon'
5
+ import DragIcon from './icons/DragIcon'
6
+ import { DraggableItemProps } from './types'
7
+
8
+ export default function DraggableItem(props: DraggableItemProps) {
9
+ return (
10
+ <Reorder.Item key={props.title} value={props.title}>
11
+ <div>
12
+ {DragIcon}
13
+ <span>{props.title}</span>
14
+ <button className="closeIcon" onClick={() => {
15
+ props.onDelete(props.index)
16
+ }}>
17
+ {CloseIcon}
18
+ </button>
19
+ </div>
20
+ </Reorder.Item>
21
+ )
22
+ }
@@ -0,0 +1,30 @@
1
+ // Libs
2
+ import { useState } from 'react'
3
+ // Views
4
+ import NavButton from './NavButton'
5
+ import DropdownItem from './DropdownItem'
6
+ import { DropdownOption, DropdownProps } from './types'
7
+
8
+ export default function Dropdown(props: DropdownProps) {
9
+ const [expanded, setExpanded] = useState(false)
10
+
11
+ const list: Array<any> = []
12
+ {
13
+ props.options.map((option: DropdownOption, index: number) => {
14
+ if (props.onSelect !== undefined) {
15
+ option.onSelect = props.onSelect
16
+ }
17
+ list.push(<DropdownItem option={option} key={index} />)
18
+ })
19
+ }
20
+
21
+ let ddClassName = 'dropdown'
22
+ if (props.subdropdown) ddClassName += ' subdropdown'
23
+
24
+ return (
25
+ <div className={ddClassName} onMouseEnter={() => setExpanded(true)} onMouseLeave={() => setExpanded(false)}>
26
+ <NavButton title={props.title} />
27
+ <ul style={{ visibility: expanded ? 'visible' : 'hidden' }}>{list}</ul>
28
+ </div>
29
+ )
30
+ }
@@ -0,0 +1,64 @@
1
+ // Libs
2
+ import { useState } from 'react'
3
+ // Components
4
+ import Draggable from './Draggable'
5
+ import Dropdown from './Dropdown'
6
+ import type { DropdownItemProps, DropdownOption } from './types'
7
+ // Utils
8
+ import { randomID } from '../utils'
9
+
10
+ export default function DropdownItem(props: DropdownItemProps) {
11
+ const { option } = props
12
+ const [selected, setSelected] = useState('')
13
+
14
+ let element = null
15
+ switch (option.type) {
16
+ case 'draggable':
17
+ element = (
18
+ <Draggable
19
+ title={option.title}
20
+ options={option.value as Array<string>}
21
+ onDragComplete={(options: string[]) => {
22
+ if (option.onDragComplete !== undefined) option.onDragComplete(options)
23
+ }}
24
+ subdropdown={true}
25
+ />
26
+ )
27
+ break
28
+ case 'dropdown':
29
+ element = (
30
+ <Dropdown
31
+ title={option.title}
32
+ options={option.value as Array<DropdownOption>}
33
+ onSelect={option.onSelect}
34
+ subdropdown={true}
35
+ />
36
+ )
37
+ break
38
+ case 'option':
39
+ element = (
40
+ <button
41
+ onClick={() => {
42
+ if (option.onSelect !== undefined) option.onSelect(option.value)
43
+ // Toggle selectable
44
+ if (option.selectable) {
45
+ if (selected !== option.title) {
46
+ setSelected(option.title)
47
+ } else {
48
+ setSelected('')
49
+ }
50
+ }
51
+ }}
52
+ >
53
+ {option.title}
54
+ </button>
55
+ )
56
+ break
57
+ }
58
+
59
+ return (
60
+ <li className={selected === option.title ? 'selected' : ''} key={randomID()}>
61
+ {element}
62
+ </li>
63
+ )
64
+ }
@@ -0,0 +1,11 @@
1
+ type NavButtonProps = {
2
+ title: string
3
+ }
4
+
5
+ export default function NavButton(props: NavButtonProps) {
6
+ return props.title.search('<') > -1 ? (
7
+ <button className="svg" dangerouslySetInnerHTML={{ __html: props.title }}></button>
8
+ ) : (
9
+ <button>{props.title}</button>
10
+ )
11
+ }
@@ -0,0 +1,7 @@
1
+ export default (
2
+ <svg className="closeIcon" width="14" height="14" fill="none" stroke="#666666" strokeMiterlimit="10">
3
+ <circle cx="7" cy="7" r="6" />
4
+ <line x1="4" y1="4" x2="10" y2="10" />
5
+ <line x1="4" y1="10" x2="10" y2="4" />
6
+ </svg>
7
+ )
@@ -0,0 +1,9 @@
1
+ export default (
2
+ <svg className="dragIcon" width="14" height="14" fill="#666666" stroke="none">
3
+ <path
4
+ d="M10.43,4H3.57C3.26,4,3,4.22,3,4.5v1C3,5.78,3.26,6,3.57,6h6.86C10.74,6,11,5.78,11,5.5v-1
5
+ C11,4.22,10.74,4,10.43,4z M10.43,8H3.57C3.26,8,3,8.22,3,8.5v1C3,9.78,3.26,10,3.57,10h6.86C10.74,10,11,9.78,11,9.5v-1
6
+ C11,8.22,10.74,8,10.43,8z"
7
+ />
8
+ </svg>
9
+ )
@@ -0,0 +1,41 @@
1
+ export type DropdownType = 'option' | 'dropdown' | 'draggable'
2
+
3
+ export interface DropdownOption {
4
+ title: string
5
+ value: any | Array<DropdownOption>
6
+ type: DropdownType
7
+ // Option
8
+ onSelect?: (value: any) => void
9
+ selectable?: boolean
10
+ // Draggable
11
+ onDragComplete?: (options: Array<string>) => void
12
+ }
13
+
14
+ export interface DropdownProps {
15
+ title: string
16
+ options: Array<DropdownOption>
17
+ onSelect?: (value: any) => void
18
+ subdropdown?: boolean
19
+ }
20
+
21
+ export interface DropdownItemProps {
22
+ option: DropdownOption
23
+ onSelect?: (value: any) => void
24
+ // Draggable
25
+ onDragComplete?: (options: Array<string>) => void
26
+ }
27
+
28
+ // Draggable
29
+
30
+ export interface DraggableItemProps {
31
+ index: number
32
+ title: string
33
+ onDelete: (index: number) => void
34
+ }
35
+
36
+ export interface DraggableProps {
37
+ title: string
38
+ options: Array<string>
39
+ onDragComplete: (options: Array<string>) => void
40
+ subdropdown?: boolean
41
+ }
@@ -0,0 +1,13 @@
1
+ import { EventDispatcher } from 'three';
2
+
3
+ export const debugDispatcher = new EventDispatcher()
4
+
5
+ export const ToolEvents = {
6
+ // Components
7
+ SELECT_DROPDOWN: 'ToolEvents::selectDropdown',
8
+ DRAG_UPDATE: 'ToolEvents::dragUpdate',
9
+ // SceneHierarchy
10
+ INSPECT_ITEM: 'ToolEvents::inspectItem',
11
+ REFRESH_SCENE: 'ToolEvents::refreshScene',
12
+ SET_SCENE: 'ToolEvents::setScene',
13
+ }
@@ -0,0 +1,56 @@
1
+ // Libs
2
+ import { useState } from 'react'
3
+ import { Object3D } from 'three'
4
+ // Models
5
+ import { debugDispatcher, ToolEvents } from '../global'
6
+ import { ChildObjectProps } from './types'
7
+ // Utils
8
+ import { determineIcon } from './utils'
9
+
10
+ export default function ChildObject(props: ChildObjectProps) {
11
+ const [open, setOpen] = useState(false)
12
+
13
+ let container = null
14
+ let hasChildren = false
15
+ if (props.child.children.length > 0) {
16
+ hasChildren = true
17
+ const children: Array<any> = []
18
+ 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>
22
+ }
23
+
24
+ return (
25
+ <div className="childObject" key={Math.random()}>
26
+ <div className="child">
27
+ {hasChildren ? (
28
+ <button
29
+ className="status"
30
+ style={{
31
+ backgroundPositionX: open ? '-14px' : '2px',
32
+ }}
33
+ onClick={() => {
34
+ setOpen(!open)
35
+ }}
36
+ ></button>
37
+ ) : null}
38
+ <button
39
+ className="name"
40
+ style={{
41
+ left: hasChildren ? '20px' : '5px',
42
+ }}
43
+ onClick={() => {
44
+ debugDispatcher.dispatchEvent({ type: ToolEvents.INSPECT_ITEM, value: props.child })
45
+ }}
46
+ >
47
+ {props.child.name.length > 0
48
+ ? `${props.child.name} (${props.child.type})`
49
+ : `${props.child.type}::${props.child.uuid}`}
50
+ </button>
51
+ <div className={`icon ${determineIcon(props.child)}`}></div>
52
+ </div>
53
+ {container}
54
+ </div>
55
+ )
56
+ }
@@ -0,0 +1,12 @@
1
+ import { Object3D } from 'three'
2
+ import ChildObject from './ChildObject'
3
+ import type { ChildObjectProps } from './types'
4
+
5
+ export default function ContainerObject(props: ChildObjectProps) {
6
+ const children: Array<any> = []
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>
12
+ }
@@ -0,0 +1,77 @@
1
+ // Libs
2
+ import { Component, ReactNode } from 'react'
3
+ // Models
4
+ import { debugDispatcher, ToolEvents } from '../global'
5
+ // Components
6
+ import '../scss/_sceneHierarchy.scss'
7
+ import ContainerObject from './ContainerObject'
8
+ import { SceneHierarchyState } from './types'
9
+
10
+ export default class SceneHierarchy extends Component {
11
+ constructor(props: object | SceneHierarchyState) {
12
+ super(props)
13
+ this.state = {
14
+ open: false,
15
+ scene: null,
16
+ } as SceneHierarchyState
17
+
18
+ debugDispatcher.addEventListener(ToolEvents.REFRESH_SCENE, this.onUpdate)
19
+ debugDispatcher.addEventListener(ToolEvents.SET_SCENE, this.onSetScene)
20
+ }
21
+
22
+ componentWillUnmount(): void {
23
+ debugDispatcher.removeEventListener(ToolEvents.REFRESH_SCENE, this.onUpdate)
24
+ debugDispatcher.removeEventListener(ToolEvents.SET_SCENE, this.onSetScene)
25
+ }
26
+
27
+ render(): ReactNode {
28
+ const headerTitle = this.componentState.scene !== null ? `Hierarchy: ${this.componentState.scene.name}` : 'Hierarchy'
29
+ 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}
45
+ </div>
46
+ )
47
+ }
48
+
49
+ // Private
50
+
51
+ private onUpdate = () => {
52
+ //
53
+ }
54
+
55
+ private toggleOpen = () => {
56
+ this.setState(()=> ({
57
+ open: !this.componentState.open,
58
+ }))
59
+ }
60
+
61
+ private onRefresh = () => {
62
+ debugDispatcher.dispatchEvent({ type: ToolEvents.INSPECT_ITEM, value: this.componentState.scene })
63
+ }
64
+
65
+ private onSetScene = (evt: any) => {
66
+ console.log('SceneHierarchy::onSetScene', evt)
67
+ this.setState(() => ({
68
+ scene: evt.value
69
+ }))
70
+ }
71
+
72
+ // Getters / Setters
73
+
74
+ get componentState(): SceneHierarchyState {
75
+ return this.state as SceneHierarchyState
76
+ }
77
+ }
@@ -0,0 +1,10 @@
1
+ import { Object3D } from 'three'
2
+
3
+ export type ChildObjectProps = {
4
+ child: Object3D
5
+ }
6
+
7
+ export type SceneHierarchyState = {
8
+ open: boolean
9
+ scene: Object3D | null
10
+ }
@@ -0,0 +1,25 @@
1
+ import { Object3D } from 'three'
2
+
3
+ export function determineIcon(obj: Object3D): string {
4
+ if (obj.name === 'cameras') {
5
+ return 'camera'
6
+ } else if (obj.name === 'interactive') {
7
+ return 'interactive'
8
+ } else if (obj.name === 'lights') {
9
+ return 'light'
10
+ } else if (obj.name === 'ui') {
11
+ return 'ui'
12
+ } else if (obj.name === 'utils') {
13
+ return 'utils'
14
+ }
15
+
16
+ const type = obj.type
17
+ if (type.search('Helper') > -1) {
18
+ return 'icon_utils'
19
+ } else if (type.search('Camera') > -1) {
20
+ return 'camera'
21
+ } else if (type.search('Light') > -1) {
22
+ return 'light'
23
+ }
24
+ return 'obj3D'
25
+ }