@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
@@ -0,0 +1,127 @@
1
+ import { Color, DoubleSide, GLSL3, ShaderMaterial } from 'three';
2
+
3
+ type InfiniteGridProps = {
4
+ divisions?: number
5
+ scale?: number
6
+ color?: Color
7
+ distance?: number
8
+ subgridOpacity?: number
9
+ gridOpacity?: number
10
+ }
11
+
12
+ const vertex = `out vec3 worldPosition;
13
+ uniform float uDistance;
14
+
15
+ void main() {
16
+ // Scale the plane by the drawing distance
17
+ worldPosition = position.xzy * uDistance;
18
+ worldPosition.xz += cameraPosition.xz;
19
+
20
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(worldPosition, 1.0);
21
+ }`;
22
+
23
+ const fragment = `out vec4 fragColor;
24
+ in vec3 worldPosition;
25
+
26
+ uniform float uDivisions;
27
+ uniform float uScale;
28
+ uniform vec3 uColor;
29
+ uniform float uDistance;
30
+ uniform float uSubgridOpacity;
31
+ uniform float uGridOpacity;
32
+
33
+ float getGrid(float gapSize) {
34
+ vec2 worldPositionByDivision = worldPosition.xz / gapSize;
35
+
36
+ // Inverted, 0 where line, >1 where there's no line
37
+ // We use the worldPosition (which in this case we use similarly to UVs) differential to control the anti-aliasing
38
+ // We need to do the -0.5)-0.5 trick because the result fades out from 0 to 1, and we want both
39
+ // worldPositionByDivision == 0.3 and worldPositionByDivision == 0.7 to result in the same fade, i.e. 0.3,
40
+ // otherwise only one side of the line will be anti-aliased
41
+ vec2 grid = abs(fract(worldPositionByDivision-0.5)-0.5) / fwidth(worldPositionByDivision) / 2.0;
42
+ float gridLine = min(grid.x, grid.y);
43
+
44
+ // Uninvert and clamp
45
+ return 1.0 - min(gridLine, 1.0);
46
+ }
47
+
48
+ void main() {
49
+ float cameraDistanceToGridPlane = distance(cameraPosition.y, worldPosition.y);
50
+ float cameraDistanceToFragmentOnGridPlane = distance(cameraPosition.xz, worldPosition.xz);
51
+
52
+ // The size of the grid and subgrid are powers of each other and they are determined based on camera distance.
53
+ // The current grid will become the next subgrid when it becomes too small, and its next power becomes the new grid.
54
+ float subGridPower = pow(uDivisions, floor(log(cameraDistanceToGridPlane) / log(uDivisions)));
55
+ float gridPower = subGridPower * uDivisions;
56
+
57
+ // If we want to fade both the grid and its subgrid, we need to displays 3 different opacities, with the next grid being the third
58
+ float nextGridPower = gridPower * uDivisions;
59
+
60
+ // 1 where grid, 0 where no grid
61
+ float subgrid = getGrid(subGridPower * uScale);
62
+ float grid = getGrid(gridPower * uScale);
63
+ float nextGrid = getGrid(nextGridPower * uScale);
64
+
65
+ // Where we are between the introduction of the current grid power and when we switch to the next grid power
66
+ float stepPercentage = (cameraDistanceToGridPlane - subGridPower)/(gridPower - subGridPower);
67
+
68
+ // The last x percentage of the current step over which we want to fade
69
+ float fadeRange = 0.3;
70
+
71
+ // We calculate the fade percentage from the step percentage and the fade range
72
+ float fadePercentage = max(stepPercentage - 1.0 + fadeRange, 0.0) / fadeRange;
73
+
74
+ // Set base opacity based on how close we are to the drawing distance, with a cubic falloff
75
+ float baseOpacity = subgrid * pow(1.0 - min(cameraDistanceToFragmentOnGridPlane / uDistance, 1.0), 3.0);
76
+
77
+ // Shade the subgrid
78
+ fragColor = vec4(uColor.rgb, (baseOpacity - fadePercentage) * uSubgridOpacity);
79
+
80
+ // Somewhat arbitrary additional fade coefficient to counter anti-aliasing popping when switching between grid powers
81
+ float fadeCoefficient = 0.5;
82
+
83
+ // Shade the grid
84
+ fragColor.a = mix(fragColor.a, baseOpacity * uGridOpacity - fadePercentage * (uGridOpacity - uSubgridOpacity) * fadeCoefficient, grid);
85
+
86
+ // Shade the next grid
87
+ fragColor.a = mix(fragColor.a, baseOpacity * uGridOpacity, nextGrid);
88
+
89
+ if (fragColor.a <= 0.0) discard;
90
+ }`;
91
+
92
+ export default class InfiniteGridMaterial extends ShaderMaterial {
93
+ constructor(props?: InfiniteGridProps) {
94
+ super({
95
+ extensions: {
96
+ derivatives: true,
97
+ },
98
+ glslVersion: GLSL3,
99
+ side: DoubleSide,
100
+ transparent: true,
101
+ uniforms: {
102
+ uScale: {
103
+ value: props?.scale !== undefined ? props?.scale : 0.1,
104
+ },
105
+ uDivisions: {
106
+ value: props?.divisions !== undefined ? props?.divisions : 10,
107
+ },
108
+ uColor: {
109
+ value: props?.color !== undefined ? props?.color : new Color(0xffffff),
110
+ },
111
+ uDistance: {
112
+ value: props?.distance !== undefined ? props?.distance : 10000,
113
+ },
114
+ uSubgridOpacity: {
115
+ value: props?.subgridOpacity !== undefined ? props?.subgridOpacity : 0.15,
116
+ },
117
+ uGridOpacity: {
118
+ value: props?.gridOpacity !== undefined ? props?.gridOpacity : 0.25,
119
+ },
120
+ },
121
+ vertexShader: vertex,
122
+ fragmentShader: fragment,
123
+ name: 'InfiniteGrid',
124
+ depthWrite: false,
125
+ });
126
+ }
127
+ }
@@ -0,0 +1,93 @@
1
+ $padding: 2px;
2
+
3
+ .multiview {
4
+ display: grid;
5
+ font-family: Roboto Mono, Source Code Pro, Menlo, Courier, monospace;
6
+ font-size: 10px;
7
+ grid-template-columns: repeat(2, 1fr);
8
+ position: absolute;
9
+ overflow: hidden;
10
+ left: 0;
11
+ top: 0;
12
+ right: 300px;
13
+ bottom: 0;
14
+
15
+ .dropdown {
16
+ background-color: #222;
17
+ display: inline-block;
18
+ font-size: 10px;
19
+ padding: $padding;
20
+ text-align: center;
21
+ width: 100px;
22
+
23
+ .dropdown-toggle {
24
+ cursor: pointer;
25
+ }
26
+
27
+ .dropdown-menu {
28
+ position: absolute;
29
+ top: 100%;
30
+ left: 0;
31
+ z-index: 1;
32
+ list-style: none;
33
+ padding: 0;
34
+ margin: 0;
35
+ width: 100%;
36
+
37
+ li {
38
+ background-color: #222;
39
+ cursor: pointer;
40
+ padding: $padding;
41
+ transition: 0.2s linear background-color;
42
+ &:hover {
43
+ background-color: #333;
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ .cameras {
50
+ display: grid;
51
+ grid-template-columns: repeat(2, 1fr);
52
+ position: absolute;
53
+ width: 100%;
54
+ height: 100%;
55
+
56
+ &.single {
57
+ grid-template-columns: repeat(1, 1fr);
58
+ }
59
+
60
+ .dropdown {
61
+ position: absolute;
62
+ top: 0;
63
+ left: 50%;
64
+ transform: translateX(-50%);
65
+
66
+ &.up {
67
+ bottom: 0;
68
+ top: initial;
69
+ .dropdown-menu {
70
+ top: initial;
71
+ bottom: 100%;
72
+ }
73
+ }
74
+ }
75
+
76
+ .CameraWindow {
77
+ border: 1px dotted #333;
78
+ position: relative;
79
+
80
+ .clickable {
81
+ display: inline-block;
82
+ width: 100%;
83
+ height: 100%;
84
+ }
85
+ }
86
+ }
87
+
88
+ .settings {
89
+ position: absolute;
90
+ left: 50%;
91
+ transform: translateX(-50%);
92
+ }
93
+ }
@@ -0,0 +1,450 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { AxesHelper, Camera, CameraHelper, MeshBasicMaterial, MeshNormalMaterial, OrthographicCamera, PerspectiveCamera, Scene, Vector2, Vector3, WebGLRenderer } from 'three';
3
+ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
4
+ import CameraWindow, { Dropdown } from './CameraWindow';
5
+ import InfiniteGridHelper from './InfiniteGridHelper';
6
+ import './MultiView.scss';
7
+
8
+ type MultiViewMode = 'Single' | 'Side by Side' | 'Stacked' |'Quad';
9
+ const ModeOptions: MultiViewMode[] = [
10
+ 'Single',
11
+ 'Side by Side',
12
+ 'Stacked',
13
+ 'Quad'
14
+ ];
15
+ interface MultiViewProps {
16
+ scene: Scene;
17
+ renderer: WebGLRenderer;
18
+ cameras: Camera[];
19
+ mode?: MultiViewMode;
20
+ }
21
+
22
+ const cameras: Map<string, Camera> = new Map();
23
+ const controls: Map<string, OrbitControls> = new Map();
24
+ const helpers: Map<string, CameraHelper> = new Map();
25
+
26
+ function createOrtho(name: string, position: Vector3) {
27
+ const camera = new OrthographicCamera(-100, 100, 100, -100, 50, 3000);
28
+ camera.name = name;
29
+ camera.position.copy(position);
30
+ camera.lookAt(0, 0, 0);
31
+ cameras.set(name, camera);
32
+ return camera;
33
+ }
34
+
35
+ // Cameras
36
+
37
+ createOrtho('Top', new Vector3(0, 1000, 0));
38
+ createOrtho('Bottom', new Vector3(0, -1000, 0));
39
+ createOrtho('Left', new Vector3(-1000, 0, 0));
40
+ createOrtho('Right', new Vector3(1000, 0, 0));
41
+ createOrtho('Front', new Vector3(0, 0, 1000));
42
+ createOrtho('Back', new Vector3(0, 0, -1000));
43
+ createOrtho('Orthographic', new Vector3(1000, 1000, 1000));
44
+
45
+ const debugCamera = new PerspectiveCamera(60, 1, 50, 3000);
46
+ debugCamera.name = 'Debug';
47
+ debugCamera.position.set(500, 500, 500);
48
+ debugCamera.lookAt(0, 0, 0);
49
+ cameras.set('Debug', debugCamera);
50
+
51
+ type RenderMode = 'Default' | 'Normals' | 'Wireframe';
52
+ let currentRenderMode: RenderMode = 'Default';
53
+ const renderOptions: RenderMode[] = [
54
+ 'Default',
55
+ 'Normals',
56
+ 'Wireframe',
57
+ ];
58
+ const normalsMaterial = new MeshNormalMaterial();
59
+ const wireframeMaterial = new MeshBasicMaterial({
60
+ opacity: 0.33,
61
+ transparent: true,
62
+ wireframe: true
63
+ });
64
+
65
+ const scene = new Scene();
66
+
67
+ export default function MultiView(props: MultiViewProps) {
68
+ const [mode, setMode] = useState<MultiViewMode>(props.mode !== undefined ? props.mode : 'Quad');
69
+
70
+ const tlWindow = useRef<HTMLDivElement>(null);
71
+ const trWindow = useRef<HTMLDivElement>(null);
72
+ const blWindow = useRef<HTMLDivElement>(null);
73
+ const brWindow = useRef<HTMLDivElement>(null);
74
+
75
+ let tlCam = cameras.get('Debug')!;
76
+ let trCam = cameras.get('Orthographic')!;
77
+ let blCam = cameras.get('Front')!;
78
+ let brCam = cameras.get('Top')!;
79
+
80
+ const createControls = (camera: Camera, element: HTMLDivElement) => {
81
+ // Previous items
82
+ const prevControls = controls.get(camera.name);
83
+ if (prevControls !== undefined) prevControls.dispose();
84
+ controls.delete(camera.name);
85
+
86
+ const prevHelper = helpers.get(camera.name);
87
+ if (prevHelper !== undefined) {
88
+ scene.remove(prevHelper);
89
+ prevHelper.dispose();
90
+ }
91
+ helpers.delete(camera.name);
92
+
93
+ // New items
94
+ const control = new OrbitControls(camera, element);
95
+ switch (camera.name) {
96
+ case 'Top':
97
+ case 'Bottom':
98
+ case 'Left':
99
+ case 'Right':
100
+ case 'Front':
101
+ case 'Back':
102
+ control.enableRotate = false;
103
+ break;
104
+ }
105
+ controls.set(camera.name, control);
106
+
107
+ if (camera instanceof PerspectiveCamera) {
108
+ const helper = new CameraHelper(camera);
109
+ helpers.set(camera.name, helper);
110
+ scene.add(helper);
111
+ }
112
+ };
113
+
114
+ const clearCamera = (camera: Camera) => {
115
+ const helper = helpers.get(camera.name);
116
+ if (helper !== undefined) {
117
+ scene.remove(helper);
118
+ helper.dispose();
119
+ helpers.delete(camera.name);
120
+ }
121
+ const control = controls.get(camera.name);
122
+ if (control !== undefined) {
123
+ control.dispose();
124
+ controls.delete(camera.name);
125
+ }
126
+ };
127
+
128
+ const killControls = () => {
129
+ controls.forEach((value: OrbitControls, key: string) => {
130
+ value.dispose();
131
+ const helper = helpers.get(key);
132
+ if (helper !== undefined) {
133
+ scene.remove(helper);
134
+ helper.dispose();
135
+ }
136
+ helpers.delete(key);
137
+ controls.delete(key);
138
+ });
139
+ controls.clear();
140
+ helpers.clear();
141
+ };
142
+
143
+ const assignControls = () => {
144
+ switch (mode) {
145
+ case 'Single':
146
+ createControls(tlCam, tlWindow.current!);
147
+ break;
148
+ case 'Side by Side':
149
+ case 'Stacked':
150
+ createControls(tlCam, tlWindow.current!);
151
+ createControls(trCam, trWindow.current!);
152
+ break;
153
+ case 'Quad':
154
+ createControls(tlCam, tlWindow.current!);
155
+ createControls(trCam, trWindow.current!);
156
+ createControls(blCam, blWindow.current!);
157
+ createControls(brCam, brWindow.current!);
158
+ break;
159
+ }
160
+ };
161
+
162
+ // Add scene + helpers
163
+ useEffect(() => {
164
+ scene.name = 'Debug Scene';
165
+ scene.add(props.scene);
166
+
167
+ const grid = new InfiniteGridHelper();
168
+ scene.add(grid);
169
+
170
+ const axisHelper = new AxesHelper(500);
171
+ axisHelper.name = 'axisHelper';
172
+ scene.add(axisHelper);
173
+ }, []);
174
+
175
+ // Resize handling + drawing
176
+ useEffect(() => {
177
+ const size = props.renderer.getSize(new Vector2());
178
+ let width = size.x;
179
+ let height = size.y;
180
+ let bw = Math.floor(width / 2);
181
+ let bh = Math.floor(height / 2);
182
+ let raf = -1;
183
+
184
+ const onResize = () => {
185
+ width = window.innerWidth - 300;
186
+ height = window.innerHeight;
187
+ bw = Math.floor(width / 2);
188
+ bh = Math.floor(height / 2);
189
+
190
+ let cw = width;
191
+ let ch = height;
192
+ switch (mode) {
193
+ case 'Side by Side':
194
+ cw = bw;
195
+ ch = height;
196
+ break;
197
+ case 'Stacked':
198
+ cw = width;
199
+ ch = bh;
200
+ break;
201
+ case 'Quad':
202
+ cw = bw;
203
+ ch = bh;
204
+ break;
205
+ }
206
+
207
+ cameras.forEach((camera) => {
208
+ if (camera instanceof OrthographicCamera) {
209
+ camera.left = cw / -2;
210
+ camera.right = cw / 2;
211
+ camera.top = ch / 2;
212
+ camera.bottom = ch / -2;
213
+ camera.updateProjectionMatrix();
214
+ } else if (camera instanceof PerspectiveCamera) {
215
+ camera.aspect = cw / ch;
216
+ camera.updateProjectionMatrix();
217
+ helpers.get(camera.name)?.update();
218
+ }
219
+ });
220
+ };
221
+
222
+ const drawSingle = () => {
223
+ props.renderer.setViewport(0, 0, width, height);
224
+ props.renderer.setScissor(0, 0, width, height);
225
+ props.renderer.render(scene, tlCam);
226
+ };
227
+
228
+ const drawDouble = () => {
229
+ if (mode === 'Side by Side') {
230
+ props.renderer.setViewport(0, 0, bw, height);
231
+ props.renderer.setScissor(0, 0, bw, height);
232
+ props.renderer.render(scene, tlCam);
233
+
234
+ props.renderer.setViewport(bw, 0, bw, height);
235
+ props.renderer.setScissor(bw, 0, bw, height);
236
+ props.renderer.render(scene, trCam);
237
+ } else {
238
+ const y = height - bh;
239
+ props.renderer.setViewport(0, y, width, bh);
240
+ props.renderer.setScissor(0, y, width, bh);
241
+ props.renderer.render(scene, tlCam);
242
+
243
+ props.renderer.setViewport(0, 0, width, bh);
244
+ props.renderer.setScissor(0, 0, width, bh);
245
+ props.renderer.render(scene, trCam);
246
+ }
247
+ };
248
+
249
+ const drawQuad = () => {
250
+ let x = 0;
251
+ let y = 0;
252
+ y = height - bh;
253
+
254
+ // TL
255
+ x = 0;
256
+ props.renderer.setViewport(x, y, bw, bh);
257
+ props.renderer.setScissor(x, y, bw, bh);
258
+ props.renderer.render(scene, tlCam);
259
+
260
+ // TR
261
+ x = bw;
262
+ props.renderer.setViewport(x, y, bw, bh);
263
+ props.renderer.setScissor(x, y, bw, bh);
264
+ props.renderer.render(scene, trCam);
265
+
266
+ y = 0;
267
+
268
+ // BL
269
+ x = 0;
270
+ props.renderer.setViewport(x, y, bw, bh);
271
+ props.renderer.setScissor(x, y, bw, bh);
272
+ props.renderer.render(scene, blCam);
273
+
274
+ // BR
275
+ x = bw;
276
+ props.renderer.setViewport(x, y, bw, bh);
277
+ props.renderer.setScissor(x, y, bw, bh);
278
+ props.renderer.render(scene, brCam);
279
+ };
280
+
281
+ const onUpdate = () => {
282
+ // Updates
283
+ controls.forEach((control: OrbitControls) => {
284
+ control.update();
285
+ });
286
+ props.scene['update']();
287
+
288
+ // Drawing
289
+ props.renderer.clear();
290
+ switch (mode) {
291
+ case 'Single':
292
+ drawSingle();
293
+ break;
294
+ case 'Side by Side':
295
+ case 'Stacked':
296
+ drawDouble();
297
+ break;
298
+ case 'Quad':
299
+ drawQuad();
300
+ break;
301
+ }
302
+
303
+ raf = requestAnimationFrame(onUpdate);
304
+ };
305
+
306
+ // Start rendering
307
+ assignControls();
308
+ window.addEventListener('resize', onResize);
309
+ onResize();
310
+ onUpdate();
311
+
312
+ return () => {
313
+ window.removeEventListener('resize', onResize);
314
+ cancelAnimationFrame(raf);
315
+ raf = -1;
316
+ };
317
+ }, [mode]);
318
+
319
+ const cameraOptions: string[] = [
320
+ 'Top',
321
+ 'Bottom',
322
+ 'Left',
323
+ 'Right',
324
+ 'Front',
325
+ 'Back',
326
+ 'Orthographic',
327
+ 'Debug',
328
+ ];
329
+ props.cameras.forEach((camera: Camera) => {
330
+ cameras.set(camera.name, camera);
331
+ cameraOptions.push(camera.name);
332
+ });
333
+
334
+ return (
335
+ <div className='multiview'>
336
+ <div className={`cameras ${mode === 'Single' || mode === 'Stacked' ? 'single' : ''}`}>
337
+ {mode === 'Single' && (
338
+ <>
339
+ <CameraWindow camera={tlCam} options={cameraOptions} ref={tlWindow} onSelect={(value: string) => {
340
+ controls.get(tlCam.name)?.dispose();
341
+ const camera = cameras.get(value);
342
+ if (camera !== undefined) {
343
+ clearCamera(tlCam);
344
+ tlCam = camera;
345
+ createControls(camera, tlWindow.current!);
346
+ }
347
+ }} />
348
+ </>
349
+ )}
350
+
351
+ {(mode === 'Side by Side' || mode === 'Stacked') && (
352
+ <>
353
+ <CameraWindow camera={tlCam} options={cameraOptions} ref={tlWindow} onSelect={(value: string) => {
354
+ controls.get(tlCam.name)?.dispose();
355
+ const camera = cameras.get(value);
356
+ if (camera !== undefined) {
357
+ clearCamera(tlCam);
358
+ tlCam = camera;
359
+ createControls(camera, tlWindow.current!);
360
+ }
361
+ }} />
362
+ <CameraWindow camera={trCam} options={cameraOptions} ref={trWindow} onSelect={(value: string) => {
363
+ controls.get(trCam.name)?.dispose();
364
+ const camera = cameras.get(value);
365
+ if (camera !== undefined) {
366
+ clearCamera(trCam);
367
+ trCam = camera;
368
+ createControls(camera, trWindow.current!);
369
+ }
370
+ }} />
371
+ </>
372
+ )}
373
+
374
+ {mode === 'Quad' && (
375
+ <>
376
+ <CameraWindow camera={tlCam} options={cameraOptions} ref={tlWindow} onSelect={(value: string) => {
377
+ controls.get(tlCam.name)?.dispose();
378
+ const camera = cameras.get(value);
379
+ if (camera !== undefined) {
380
+ clearCamera(tlCam);
381
+ tlCam = camera;
382
+ createControls(camera, tlWindow.current!);
383
+ }
384
+ }} />
385
+ <CameraWindow camera={trCam} options={cameraOptions} ref={trWindow} onSelect={(value: string) => {
386
+ controls.get(trCam.name)?.dispose();
387
+ const camera = cameras.get(value);
388
+ if (camera !== undefined) {
389
+ clearCamera(trCam);
390
+ trCam = camera;
391
+ createControls(camera, trWindow.current!);
392
+ }
393
+ }} />
394
+ <CameraWindow camera={blCam} options={cameraOptions} ref={blWindow} onSelect={(value: string) => {
395
+ controls.get(blCam.name)?.dispose();
396
+ const camera = cameras.get(value);
397
+ if (camera !== undefined) {
398
+ clearCamera(blCam);
399
+ blCam = camera;
400
+ createControls(camera, blWindow.current!);
401
+ }
402
+ }} />
403
+ <CameraWindow camera={brCam} options={cameraOptions} ref={brWindow} onSelect={(value: string) => {
404
+ controls.get(brCam.name)?.dispose();
405
+ const camera = cameras.get(value);
406
+ if (camera !== undefined) {
407
+ clearCamera(brCam);
408
+ brCam = camera;
409
+ createControls(camera, brWindow.current!);
410
+ }
411
+ }} />
412
+ </>
413
+ )}
414
+ </div>
415
+
416
+ <div className='settings'>
417
+ {/* Mode */}
418
+ <Dropdown
419
+ index={ModeOptions.indexOf(mode)}
420
+ options={ModeOptions}
421
+ onSelect={(value: string) => {
422
+ if (value === mode) return;
423
+ killControls();
424
+ setMode(value as MultiViewMode);
425
+ }}
426
+ />
427
+ {/* Render Mode */}
428
+ <Dropdown
429
+ index={0}
430
+ options={renderOptions}
431
+ onSelect={(value: string) => {
432
+ if (value === currentRenderMode) return;
433
+ currentRenderMode = value as RenderMode;
434
+ switch (currentRenderMode) {
435
+ case 'Default':
436
+ scene.overrideMaterial = null;
437
+ break;
438
+ case 'Normals':
439
+ scene.overrideMaterial = normalsMaterial;
440
+ break;
441
+ case 'Wireframe':
442
+ scene.overrideMaterial = wireframeMaterial;
443
+ break;
444
+ }
445
+ }}
446
+ />
447
+ </div>
448
+ </div>
449
+ );
450
+ }