@thanhthbm/scene-editor 0.1.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 (136) hide show
  1. package/README.md +169 -0
  2. package/dist/index.css +921 -0
  3. package/dist/index.js +14076 -0
  4. package/package.json +49 -0
  5. package/src/editor/css/main.css +1030 -0
  6. package/src/editor/js/Animation.js +618 -0
  7. package/src/editor/js/AnimationResizer.js +73 -0
  8. package/src/editor/js/Command.js +40 -0
  9. package/src/editor/js/Config.js +86 -0
  10. package/src/editor/js/Editor.js +1006 -0
  11. package/src/editor/js/EditorControls.js +489 -0
  12. package/src/editor/js/GLTFImportDialog.js +112 -0
  13. package/src/editor/js/History.js +321 -0
  14. package/src/editor/js/Loader.js +1162 -0
  15. package/src/editor/js/LoaderUtils.js +90 -0
  16. package/src/editor/js/Menubar.Add.js +559 -0
  17. package/src/editor/js/Menubar.Edit.js +155 -0
  18. package/src/editor/js/Menubar.File.js +585 -0
  19. package/src/editor/js/Menubar.Help.js +73 -0
  20. package/src/editor/js/Menubar.Render.js +858 -0
  21. package/src/editor/js/Menubar.Status.js +51 -0
  22. package/src/editor/js/Menubar.View.js +193 -0
  23. package/src/editor/js/Menubar.js +29 -0
  24. package/src/editor/js/Player.js +53 -0
  25. package/src/editor/js/Resizer.js +65 -0
  26. package/src/editor/js/Script.js +532 -0
  27. package/src/editor/js/Selector.js +129 -0
  28. package/src/editor/js/Sidebar.Geometry.BoxGeometry.js +121 -0
  29. package/src/editor/js/Sidebar.Geometry.BufferGeometry.js +124 -0
  30. package/src/editor/js/Sidebar.Geometry.CapsuleGeometry.js +109 -0
  31. package/src/editor/js/Sidebar.Geometry.CircleGeometry.js +97 -0
  32. package/src/editor/js/Sidebar.Geometry.CylinderGeometry.js +121 -0
  33. package/src/editor/js/Sidebar.Geometry.DodecahedronGeometry.js +73 -0
  34. package/src/editor/js/Sidebar.Geometry.ExtrudeGeometry.js +196 -0
  35. package/src/editor/js/Sidebar.Geometry.IcosahedronGeometry.js +73 -0
  36. package/src/editor/js/Sidebar.Geometry.LatheGeometry.js +98 -0
  37. package/src/editor/js/Sidebar.Geometry.Modifiers.js +73 -0
  38. package/src/editor/js/Sidebar.Geometry.OctahedronGeometry.js +74 -0
  39. package/src/editor/js/Sidebar.Geometry.PlaneGeometry.js +97 -0
  40. package/src/editor/js/Sidebar.Geometry.RingGeometry.js +121 -0
  41. package/src/editor/js/Sidebar.Geometry.ShapeGeometry.js +76 -0
  42. package/src/editor/js/Sidebar.Geometry.SphereGeometry.js +133 -0
  43. package/src/editor/js/Sidebar.Geometry.TetrahedronGeometry.js +74 -0
  44. package/src/editor/js/Sidebar.Geometry.TextGeometry.js +136 -0
  45. package/src/editor/js/Sidebar.Geometry.TorusGeometry.js +109 -0
  46. package/src/editor/js/Sidebar.Geometry.TorusKnotGeometry.js +121 -0
  47. package/src/editor/js/Sidebar.Geometry.TubeGeometry.js +135 -0
  48. package/src/editor/js/Sidebar.Geometry.js +471 -0
  49. package/src/editor/js/Sidebar.Material.BooleanProperty.js +60 -0
  50. package/src/editor/js/Sidebar.Material.ColorProperty.js +87 -0
  51. package/src/editor/js/Sidebar.Material.ConstantProperty.js +62 -0
  52. package/src/editor/js/Sidebar.Material.MapProperty.js +277 -0
  53. package/src/editor/js/Sidebar.Material.NumberProperty.js +60 -0
  54. package/src/editor/js/Sidebar.Material.Program.js +73 -0
  55. package/src/editor/js/Sidebar.Material.RangeValueProperty.js +63 -0
  56. package/src/editor/js/Sidebar.Material.js +751 -0
  57. package/src/editor/js/Sidebar.Object.js +892 -0
  58. package/src/editor/js/Sidebar.Project.App.js +218 -0
  59. package/src/editor/js/Sidebar.Project.Materials.js +82 -0
  60. package/src/editor/js/Sidebar.Project.Renderer.js +193 -0
  61. package/src/editor/js/Sidebar.Project.Resources.js +242 -0
  62. package/src/editor/js/Sidebar.Project.js +21 -0
  63. package/src/editor/js/Sidebar.Properties.js +73 -0
  64. package/src/editor/js/Sidebar.Scene.js +643 -0
  65. package/src/editor/js/Sidebar.Script.js +129 -0
  66. package/src/editor/js/Sidebar.Settings.History.js +146 -0
  67. package/src/editor/js/Sidebar.Settings.Shortcuts.js +198 -0
  68. package/src/editor/js/Sidebar.Settings.js +58 -0
  69. package/src/editor/js/Sidebar.js +41 -0
  70. package/src/editor/js/Storage.js +98 -0
  71. package/src/editor/js/Strings.js +2718 -0
  72. package/src/editor/js/TextureParametersDialog.js +293 -0
  73. package/src/editor/js/Toolbar.js +75 -0
  74. package/src/editor/js/Viewport.Controls.js +98 -0
  75. package/src/editor/js/Viewport.Info.js +144 -0
  76. package/src/editor/js/Viewport.Pathtracer.js +27 -0
  77. package/src/editor/js/Viewport.ViewHelper.js +41 -0
  78. package/src/editor/js/Viewport.XR.js +7 -0
  79. package/src/editor/js/Viewport.js +982 -0
  80. package/src/editor/js/commands/AddObjectCommand.js +68 -0
  81. package/src/editor/js/commands/AddScriptCommand.js +75 -0
  82. package/src/editor/js/commands/Commands.js +24 -0
  83. package/src/editor/js/commands/MoveObjectCommand.js +117 -0
  84. package/src/editor/js/commands/MultiCmdsCommand.js +85 -0
  85. package/src/editor/js/commands/RemoveObjectCommand.js +88 -0
  86. package/src/editor/js/commands/RemoveScriptCommand.js +81 -0
  87. package/src/editor/js/commands/SetColorCommand.js +73 -0
  88. package/src/editor/js/commands/SetGeometryCommand.js +86 -0
  89. package/src/editor/js/commands/SetGeometryValueCommand.js +70 -0
  90. package/src/editor/js/commands/SetMaterialColorCommand.js +87 -0
  91. package/src/editor/js/commands/SetMaterialCommand.js +80 -0
  92. package/src/editor/js/commands/SetMaterialMapCommand.js +144 -0
  93. package/src/editor/js/commands/SetMaterialRangeCommand.js +92 -0
  94. package/src/editor/js/commands/SetMaterialValueCommand.js +91 -0
  95. package/src/editor/js/commands/SetMaterialVectorCommand.js +88 -0
  96. package/src/editor/js/commands/SetPositionCommand.js +84 -0
  97. package/src/editor/js/commands/SetRotationCommand.js +84 -0
  98. package/src/editor/js/commands/SetScaleCommand.js +84 -0
  99. package/src/editor/js/commands/SetSceneCommand.js +104 -0
  100. package/src/editor/js/commands/SetScriptValueCommand.js +80 -0
  101. package/src/editor/js/commands/SetShadowValueCommand.js +73 -0
  102. package/src/editor/js/commands/SetTextureParametersCommand.js +143 -0
  103. package/src/editor/js/commands/SetUuidCommand.js +70 -0
  104. package/src/editor/js/commands/SetValueCommand.js +75 -0
  105. package/src/editor/js/libs/acorn/acorn.js +3236 -0
  106. package/src/editor/js/libs/acorn/acorn_loose.js +1299 -0
  107. package/src/editor/js/libs/acorn/walk.js +344 -0
  108. package/src/editor/js/libs/app/index.html +51 -0
  109. package/src/editor/js/libs/app.js +14 -0
  110. package/src/editor/js/libs/codemirror/addon/dialog.css +32 -0
  111. package/src/editor/js/libs/codemirror/addon/dialog.js +163 -0
  112. package/src/editor/js/libs/codemirror/addon/show-hint.css +36 -0
  113. package/src/editor/js/libs/codemirror/addon/show-hint.js +529 -0
  114. package/src/editor/js/libs/codemirror/addon/tern.css +87 -0
  115. package/src/editor/js/libs/codemirror/addon/tern.js +750 -0
  116. package/src/editor/js/libs/codemirror/codemirror.css +344 -0
  117. package/src/editor/js/libs/codemirror/codemirror.js +9849 -0
  118. package/src/editor/js/libs/codemirror/mode/glsl.js +233 -0
  119. package/src/editor/js/libs/codemirror/mode/javascript.js +959 -0
  120. package/src/editor/js/libs/codemirror/theme/monokai.css +41 -0
  121. package/src/editor/js/libs/esprima.js +6401 -0
  122. package/src/editor/js/libs/jsonlint.js +453 -0
  123. package/src/editor/js/libs/signals.min.js +14 -0
  124. package/src/editor/js/libs/tern-threejs/build-defs.js +233 -0
  125. package/src/editor/js/libs/ternjs/comment.js +87 -0
  126. package/src/editor/js/libs/ternjs/def.js +588 -0
  127. package/src/editor/js/libs/ternjs/doc_comment.js +401 -0
  128. package/src/editor/js/libs/ternjs/infer.js +1635 -0
  129. package/src/editor/js/libs/ternjs/polyfill.js +80 -0
  130. package/src/editor/js/libs/ternjs/signal.js +26 -0
  131. package/src/editor/js/libs/ternjs/tern.js +993 -0
  132. package/src/editor/js/libs/ui.js +1305 -0
  133. package/src/editor/js/libs/ui.three.js +946 -0
  134. package/src/events.js +273 -0
  135. package/src/index.js +284 -0
  136. package/src/presets/machines.js +239 -0
@@ -0,0 +1,1006 @@
1
+ import * as THREE from 'three';
2
+
3
+ import { Config } from './Config.js';
4
+ import { Loader } from './Loader.js';
5
+ import { History as _History } from './History.js';
6
+ import { Strings } from './Strings.js';
7
+ import { Storage as _Storage } from './Storage.js';
8
+ import { Selector } from './Selector.js';
9
+ import Signal from 'signals';
10
+
11
+ var _DEFAULT_CAMERA = new THREE.PerspectiveCamera( 50, 1, 0.001, 1e10 );
12
+ _DEFAULT_CAMERA.name = 'Camera';
13
+ _DEFAULT_CAMERA.position.set( 0, 5, 10 );
14
+ _DEFAULT_CAMERA.lookAt( new THREE.Vector3() );
15
+ const _ORTHOGRAPHIC_FRUSTUM_SIZE = 100;
16
+
17
+ function Editor() {
18
+
19
+ this.signals = {
20
+
21
+ // script
22
+
23
+ editScript: new Signal(),
24
+
25
+ // player
26
+
27
+ startPlayer: new Signal(),
28
+ stopPlayer: new Signal(),
29
+
30
+ // xr
31
+
32
+ enterXR: new Signal(),
33
+ offerXR: new Signal(),
34
+ leaveXR: new Signal(),
35
+
36
+ // notifications
37
+
38
+ editorCleared: new Signal(),
39
+
40
+ savingStarted: new Signal(),
41
+ savingFinished: new Signal(),
42
+
43
+ transformModeChanged: new Signal(),
44
+ snapChanged: new Signal(),
45
+ spaceChanged: new Signal(),
46
+ rendererCreated: new Signal(),
47
+ rendererUpdated: new Signal(),
48
+ rendererDetectKTX2Support: new Signal(),
49
+
50
+ sceneBackgroundChanged: new Signal(),
51
+ sceneEnvironmentChanged: new Signal(),
52
+ sceneFogChanged: new Signal(),
53
+ sceneFogSettingsChanged: new Signal(),
54
+ sceneGraphChanged: new Signal(),
55
+ sceneRendered: new Signal(),
56
+
57
+ cameraChanged: new Signal(),
58
+ cameraResetted: new Signal(),
59
+
60
+ geometryChanged: new Signal(),
61
+
62
+ objectSelected: new Signal(),
63
+ objectMultiSelected: new Signal(),
64
+ objectFocused: new Signal(),
65
+
66
+ objectAdded: new Signal(),
67
+ objectChanged: new Signal(),
68
+ objectRemoved: new Signal(),
69
+
70
+ cameraAdded: new Signal(),
71
+ cameraRemoved: new Signal(),
72
+
73
+ helperAdded: new Signal(),
74
+ helperRemoved: new Signal(),
75
+
76
+ materialAdded: new Signal(),
77
+ materialChanged: new Signal(),
78
+ materialRemoved: new Signal(),
79
+
80
+ scriptAdded: new Signal(),
81
+ scriptChanged: new Signal(),
82
+ scriptRemoved: new Signal(),
83
+
84
+ windowResize: new Signal(),
85
+
86
+ showHelpersChanged: new Signal(),
87
+ refreshSidebarObject3D: new Signal(),
88
+ historyChanged: new Signal(),
89
+
90
+ viewportCameraChanged: new Signal(),
91
+ viewportShadingChanged: new Signal(),
92
+
93
+ intersectionsDetected: new Signal(),
94
+
95
+ pathTracerUpdated: new Signal(),
96
+
97
+ animationPanelChanged: new Signal(),
98
+ animationPanelResized: new Signal(),
99
+
100
+ morphTargetsUpdated: new Signal()
101
+
102
+ };
103
+
104
+ this.config = new Config();
105
+ this.history = new _History( this );
106
+ this.selector = new Selector( this );
107
+ this.storage = new _Storage();
108
+ this.strings = new Strings( this.config );
109
+
110
+ this.loader = new Loader( this );
111
+
112
+ this.camera = _DEFAULT_CAMERA.clone();
113
+
114
+ this.scene = new THREE.Scene();
115
+ this.scene.name = 'Scene';
116
+
117
+ this.sceneHelpers = new THREE.Scene();
118
+ this.sceneHelpers.add( new THREE.HemisphereLight( 0xffffff, 0x888888, 2 ) );
119
+
120
+ this.backgroundType = 'Default';
121
+ this.environmentType = 'Default';
122
+
123
+ this.object = {};
124
+ this.geometries = {};
125
+ this.materials = {};
126
+ this.textures = {};
127
+ this.scripts = {};
128
+
129
+ this.materialsRefCounter = new Map(); // tracks how often is a material used by a 3D object
130
+
131
+ this.mixer = new THREE.AnimationMixer( this.scene );
132
+
133
+ this.selected = null;
134
+ this.selectedObjects = []; // multi-selection (Spline-like); [0] is primary == this.selected
135
+ this.helpers = {};
136
+
137
+ this.cameras = {};
138
+
139
+ this.viewportCamera = this.camera;
140
+ this.viewportShading = 'default';
141
+ this.viewportColor = new THREE.Color();
142
+
143
+ this.addCamera( this.camera );
144
+
145
+ }
146
+
147
+ Editor.prototype = {
148
+
149
+ setScene: function ( scene ) {
150
+
151
+ this.scene.uuid = scene.uuid;
152
+ this.scene.name = scene.name;
153
+
154
+ this.scene.background = scene.background;
155
+ this.scene.environment = scene.environment;
156
+ this.scene.fog = scene.fog;
157
+ this.scene.backgroundBlurriness = scene.backgroundBlurriness;
158
+ this.scene.backgroundIntensity = scene.backgroundIntensity;
159
+
160
+ this.scene.userData = JSON.parse( JSON.stringify( scene.userData ) );
161
+
162
+ // avoid render per object
163
+
164
+ this.signals.sceneGraphChanged.active = false;
165
+
166
+ while ( scene.children.length > 0 ) {
167
+
168
+ this.addObject( scene.children[ 0 ] );
169
+
170
+ }
171
+
172
+ this.signals.sceneGraphChanged.active = true;
173
+ this.signals.sceneGraphChanged.dispatch();
174
+
175
+ this.signals.sceneEnvironmentChanged.dispatch( this.environmentType, scene.environment );
176
+
177
+ },
178
+
179
+ //
180
+
181
+ addObject: function ( object, parent, index ) {
182
+
183
+ var scope = this;
184
+
185
+ object.traverse( function ( child ) {
186
+
187
+ if ( child.geometry !== undefined ) scope.addGeometry( child.geometry );
188
+ if ( child.material !== undefined ) scope.addMaterial( child.material );
189
+
190
+ scope.addCamera( child );
191
+ scope.addHelper( child );
192
+
193
+ } );
194
+
195
+ if ( parent === undefined ) {
196
+
197
+ this.scene.add( object );
198
+
199
+ } else {
200
+
201
+ parent.children.splice( index, 0, object );
202
+ object.parent = parent;
203
+
204
+ }
205
+
206
+ this.signals.objectAdded.dispatch( object );
207
+ this.signals.sceneGraphChanged.dispatch();
208
+
209
+ },
210
+
211
+ nameObject: function ( object, name ) {
212
+
213
+ object.name = name;
214
+ this.signals.sceneGraphChanged.dispatch();
215
+
216
+ },
217
+
218
+ removeObject: function ( object ) {
219
+
220
+ if ( object.parent === null ) return; // avoid deleting the camera or scene
221
+
222
+ var scope = this;
223
+
224
+ object.traverse( function ( child ) {
225
+
226
+ scope.removeCamera( child );
227
+ scope.removeHelper( child );
228
+
229
+ if ( child.material !== undefined ) scope.removeMaterial( child.material );
230
+
231
+ } );
232
+
233
+ object.parent.remove( object );
234
+
235
+ this.signals.objectRemoved.dispatch( object );
236
+ this.signals.sceneGraphChanged.dispatch();
237
+
238
+ },
239
+
240
+ addGeometry: function ( geometry ) {
241
+
242
+ this.geometries[ geometry.uuid ] = geometry;
243
+
244
+ },
245
+
246
+ setGeometryName: function ( geometry, name ) {
247
+
248
+ geometry.name = name;
249
+ this.signals.sceneGraphChanged.dispatch();
250
+
251
+ },
252
+
253
+ addMaterial: function ( material ) {
254
+
255
+ if ( Array.isArray( material ) ) {
256
+
257
+ for ( var i = 0, l = material.length; i < l; i ++ ) {
258
+
259
+ this.addMaterialToRefCounter( material[ i ] );
260
+
261
+ }
262
+
263
+ } else {
264
+
265
+ this.addMaterialToRefCounter( material );
266
+
267
+ }
268
+
269
+ this.signals.materialAdded.dispatch();
270
+
271
+ },
272
+
273
+ addMaterialToRefCounter: function ( material ) {
274
+
275
+ var materialsRefCounter = this.materialsRefCounter;
276
+
277
+ var count = materialsRefCounter.get( material );
278
+
279
+ if ( count === undefined ) {
280
+
281
+ materialsRefCounter.set( material, 1 );
282
+ this.materials[ material.uuid ] = material;
283
+
284
+ } else {
285
+
286
+ count ++;
287
+ materialsRefCounter.set( material, count );
288
+
289
+ }
290
+
291
+ },
292
+
293
+ removeMaterial: function ( material ) {
294
+
295
+ if ( Array.isArray( material ) ) {
296
+
297
+ for ( var i = 0, l = material.length; i < l; i ++ ) {
298
+
299
+ this.removeMaterialFromRefCounter( material[ i ] );
300
+
301
+ }
302
+
303
+ } else {
304
+
305
+ this.removeMaterialFromRefCounter( material );
306
+
307
+ }
308
+
309
+ this.signals.materialRemoved.dispatch();
310
+
311
+ },
312
+
313
+ removeMaterialFromRefCounter: function ( material ) {
314
+
315
+ var materialsRefCounter = this.materialsRefCounter;
316
+
317
+ var count = materialsRefCounter.get( material );
318
+ count --;
319
+
320
+ if ( count === 0 ) {
321
+
322
+ materialsRefCounter.delete( material );
323
+ delete this.materials[ material.uuid ];
324
+
325
+ } else {
326
+
327
+ materialsRefCounter.set( material, count );
328
+
329
+ }
330
+
331
+ },
332
+
333
+ getMaterialById: function ( id ) {
334
+
335
+ var material;
336
+ var materials = Object.values( this.materials );
337
+
338
+ for ( var i = 0; i < materials.length; i ++ ) {
339
+
340
+ if ( materials[ i ].id === id ) {
341
+
342
+ material = materials[ i ];
343
+ break;
344
+
345
+ }
346
+
347
+ }
348
+
349
+ return material;
350
+
351
+ },
352
+
353
+ setMaterialName: function ( material, name ) {
354
+
355
+ material.name = name;
356
+ this.signals.sceneGraphChanged.dispatch();
357
+
358
+ },
359
+
360
+ addTexture: function ( texture ) {
361
+
362
+ this.textures[ texture.uuid ] = texture;
363
+
364
+ },
365
+
366
+ //
367
+
368
+ addCamera: function ( camera ) {
369
+
370
+ if ( camera.isCamera ) {
371
+
372
+ this.cameras[ camera.uuid ] = camera;
373
+
374
+ this.signals.cameraAdded.dispatch( camera );
375
+
376
+ }
377
+
378
+ },
379
+
380
+ removeCamera: function ( camera ) {
381
+
382
+ if ( this.cameras[ camera.uuid ] !== undefined ) {
383
+
384
+ delete this.cameras[ camera.uuid ];
385
+
386
+ this.signals.cameraRemoved.dispatch( camera );
387
+
388
+ }
389
+
390
+ },
391
+
392
+ //
393
+
394
+ addHelper: function () {
395
+
396
+ var geometry = new THREE.SphereGeometry( 2, 4, 2 );
397
+ var material = new THREE.MeshBasicMaterial( { color: 0xff0000, visible: false } );
398
+
399
+ return function ( object, helper ) {
400
+
401
+ if ( helper === undefined ) {
402
+
403
+ if ( object.isCamera ) {
404
+
405
+ helper = new THREE.CameraHelper( object );
406
+
407
+ } else if ( object.isPointLight ) {
408
+
409
+ helper = new THREE.PointLightHelper( object, 1 );
410
+
411
+ helper.matrix = new THREE.Matrix4();
412
+ helper.matrixAutoUpdate = true;
413
+
414
+ const light = object;
415
+ const editor = this;
416
+
417
+ helper.updateMatrixWorld = function () {
418
+
419
+ light.getWorldPosition( this.position );
420
+
421
+ const distance = editor.viewportCamera.position.distanceTo( this.position );
422
+ this.scale.setScalar( distance / 30 );
423
+
424
+ this.updateMatrix();
425
+ this.matrixWorld.copy( this.matrix );
426
+
427
+ const children = this.children;
428
+
429
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
430
+
431
+ children[ i ].updateMatrixWorld();
432
+
433
+ }
434
+
435
+ };
436
+
437
+ } else if ( object.isDirectionalLight ) {
438
+
439
+ helper = new THREE.DirectionalLightHelper( object, 1 );
440
+
441
+ } else if ( object.isSpotLight ) {
442
+
443
+ helper = new THREE.SpotLightHelper( object );
444
+
445
+ } else if ( object.isHemisphereLight ) {
446
+
447
+ helper = new THREE.HemisphereLightHelper( object, 1 );
448
+
449
+ } else if ( object.isSkinnedMesh ) {
450
+
451
+ helper = new THREE.SkeletonHelper( object.skeleton.bones[ 0 ] );
452
+
453
+ } else if ( object.isBone === true && object.parent && object.parent.isBone !== true ) {
454
+
455
+ helper = new THREE.SkeletonHelper( object );
456
+
457
+ } else {
458
+
459
+ // no helper for this object type
460
+ return;
461
+
462
+ }
463
+
464
+ const picker = new THREE.Mesh( geometry, material );
465
+ picker.name = 'picker';
466
+ picker.userData.object = object;
467
+ helper.add( picker );
468
+
469
+ }
470
+
471
+ this.sceneHelpers.add( helper );
472
+ this.helpers[ object.id ] = helper;
473
+
474
+ this.signals.helperAdded.dispatch( helper );
475
+
476
+ };
477
+
478
+ }(),
479
+
480
+ removeHelper: function ( object ) {
481
+
482
+ if ( this.helpers[ object.id ] !== undefined ) {
483
+
484
+ var helper = this.helpers[ object.id ];
485
+ helper.parent.remove( helper );
486
+ helper.dispose();
487
+
488
+ delete this.helpers[ object.id ];
489
+
490
+ this.signals.helperRemoved.dispatch( helper );
491
+
492
+ }
493
+
494
+ },
495
+
496
+ //
497
+
498
+ addScript: function ( object, script ) {
499
+
500
+ if ( this.scripts[ object.uuid ] === undefined ) {
501
+
502
+ this.scripts[ object.uuid ] = [];
503
+
504
+ }
505
+
506
+ this.scripts[ object.uuid ].push( script );
507
+
508
+ this.signals.scriptAdded.dispatch( script );
509
+
510
+ },
511
+
512
+ removeScript: function ( object, script ) {
513
+
514
+ if ( this.scripts[ object.uuid ] === undefined ) return;
515
+
516
+ var index = this.scripts[ object.uuid ].indexOf( script );
517
+
518
+ if ( index !== - 1 ) {
519
+
520
+ this.scripts[ object.uuid ].splice( index, 1 );
521
+
522
+ }
523
+
524
+ this.signals.scriptRemoved.dispatch( script );
525
+
526
+ },
527
+
528
+ getObjectMaterial: function ( object, slot ) {
529
+
530
+ var material = object.material;
531
+
532
+ if ( Array.isArray( material ) && slot !== undefined ) {
533
+
534
+ material = material[ slot ];
535
+
536
+ }
537
+
538
+ return material;
539
+
540
+ },
541
+
542
+ setObjectMaterial: function ( object, slot, newMaterial ) {
543
+
544
+ if ( Array.isArray( object.material ) && slot !== undefined ) {
545
+
546
+ object.material[ slot ] = newMaterial;
547
+
548
+ } else {
549
+
550
+ object.material = newMaterial;
551
+
552
+ }
553
+
554
+ },
555
+
556
+ setCameraType: function ( type ) {
557
+
558
+ const oldCamera = this.camera;
559
+
560
+ const isOrthographic = oldCamera.isOrthographicCamera === true;
561
+
562
+ if ( ( type === 'orthographic' && isOrthographic ) || ( type === 'perspective' && ! isOrthographic ) ) return;
563
+
564
+ // the orbit point the framing should be preserved around
565
+
566
+ const center = this.controls ? this.controls.center : new THREE.Vector3();
567
+ const distance = oldCamera.position.distanceTo( center );
568
+
569
+ let newCamera;
570
+
571
+ if ( type === 'orthographic' ) {
572
+
573
+ const halfSize = _ORTHOGRAPHIC_FRUSTUM_SIZE / 2;
574
+ newCamera = new THREE.OrthographicCamera( - halfSize, halfSize, halfSize, - halfSize, 0, 10000 );
575
+ newCamera.position.copy( oldCamera.position );
576
+ newCamera.quaternion.copy( oldCamera.quaternion );
577
+
578
+ // derive the zoom so the orthographic framing matches the perspective view at the orbit center
579
+
580
+ const halfFOV = THREE.MathUtils.DEG2RAD * oldCamera.fov / 2;
581
+ newCamera.zoom = ( newCamera.top - newCamera.bottom ) / ( 2 * Math.max( distance, 0.0001 ) * Math.tan( halfFOV ) );
582
+
583
+ } else {
584
+
585
+ newCamera = new THREE.PerspectiveCamera( 50, 1, 0.001, 1e10 );
586
+ newCamera.quaternion.copy( oldCamera.quaternion );
587
+
588
+ // reposition along the view direction so the perspective framing matches the orthographic view
589
+
590
+ const halfFOV = THREE.MathUtils.DEG2RAD * newCamera.fov / 2;
591
+ const targetDistance = ( oldCamera.top - oldCamera.bottom ) / ( 2 * oldCamera.zoom * Math.tan( halfFOV ) );
592
+
593
+ const offset = new THREE.Vector3().subVectors( oldCamera.position, center );
594
+ if ( offset.lengthSq() === 0 ) offset.set( 0, 0, 1 ).applyQuaternion( oldCamera.quaternion );
595
+ offset.normalize().multiplyScalar( targetDistance );
596
+
597
+ newCamera.position.copy( center ).add( offset );
598
+
599
+ }
600
+
601
+ newCamera.name = oldCamera.name;
602
+ newCamera.uuid = oldCamera.uuid;
603
+ newCamera.updateProjectionMatrix();
604
+
605
+ this.camera = newCamera;
606
+ this.cameras[ newCamera.uuid ] = newCamera;
607
+
608
+ if ( this.viewportCamera === oldCamera ) this.viewportCamera = newCamera;
609
+
610
+ this.signals.cameraResetted.dispatch();
611
+
612
+ // keep the selection (and thus the sidebar) in sync with the new camera instance
613
+
614
+ if ( this.selected === oldCamera ) this.select( newCamera );
615
+
616
+ },
617
+
618
+ setViewportCamera: function ( uuid ) {
619
+
620
+ this.viewportCamera = this.cameras[ uuid ] || this.camera;
621
+ this.signals.viewportCameraChanged.dispatch();
622
+
623
+ },
624
+
625
+ setViewportShading: function ( value ) {
626
+
627
+ this.viewportShading = value;
628
+ this.signals.viewportShadingChanged.dispatch();
629
+
630
+ },
631
+
632
+ //
633
+
634
+ selectMultiple: function ( objects ) {
635
+
636
+ // Replace multi-selection. objects[0] becomes the primary `this.selected`.
637
+ this.selectedObjects = Array.isArray( objects ) ? objects.slice() : ( objects ? [ objects ] : [] );
638
+ this.selected = this.selectedObjects[ 0 ] || null;
639
+
640
+ const uuid = this.selected ? this.selected.uuid : null;
641
+ this.config.setKey( 'selected', uuid );
642
+
643
+ this.signals.objectSelected.dispatch( this.selected );
644
+ this.signals.objectMultiSelected.dispatch( this.selectedObjects );
645
+
646
+ },
647
+
648
+ addToSelection: function ( object ) {
649
+
650
+ if ( object === null ) { this.selectMultiple( [] ); return; }
651
+ const idx = this.selectedObjects.indexOf( object );
652
+ if ( idx === - 1 ) this.selectedObjects.push( object );
653
+ // promote to primary without dropping the rest
654
+ this.selected = object;
655
+ this.config.setKey( 'selected', object.uuid );
656
+ this.signals.objectSelected.dispatch( object );
657
+ this.signals.objectMultiSelected.dispatch( this.selectedObjects );
658
+
659
+ },
660
+
661
+ toggleInSelection: function ( object ) {
662
+
663
+ if ( object === null ) { this.selectMultiple( [] ); return; }
664
+ const idx = this.selectedObjects.indexOf( object );
665
+ if ( idx === - 1 ) this.selectedObjects.push( object );
666
+ else this.selectedObjects.splice( idx, 1 );
667
+ this.selected = this.selectedObjects[ this.selectedObjects.length - 1 ] || null;
668
+ this.config.setKey( 'selected', this.selected ? this.selected.uuid : null );
669
+ this.signals.objectSelected.dispatch( this.selected );
670
+ this.signals.objectMultiSelected.dispatch( this.selectedObjects );
671
+
672
+ },
673
+
674
+ getSelection: function () {
675
+
676
+ return this.selectedObjects.slice();
677
+
678
+ },
679
+
680
+ mergeSelectionIntoGroup: function ( name ) {
681
+
682
+ const objs = this.getSelection();
683
+ if ( objs.length < 2 ) return null;
684
+
685
+ // Determine a common parent (most frequent parent among selected)
686
+ const counts = new Map();
687
+ for ( const o of objs ) {
688
+
689
+ const p = o.parent;
690
+ counts.set( p, ( counts.get( p ) || 0 ) + 1 );
691
+
692
+ }
693
+ let commonParent = objs[ 0 ].parent, max = 0;
694
+ for ( const [ p, n ] of counts ) if ( n > max ) { max = n; commonParent = p; }
695
+
696
+ const group = new THREE.Group();
697
+ group.name = name || 'Group';
698
+
699
+ let box = null;
700
+ objs.forEach( o => box = ( box ? box.expandByObject( o ) : new THREE.Box3().setFromObject( o ) ) );
701
+ if ( box ) {
702
+
703
+ const c = new THREE.Vector3();
704
+ box.getCenter( c );
705
+ group.position.copy( c );
706
+
707
+ }
708
+
709
+ commonParent.add( group );
710
+ group.userData = { type: 'group', merged: true };
711
+
712
+ // Reparent each selected object into the group, preserving world transform
713
+ objs.forEach( o => {
714
+
715
+ if ( o.parent === commonParent || o.parent !== group ) o.parent.remove( o );
716
+ group.attach( o ); // attach keeps world transform
717
+
718
+ } );
719
+
720
+ this.signals.objectAdded.dispatch( group );
721
+ this.signals.sceneGraphChanged.dispatch();
722
+ this.selectMultiple( [ group ] );
723
+ return group;
724
+
725
+ },
726
+
727
+ ungroupBySelection: function () {
728
+
729
+ const groups = this.getSelection().filter( o => o.isGroup );
730
+ const moved = [];
731
+ groups.forEach( g => {
732
+
733
+ const children = g.children.slice();
734
+ children.forEach( c => {
735
+
736
+ if ( c === g ) return;
737
+ g.parent.attach( c ); // preserve world transform
738
+ moved.push( c );
739
+
740
+ } );
741
+ if ( g.parent ) g.parent.remove( g );
742
+
743
+ } );
744
+ if ( moved.length ) {
745
+
746
+ this.signals.sceneGraphChanged.dispatch();
747
+ this.selectMultiple( moved );
748
+
749
+ }
750
+ return moved;
751
+
752
+ },
753
+
754
+ select: function ( object ) {
755
+
756
+ this.selector.select( object );
757
+
758
+ },
759
+
760
+ selectById: function ( id ) {
761
+
762
+ if ( id === this.camera.id ) {
763
+
764
+ this.select( this.camera );
765
+ return;
766
+
767
+ }
768
+
769
+ this.select( this.scene.getObjectById( id ) );
770
+
771
+ },
772
+
773
+ selectByUuid: function ( uuid ) {
774
+
775
+ var scope = this;
776
+
777
+ this.scene.traverse( function ( child ) {
778
+
779
+ if ( child.uuid === uuid ) {
780
+
781
+ scope.select( child );
782
+
783
+ }
784
+
785
+ } );
786
+
787
+ },
788
+
789
+ deselect: function () {
790
+
791
+ this.selector.deselect();
792
+
793
+ },
794
+
795
+ focus: function ( object ) {
796
+
797
+ if ( object !== undefined ) {
798
+
799
+ this.signals.objectFocused.dispatch( object );
800
+
801
+ }
802
+
803
+ },
804
+
805
+ focusById: function ( id ) {
806
+
807
+ this.focus( this.scene.getObjectById( id ) );
808
+
809
+ },
810
+
811
+ clear: function () {
812
+
813
+ this.history.clear();
814
+ this.storage.clear();
815
+
816
+ this.setCameraType( 'perspective' );
817
+ this.camera.copy( _DEFAULT_CAMERA );
818
+ this.signals.cameraResetted.dispatch();
819
+
820
+ this.scene.name = 'Scene';
821
+ this.scene.userData = {};
822
+ this.scene.background = null;
823
+ this.scene.environment = null;
824
+ this.scene.fog = null;
825
+
826
+ var objects = this.scene.children;
827
+
828
+ this.signals.sceneGraphChanged.active = false;
829
+
830
+ while ( objects.length > 0 ) {
831
+
832
+ this.removeObject( objects[ 0 ] );
833
+
834
+ }
835
+
836
+ this.signals.sceneGraphChanged.active = true;
837
+
838
+ this.geometries = {};
839
+ this.materials = {};
840
+ this.textures = {};
841
+ this.scripts = {};
842
+
843
+ this.materialsRefCounter.clear();
844
+
845
+ this.animations = {};
846
+ this.mixer.stopAllAction();
847
+
848
+ this.deselect();
849
+
850
+ this.backgroundType = 'Default';
851
+ this.environmentType = 'Default';
852
+
853
+ this.signals.editorCleared.dispatch();
854
+
855
+ },
856
+
857
+ //
858
+
859
+ fromJSON: async function ( json ) {
860
+
861
+ var loader = new THREE.ObjectLoader();
862
+ var camera = await loader.parseAsync( json.camera );
863
+
864
+ this.setCameraType( camera.isOrthographicCamera ? 'orthographic' : 'perspective' );
865
+
866
+ const existingUuid = this.camera.uuid;
867
+ const incomingUuid = camera.uuid;
868
+
869
+ // copy all properties, including uuid
870
+ this.camera.copy( camera );
871
+ this.camera.uuid = incomingUuid;
872
+
873
+ delete this.cameras[ existingUuid ]; // remove old entry [existingUuid, this.camera]
874
+ this.cameras[ incomingUuid ] = this.camera; // add new entry [incomingUuid, this.camera]
875
+
876
+ if ( json.controls !== undefined ) {
877
+
878
+ this.controls.fromJSON( json.controls );
879
+
880
+ }
881
+
882
+ this.signals.cameraResetted.dispatch();
883
+
884
+ this.history.fromJSON( json.history );
885
+ this.scripts = json.scripts;
886
+
887
+ const scene = await loader.parseAsync( json.scene );
888
+
889
+ this.backgroundType = json.backgroundType || 'Default';
890
+ this.environmentType = json.environmentType || 'Default';
891
+
892
+ this.setScene( scene );
893
+
894
+ },
895
+
896
+ toJSON: function () {
897
+
898
+ // scripts clean up
899
+
900
+ var scene = this.scene;
901
+ var scripts = this.scripts;
902
+
903
+ for ( var key in scripts ) {
904
+
905
+ var script = scripts[ key ];
906
+
907
+ if ( script.length === 0 || scene.getObjectByProperty( 'uuid', key ) === undefined ) {
908
+
909
+ delete scripts[ key ];
910
+
911
+ }
912
+
913
+ }
914
+
915
+ return {
916
+
917
+ metadata: {},
918
+ project: {
919
+ renderer: this.config.getKey( 'project/renderer/type' ),
920
+ shadows: this.config.getKey( 'project/renderer/shadows' ),
921
+ shadowType: this.config.getKey( 'project/renderer/shadowType' ),
922
+ toneMapping: this.config.getKey( 'project/renderer/toneMapping' ),
923
+ toneMappingExposure: this.config.getKey( 'project/renderer/toneMappingExposure' )
924
+ },
925
+ camera: this.viewportCamera.toJSON(),
926
+ controls: this.controls.toJSON(),
927
+ scene: this.scene.toJSON(),
928
+ scripts: this.scripts,
929
+ history: this.history.toJSON(),
930
+ backgroundType: this.backgroundType,
931
+ environmentType: this.environmentType
932
+
933
+ };
934
+
935
+ },
936
+
937
+ objectByUuid: function ( uuid ) {
938
+
939
+ return this.scene.getObjectByProperty( 'uuid', uuid, true );
940
+
941
+ },
942
+
943
+ execute: function ( cmd, optionalName ) {
944
+
945
+ this.history.execute( cmd, optionalName );
946
+
947
+ },
948
+
949
+ undo: function () {
950
+
951
+ this.history.undo();
952
+
953
+ },
954
+
955
+ redo: function () {
956
+
957
+ this.history.redo();
958
+
959
+ },
960
+
961
+ utils: {
962
+
963
+ save: save,
964
+ saveArrayBuffer: saveArrayBuffer,
965
+ saveString: saveString,
966
+ formatNumber: formatNumber
967
+
968
+ }
969
+
970
+ };
971
+
972
+ const link = document.createElement( 'a' );
973
+
974
+ function save( blob, filename ) {
975
+
976
+ if ( link.href ) {
977
+
978
+ URL.revokeObjectURL( link.href );
979
+
980
+ }
981
+
982
+ link.href = URL.createObjectURL( blob );
983
+ link.download = filename || 'data.json';
984
+ link.dispatchEvent( new MouseEvent( 'click' ) );
985
+
986
+ }
987
+
988
+ function saveArrayBuffer( buffer, filename ) {
989
+
990
+ save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
991
+
992
+ }
993
+
994
+ function saveString( text, filename ) {
995
+
996
+ save( new Blob( [ text ], { type: 'text/plain' } ), filename );
997
+
998
+ }
999
+
1000
+ function formatNumber( number ) {
1001
+
1002
+ return new Intl.NumberFormat( 'en-us', { useGrouping: true } ).format( number );
1003
+
1004
+ }
1005
+
1006
+ export { Editor };