@tomorrowevening/hermes 0.0.36 → 0.0.37

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 (69) hide show
  1. package/dist/hermes.cjs.js +16 -16
  2. package/dist/hermes.esm.js +1446 -1498
  3. package/dist/hermes.umd.js +16 -16
  4. package/dist/style.css +1 -1
  5. package/package.json +2 -1
  6. package/src/core/Application.ts +111 -0
  7. package/src/core/RemoteController.ts +60 -0
  8. package/src/core/remote/BaseRemote.ts +16 -0
  9. package/src/core/remote/RemoteComponents.ts +45 -0
  10. package/src/core/remote/RemoteTheatre.ts +300 -0
  11. package/src/core/remote/RemoteThree.ts +143 -0
  12. package/src/core/remote/RemoteTweakpane.ts +194 -0
  13. package/src/core/types.ts +56 -0
  14. package/src/editor/Editor.tsx +20 -0
  15. package/src/editor/components/Draggable.tsx +40 -0
  16. package/src/editor/components/DraggableItem.tsx +22 -0
  17. package/src/editor/components/Dropdown.tsx +38 -0
  18. package/src/editor/components/DropdownItem.tsx +64 -0
  19. package/src/editor/components/NavButton.tsx +11 -0
  20. package/src/editor/components/content.ts +2 -0
  21. package/src/editor/components/icons/CloseIcon.tsx +7 -0
  22. package/src/editor/components/icons/DragIcon.tsx +9 -0
  23. package/src/editor/components/types.ts +41 -0
  24. package/src/editor/global.ts +20 -0
  25. package/src/editor/multiView/CameraWindow.tsx +74 -0
  26. package/src/editor/multiView/InfiniteGridHelper.ts +24 -0
  27. package/src/editor/multiView/InfiniteGridMaterial.ts +127 -0
  28. package/src/editor/multiView/MultiView.scss +101 -0
  29. package/src/editor/multiView/MultiView.tsx +636 -0
  30. package/src/editor/multiView/MultiViewData.ts +59 -0
  31. package/src/editor/multiView/UVMaterial.ts +55 -0
  32. package/src/editor/scss/_debug.scss +58 -0
  33. package/src/editor/scss/_draggable.scss +43 -0
  34. package/src/editor/scss/_dropdown.scss +84 -0
  35. package/src/editor/scss/_sidePanel.scss +278 -0
  36. package/src/editor/scss/_theme.scss +9 -0
  37. package/src/editor/scss/index.scss +67 -0
  38. package/src/editor/sidePanel/Accordion.tsx +41 -0
  39. package/src/editor/sidePanel/ChildObject.tsx +57 -0
  40. package/src/editor/sidePanel/ContainerObject.tsx +11 -0
  41. package/src/editor/sidePanel/SidePanel.tsx +64 -0
  42. package/src/editor/sidePanel/ToggleBtn.tsx +27 -0
  43. package/src/editor/sidePanel/inspector/Inspector.tsx +119 -0
  44. package/src/editor/sidePanel/inspector/InspectorField.tsx +198 -0
  45. package/src/editor/sidePanel/inspector/InspectorGroup.tsx +50 -0
  46. package/src/editor/sidePanel/inspector/SceneInspector.tsx +84 -0
  47. package/src/editor/sidePanel/inspector/inspector.scss +161 -0
  48. package/src/editor/sidePanel/inspector/utils/InspectAnimation.tsx +102 -0
  49. package/src/editor/sidePanel/inspector/utils/InspectCamera.tsx +75 -0
  50. package/src/editor/sidePanel/inspector/utils/InspectLight.tsx +62 -0
  51. package/src/editor/sidePanel/inspector/utils/InspectMaterial.tsx +710 -0
  52. package/src/editor/sidePanel/inspector/utils/InspectTransform.tsx +113 -0
  53. package/src/editor/sidePanel/types.ts +130 -0
  54. package/src/editor/sidePanel/utils.ts +278 -0
  55. package/src/editor/utils.ts +117 -0
  56. package/src/example/CustomEditor.tsx +78 -0
  57. package/src/example/components/App.css +6 -0
  58. package/src/example/components/App.tsx +246 -0
  59. package/src/example/constants.ts +52 -0
  60. package/src/example/index.scss +45 -0
  61. package/src/example/main.tsx +37 -0
  62. package/src/example/three/BaseScene.ts +42 -0
  63. package/src/example/three/CustomMaterial.ts +72 -0
  64. package/src/example/three/FBXAnimation.ts +26 -0
  65. package/src/example/three/Scene1.ts +225 -0
  66. package/src/example/three/Scene2.ts +138 -0
  67. package/src/example/three/loader.ts +110 -0
  68. package/src/index.ts +27 -0
  69. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,710 @@
1
+ import { AddEquation, AdditiveBlending, BackSide, Color, ConstantAlphaFactor, ConstantColorFactor, CustomBlending, DoubleSide, DstAlphaFactor, DstColorFactor, FrontSide, MaxEquation, MinEquation, MultiplyBlending, NoBlending, NormalBlending, OneFactor, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, ReverseSubtractEquation, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, SubtractEquation, SubtractiveBlending, Texture, ZeroFactor } from 'three';
2
+ import InspectorGroup from '../InspectorGroup';
3
+ import { RemoteMaterial, RemoteObject } from '../../types';
4
+ import RemoteThree from '@/core/remote/RemoteThree';
5
+ import { setItemProps, textureFromSrc } from '../../utils';
6
+ import { capitalize } from '@/editor/utils';
7
+
8
+ export function acceptedMaterialNames(name: string): boolean {
9
+ return !(
10
+ name === 'alphaHash' ||
11
+ name === 'alphaToCoverage' ||
12
+ name === 'attenuationDistance' ||
13
+ name === 'blendDstAlpha' ||
14
+ name === 'colorWrite' ||
15
+ name === 'combine' ||
16
+ name === 'defaultAttributeValues' ||
17
+ name === 'depthFunc' ||
18
+ name === 'forceSinglePass' ||
19
+ name === 'glslVersion' ||
20
+ name === 'linecap' ||
21
+ name === 'linejoin' ||
22
+ name === 'linewidth' ||
23
+ name === 'normalMapType' ||
24
+ name === 'precision' ||
25
+ name === 'premultipliedAlpha' ||
26
+ name === 'shadowSide' ||
27
+ name === 'toneMapped' ||
28
+ name === 'uniformsGroups' ||
29
+ name === 'uniformsNeedUpdate' ||
30
+ name === 'userData' ||
31
+ name === 'vertexColors' ||
32
+ name === 'version' ||
33
+ name === 'wireframeLinecap' ||
34
+ name === 'wireframeLinejoin' ||
35
+ name === 'wireframeLinewidth' ||
36
+ name.slice(0, 4) === 'clip' ||
37
+ name.slice(0, 7) === 'polygon' ||
38
+ name.slice(0, 7) === 'stencil' ||
39
+ name.slice(0, 2) === 'is'
40
+ );
41
+ }
42
+
43
+ export function prettyName(name: string): string {
44
+ switch (name) {
45
+ case 'alphaMap': return 'Alpha Map';
46
+ case 'anisotropyMap': return 'Anisotropy Map';
47
+ case 'anisotropyRotation': return 'Anisotropy Rotation';
48
+ case 'aoMap': return 'AO Map';
49
+ case 'aoMapIntensity': return 'AO Map Intensity';
50
+ case 'attenuationColor': return 'Attenuation Color';
51
+ case 'blendAlpha': return 'Blend Alpha';
52
+ case 'blendColor': return 'Blend Color';
53
+ case 'blendDst': return 'Blend Dst';
54
+ case 'blendDstAlpha': return 'Blend Dst Alha';
55
+ case 'blendEquation': return 'Blend Equation';
56
+ case 'blendEquationAlpha': return 'Blend Equation Alpha';
57
+ case 'blending': return 'Blending';
58
+ case 'blendSrc': return 'Blend Src';
59
+ case 'blendSrcAlpha': return 'Blend Src Alpha';
60
+ case 'bumpMap': return 'Bump Map';
61
+ case 'bumpScale': return 'Bump Scale';
62
+ case 'clearcoatMap': return 'Clearcoat Map';
63
+ case 'clearcoatNormalMap': return 'Clearcoat Normal Map';
64
+ case 'clearcoatNormalScale': return 'Clearcoat Normal Scale';
65
+ case 'clearcoatRoughness': return 'Clearcoat Roughness';
66
+ case 'clearcoatRoughnessMap': return 'Clearcoat Roughness Map';
67
+ case 'color': return 'Color';
68
+ case 'defines': return 'Defines';
69
+ case 'depthTest': return 'Depth Test';
70
+ case 'depthWrite': return 'Depth Write';
71
+ case 'displacementBias': return 'Displacement Bias';
72
+ case 'displacementMap': return 'Displacement Map';
73
+ case 'displacementScale': return 'Displacement Scale';
74
+ case 'dithering': return 'Dithering';
75
+ case 'emissive': return 'Emissive';
76
+ case 'emissiveMap': return 'Emissive Map';
77
+ case 'emissiveIntensity': return 'Emissive Intensity';
78
+ case 'envMap': return 'Environment Map';
79
+ case 'envMapIntensity': return 'Environment Map Intensity';
80
+ case 'extensions': return 'Extensions';
81
+ case 'flatShading': return 'Flat Shading';
82
+ case 'fragmentShader': return 'Fragment Shader';
83
+ case 'fog': return 'Fog';
84
+ case 'gradientMap': return 'Gradient Map';
85
+ case 'ior': return 'IOR';
86
+ case 'iridescenceIOR': return 'Iridescence IOR';
87
+ case 'iridescenceMap': return 'Iridescence Map';
88
+ case 'iridescenceThicknessMap': return 'Iridescence Thickness Map';
89
+ case 'iridescenceThicknessRange': return 'Iridescence Thickness Range';
90
+ case 'lights': return 'Lights';
91
+ case 'lightMap': return 'Light Map';
92
+ case 'lightMapIntensity': return 'Light Map Intensity';
93
+ case 'map': return 'Map';
94
+ case 'matcap': return 'Matcap';
95
+ case 'metalness': return 'Metalness';
96
+ case 'metalnessMap': return 'Metalness Map';
97
+ case 'name': return 'Name';
98
+ case 'normalMap': return 'Normal Map';
99
+ case 'normalScale': return 'Normal Scale';
100
+ case 'opacity': return 'Opacity';
101
+ case 'reflectivity': return 'Reflectivity';
102
+ case 'refractionRatio': return 'Refraction Ratio';
103
+ case 'roughness': return 'Roughness';
104
+ case 'roughnessMap': return 'Roughness Map';
105
+ case 'sheenColor': return 'Sheen Color';
106
+ case 'sheenColorMap': return 'Sheen Color Map';
107
+ case 'sheenRoughness': return 'Sheen Roughness';
108
+ case 'sheenRoughnessMap': return 'Sheen Roughness Map';
109
+ case 'shininess': return 'Shininess';
110
+ case 'side': return 'Side';
111
+ case 'size': return 'Size';
112
+ case 'sizeAttenuation': return 'Size Attenuation';
113
+ case 'specular': return 'Specular';
114
+ case 'specularColor': return 'Specular Color';
115
+ case 'specularColorMap': return 'Specular Color Map';
116
+ case 'specularIntensity': return 'Specular Intensity';
117
+ case 'specularIntensityMap': return 'Specular Map Intensity';
118
+ case 'thickness': return 'Thickness';
119
+ case 'thicknessMap': return 'Thickness Map';
120
+ case 'transmission': return 'Transmission';
121
+ case 'transmissionMap': return 'Transmission Map';
122
+ case 'transparent': return 'Transparent';
123
+ case 'type': return 'Type';
124
+ case 'uuid': return 'UUID';
125
+ case 'uniforms': return 'Uniforms';
126
+ case 'vertexShader': return 'Vertex Shader';
127
+ case 'visible': return 'Visible';
128
+ case 'wireframe': return 'Wireframe';
129
+ }
130
+ return name;
131
+ }
132
+
133
+ export function clampedNames(name: string): boolean {
134
+ return (
135
+ name.toLowerCase().search('intensity') > -1 ||
136
+ name === 'anisotropyRotation' ||
137
+ name === 'blendAlpha' ||
138
+ name === 'bumpScale' ||
139
+ name === 'clearcoatRoughness' ||
140
+ name === 'displacementBias' ||
141
+ name === 'displacementScale' ||
142
+ name === 'metalness' ||
143
+ name === 'opacity' ||
144
+ name === 'reflectivity' ||
145
+ name === 'refractionRatio' ||
146
+ name === 'roughness' ||
147
+ name === 'sheenRoughness' ||
148
+ name === 'thickness'
149
+ );
150
+ }
151
+
152
+ export function uploadLocalImage(): Promise<string> {
153
+ const inputElement = document.createElement('input');
154
+ inputElement.type = 'file';
155
+ return new Promise((resolve: any, reject: any) => {
156
+ inputElement.addEventListener('change', function() {
157
+ if (inputElement.files === null) {
158
+ reject();
159
+ } else {
160
+ const selectedFile = inputElement.files[0];
161
+ const reader = new FileReader();
162
+ reader.onload = function(e: any) {
163
+ resolve(e.target.result);
164
+ };
165
+ reader.readAsDataURL(selectedFile);
166
+ }
167
+ });
168
+ inputElement.click();
169
+ });
170
+ }
171
+
172
+ // Material side
173
+
174
+ const materialSideOpts: any[] = [
175
+ {
176
+ title: 'Front',
177
+ value: FrontSide,
178
+ },
179
+ {
180
+ title: 'Back',
181
+ value: BackSide,
182
+ },
183
+ {
184
+ title: 'Double',
185
+ value: DoubleSide,
186
+ },
187
+ ];
188
+
189
+ // Blending
190
+
191
+ const blendingOpts: any[] = [
192
+ {
193
+ title: 'No Blending',
194
+ value: NoBlending,
195
+ },
196
+ {
197
+ title: 'Normal',
198
+ value: NormalBlending,
199
+ },
200
+ {
201
+ title: 'Additive',
202
+ value: AdditiveBlending,
203
+ },
204
+ {
205
+ title: 'Subtractive',
206
+ value: SubtractiveBlending,
207
+ },
208
+ {
209
+ title: 'Multiply',
210
+ value: MultiplyBlending,
211
+ },
212
+ {
213
+ title: 'Custom',
214
+ value: CustomBlending,
215
+ }
216
+ ];
217
+
218
+ const blendingEquationOpts: any[] = [
219
+ {
220
+ title: 'Add',
221
+ value: AddEquation,
222
+ },
223
+ {
224
+ title: 'Subtract',
225
+ value: SubtractEquation,
226
+ },
227
+ {
228
+ title: 'Reverse Subtract',
229
+ value: ReverseSubtractEquation,
230
+ },
231
+ {
232
+ title: 'Min',
233
+ value: MinEquation,
234
+ },
235
+ {
236
+ title: 'Max',
237
+ value: MaxEquation,
238
+ },
239
+ ];
240
+
241
+ // Blending Equations (Source)
242
+ const blendSourceOpts: any[] = [
243
+ {
244
+ title: 'Zero',
245
+ valye: ZeroFactor,
246
+ },
247
+ {
248
+ title: 'One',
249
+ valye: OneFactor,
250
+ },
251
+ {
252
+ title: 'Src Color',
253
+ valye: SrcColorFactor,
254
+ },
255
+ {
256
+ title: 'One Minus Src Color',
257
+ valye: OneMinusSrcColorFactor,
258
+ },
259
+ {
260
+ title: 'Src Alpha',
261
+ valye: SrcAlphaFactor,
262
+ },
263
+ {
264
+ title: 'One Minus Src Alpha',
265
+ valye: OneMinusSrcAlphaFactor,
266
+ },
267
+ {
268
+ title: 'Dst Alpha',
269
+ valye: DstAlphaFactor,
270
+ },
271
+ {
272
+ title: 'One Minus Dst Alpha',
273
+ valye: OneMinusDstAlphaFactor,
274
+ },
275
+ {
276
+ title: 'Dst Color',
277
+ valye: DstColorFactor,
278
+ },
279
+ {
280
+ title: 'One Minus Dst Color',
281
+ valye: OneMinusDstColorFactor,
282
+ },
283
+ {
284
+ title: 'Src Alpha Saturate',
285
+ valye: SrcAlphaSaturateFactor,
286
+ },
287
+ {
288
+ title: 'Constant Color',
289
+ valye: ConstantColorFactor,
290
+ },
291
+ {
292
+ title: 'One Minus Constant Color',
293
+ valye: OneMinusConstantColorFactor,
294
+ },
295
+ {
296
+ title: 'Constant Alpha',
297
+ valye: ConstantAlphaFactor,
298
+ },
299
+ {
300
+ title: 'One Minus Constant Alpha',
301
+ valye: OneMinusConstantAlphaFactor,
302
+ },
303
+ ];
304
+
305
+ // Blending Equations (Destination)
306
+ const blendDestinationOpts: any[] = [
307
+ {
308
+ title: 'Zero',
309
+ valye: ZeroFactor,
310
+ },
311
+ {
312
+ title: 'One',
313
+ valye: OneFactor,
314
+ },
315
+ {
316
+ title: 'Src Color',
317
+ valye: SrcColorFactor,
318
+ },
319
+ {
320
+ title: 'One Minus Src Color',
321
+ valye: OneMinusSrcColorFactor,
322
+ },
323
+ {
324
+ title: 'Src Alpha',
325
+ valye: SrcAlphaFactor,
326
+ },
327
+ {
328
+ title: 'One Minus Src Alpha',
329
+ valye: OneMinusSrcAlphaFactor,
330
+ },
331
+ {
332
+ title: 'Dst Alpha',
333
+ valye: DstAlphaFactor,
334
+ },
335
+ {
336
+ title: 'One Minus Dst Alpha',
337
+ valye: OneMinusDstAlphaFactor,
338
+ },
339
+ {
340
+ title: 'Dst Color',
341
+ valye: DstColorFactor,
342
+ },
343
+ {
344
+ title: 'One Minus Dst Color',
345
+ valye: OneMinusDstColorFactor,
346
+ },
347
+ {
348
+ title: 'Constant Color',
349
+ valye: ConstantColorFactor,
350
+ },
351
+ {
352
+ title: 'One Minus Constant Color',
353
+ valye: OneMinusConstantColorFactor,
354
+ },
355
+ {
356
+ title: 'Constant Alpha',
357
+ valye: ConstantAlphaFactor,
358
+ },
359
+ {
360
+ title: 'One Minus Constant Alpha',
361
+ valye: OneMinusConstantAlphaFactor,
362
+ },
363
+ ];
364
+
365
+ function updateFieldOptions(obj: any, options: any[]) {
366
+ obj.needsUpdate = true;
367
+ obj.type = 'option';
368
+ obj.options = options;
369
+ }
370
+
371
+ export function inspectMaterialItems(material: RemoteMaterial, object: RemoteObject, three: RemoteThree): any[] {
372
+ const items: any[] = [];
373
+ for (const i in material) {
374
+ if (!acceptedMaterialNames(i)) continue;
375
+
376
+ const propType = typeof material[i];
377
+ const value = material[i];
378
+ if (propType === 'boolean' || propType === 'number' || propType === 'string') {
379
+ const newField = {
380
+ title: prettyName(i),
381
+ prop: i,
382
+ type: propType,
383
+ value: value,
384
+ min: undefined,
385
+ max: undefined,
386
+ needsUpdate: propType === 'boolean',
387
+ onChange: (prop: string, value: any) => {
388
+ three.updateObject(object.uuid, `material.${prop}`, value);
389
+ if (newField.needsUpdate) three.updateObject(object.uuid, 'material.needsUpdate', true);
390
+ // Local update
391
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
392
+ if (child !== undefined) setItemProps(child, `material.${prop}`, value);
393
+ },
394
+ };
395
+
396
+ switch (i) {
397
+ case 'blending':
398
+ updateFieldOptions(newField, blendingOpts);
399
+ break;
400
+ case 'blendDst':
401
+ updateFieldOptions(newField, blendDestinationOpts);
402
+ break;
403
+ case 'blendEquation':
404
+ updateFieldOptions(newField, blendingEquationOpts);
405
+ break;
406
+ case 'blendSrc':
407
+ updateFieldOptions(newField, blendSourceOpts);
408
+ break;
409
+ case 'side':
410
+ updateFieldOptions(newField, materialSideOpts);
411
+ break;
412
+ }
413
+
414
+ if (clampedNames(i)) {
415
+ newField.value = Number(value);
416
+ // @ts-ignore
417
+ newField.type = 'range';
418
+ // @ts-ignore
419
+ newField.min = 0;
420
+ // @ts-ignore
421
+ newField.max = 1;
422
+ // @ts-ignore
423
+ newField.step = 0.01;
424
+ }
425
+
426
+ const isShader = propType === 'string' && (i === 'vertexShader' || i === 'fragmentShader');
427
+ if (isShader) {
428
+ newField['disabled'] = false;
429
+ newField['latest'] = newField.value;
430
+ newField.onChange = (_: string, updatedValue: string) => {
431
+ newField['latest'] = updatedValue;
432
+ };
433
+ }
434
+ items.push(newField);
435
+
436
+ if (isShader) {
437
+ items.push({
438
+ title: `${capitalize(i)} - Update`,
439
+ type: 'button',
440
+ onChange: () => {
441
+ three.updateObject(object.uuid, `material.${i}`, newField['latest']);
442
+ three.updateObject(object.uuid, 'material.needsUpdate', true);
443
+ // Local update
444
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
445
+ if (child !== undefined) {
446
+ setItemProps(child, `material.${i}`, newField['latest']);
447
+ child['material'].needsUpdate = true;
448
+ }
449
+ },
450
+ });
451
+ }
452
+ } else if (propType === 'object') {
453
+ if (value.isColor) {
454
+ items.push({
455
+ title: prettyName(i),
456
+ prop: i,
457
+ type: 'color',
458
+ value: value,
459
+ onChange: (prop: string, value: any) => {
460
+ const newValue = new Color(value);
461
+ three.updateObject(object.uuid, `material.${prop}`, newValue);
462
+ // Local update
463
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
464
+ if (child !== undefined) setItemProps(child, `material.${prop}`, newValue);
465
+ },
466
+ });
467
+ } else if (Array.isArray(value)) {
468
+ const subChildren: any[] = [];
469
+ for (const index in value) {
470
+ subChildren.push({
471
+ title: `${index}`,
472
+ type: `${typeof value[index]}`,
473
+ value: value[index],
474
+ onChange: (prop: string, value: any) => {
475
+ three.updateObject(object.uuid, `material.${i}`, value);
476
+ // Local update
477
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
478
+ if (child !== undefined) setItemProps(child, `material.${i}`, value);
479
+ },
480
+ });
481
+ }
482
+ items.push({
483
+ title: prettyName(i),
484
+ items: subChildren,
485
+ });
486
+ } else {
487
+ const subChildren: any[] = [];
488
+ for (const n in value) {
489
+ const propValue = value[n];
490
+ const propValueType = typeof propValue;
491
+ switch (propValueType) {
492
+ case 'boolean':
493
+ case 'number':
494
+ case 'string':
495
+ if (n === 'src') {
496
+ items.push({
497
+ title: prettyName(i),
498
+ type: 'image',
499
+ value: propValue,
500
+ onChange: (prop: string, value: any) => {
501
+ three.createTexture(object.uuid, `material.${i}`, value);
502
+ // Local update
503
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
504
+ if (child !== undefined) {
505
+ textureFromSrc(value).then((texture: Texture) => {
506
+ setItemProps(child, `material.${i}`, texture);
507
+ setItemProps(child, `material.needsUpdate`, true);
508
+ });
509
+ }
510
+ },
511
+ });
512
+ } else {
513
+ subChildren.push({
514
+ title: `${prettyName(n)}`,
515
+ prop: `material.${i}.${n}`,
516
+ type: `${typeof material[i][n]}`,
517
+ value: value[n],
518
+ onChange: (prop: string, value: any) => {
519
+ three.updateObject(object.uuid, `material.${i}.${n}`, value);
520
+ // Local update
521
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
522
+ if (child !== undefined) setItemProps(child, `material.${i}.${n}`, value);
523
+ },
524
+ });
525
+ }
526
+ break;
527
+ case 'object':
528
+ // Uniform textures
529
+ if (propValue.value !== undefined && propValue.value.src !== undefined) {
530
+ subChildren.push({
531
+ title: prettyName(n),
532
+ type: 'image',
533
+ value: propValue.value.src,
534
+ onChange: (_: string, newValue: any) => {
535
+ three.createTexture(object.uuid, `material.${i}.${n}.value`, value);
536
+ // Local update
537
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
538
+ if (child !== undefined) {
539
+ textureFromSrc(newValue).then((texture: Texture) => {
540
+ setItemProps(child, `material.${i}.${n}.value`, texture);
541
+ });
542
+ }
543
+ },
544
+ });
545
+ } else if (i === 'uniforms') {
546
+ const pv = propValue.value;
547
+ // Probably in Uniforms
548
+ const makeFloat = (vTitle: string, vProp: string, floatValue: number) => {
549
+ return {
550
+ title: vTitle,
551
+ type: 'number',
552
+ value: floatValue,
553
+ step: 0.01,
554
+ onChange: (_: string, updatedValue: any) => {
555
+ const id = `material.uniforms.${n}.value.${vProp}`;
556
+ three.updateObject(object.uuid, id, updatedValue);
557
+ // Local update
558
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
559
+ if (child !== undefined) setItemProps(child, id, updatedValue);
560
+ },
561
+ };
562
+ };
563
+ if (typeof propValue.value === 'number') {
564
+ subChildren.push({
565
+ title: n,
566
+ type: 'number',
567
+ value: propValue.value,
568
+ onChange: (prop: string, value: any) => {
569
+ const id = `material.${i}.${prop}.value`;
570
+ three.updateObject(object.uuid, id, value);
571
+ // Local update
572
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
573
+ if (child !== undefined) setItemProps(child, id, value);
574
+ },
575
+ });
576
+ } else if (pv['r'] !== undefined && pv['g'] !== undefined && pv['b'] !== undefined) {
577
+ subChildren.push({
578
+ title: n,
579
+ type: 'color',
580
+ value: propValue.value,
581
+ onChange: (prop: string, value: any) => {
582
+ const newValue = new Color(value);
583
+ const id = `material.${i}.${prop}.value`;
584
+ three.updateObject(object.uuid, id, newValue);
585
+ // Local update
586
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
587
+ if (child !== undefined) setItemProps(child, id, newValue);
588
+ },
589
+ });
590
+ } else if (pv['x'] !== undefined && pv['y'] !== undefined && pv['z'] === undefined && pv['w'] === undefined) {
591
+ subChildren.push(
592
+ {
593
+ title: n,
594
+ items: [
595
+ makeFloat('X', 'x', propValue.value.x),
596
+ makeFloat('Y', 'y', propValue.value.y),
597
+ ]
598
+ }
599
+ );
600
+ } else if (pv['x'] !== undefined && pv['y'] !== undefined && pv['z'] !== undefined && pv['w'] === undefined) {
601
+ subChildren.push(
602
+ {
603
+ title: n,
604
+ items: [
605
+ makeFloat('X', 'x', propValue.value.x),
606
+ makeFloat('Y', 'y', propValue.value.y),
607
+ makeFloat('Z', 'z', propValue.value.z),
608
+ ]
609
+ }
610
+ );
611
+ } else if (pv['x'] !== undefined && pv['y'] !== undefined && pv['z'] !== undefined && pv['w'] !== undefined) {
612
+ subChildren.push(
613
+ {
614
+ title: n,
615
+ items: [
616
+ makeFloat('X', 'x', propValue.value.x),
617
+ makeFloat('Y', 'y', propValue.value.y),
618
+ makeFloat('Z', 'z', propValue.value.z),
619
+ makeFloat('W', 'w', propValue.value.w),
620
+ ]
621
+ }
622
+ );
623
+ } else if (pv['elements'] !== undefined) {
624
+ const matrix = pv.elements;
625
+ const matrixChildren: any[] = [];
626
+ for (let i = 0; i < matrix.length; i++) {
627
+ matrixChildren.push(makeFloat(i.toString(), i.toString(), matrix[i]));
628
+ }
629
+ subChildren.push(
630
+ {
631
+ title: n,
632
+ items: matrixChildren
633
+ }
634
+ );
635
+ } else {
636
+ console.log('>>> need to add this format:', n, pv);
637
+ }
638
+ } else {
639
+ subChildren.push({
640
+ title: n,
641
+ type: `${typeof propValue.value}`,
642
+ value: propValue.value,
643
+ onChange: (_: string, newValue: any) => {
644
+ three.updateObject(object.uuid, `material.${i}.${n}.value`, newValue);
645
+ // Local update
646
+ const child = three.scene?.getObjectByProperty('uuid', object.uuid);
647
+ if (child !== undefined) setItemProps(child, `material.${i}.${n}.value`, newValue);
648
+ },
649
+ });
650
+ }
651
+ break;
652
+ }
653
+ }
654
+
655
+ if (subChildren.length > 0) {
656
+ items.push({
657
+ title: prettyName(i),
658
+ items: subChildren,
659
+ });
660
+ }
661
+ }
662
+ } else if (value !== undefined) {
663
+ console.log('other:', i, propType, value);
664
+ }
665
+ }
666
+
667
+ // Sort items
668
+ items.sort((a: any, b: any) => {
669
+ if (a.title < b.title) return -1;
670
+ if (a.title > b.title) return 1;
671
+ return 0;
672
+ });
673
+
674
+ items.push({
675
+ title: 'Update Material',
676
+ type: 'button',
677
+ onChange: () => {
678
+ three.updateObject(object.uuid, `material.needsUpdate`, true);
679
+ },
680
+ });
681
+
682
+ return items;
683
+ }
684
+
685
+ // RemoteMaterial | RemoteMaterial[]
686
+ export function InspectMaterial(object: RemoteObject, three: RemoteThree): any {
687
+ const material = object.material!;
688
+ if (Array.isArray(material)) {
689
+ const items: any[] = [];
690
+ const total = material.length;
691
+ for (let i = 0; i < total; i++) {
692
+ items.push(
693
+ <InspectorGroup
694
+ title={`Material ${i}`}
695
+ key={`Material ${i}`}
696
+ items={inspectMaterialItems(material[i], object, three)}
697
+ />
698
+ );
699
+ }
700
+ return <>{items}</>;
701
+ } else {
702
+ return (
703
+ <InspectorGroup
704
+ title='Material'
705
+ items={inspectMaterialItems(material, object, three)}
706
+ />
707
+ );
708
+ }
709
+ return null;
710
+ }