@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,585 @@
1
+ import { UIPanel, UIRow, UIHorizontalRule } from './libs/ui.js';
2
+ import { FileLoader, PropertyBinding } from 'three';
3
+
4
+ function MenubarFile( editor ) {
5
+
6
+ const strings = editor.strings;
7
+
8
+ const saveArrayBuffer = editor.utils.saveArrayBuffer;
9
+ const saveString = editor.utils.saveString;
10
+
11
+ const container = new UIPanel();
12
+ container.setClass( 'menu' );
13
+
14
+ const title = new UIPanel();
15
+ title.setClass( 'title' );
16
+ title.setTextContent( strings.getKey( 'menubar/file' ) );
17
+ container.add( title );
18
+
19
+ const options = new UIPanel();
20
+ options.setClass( 'options' );
21
+ container.add( options );
22
+
23
+ // New Project
24
+
25
+ const newProjectSubmenuTitle = new UIRow().setTextContent( strings.getKey( 'menubar/file/new' ) ).addClass( 'option' ).addClass( 'submenu-title' );
26
+ newProjectSubmenuTitle.onMouseOver( function () {
27
+
28
+ const { top, right } = this.dom.getBoundingClientRect();
29
+ const { paddingTop } = getComputedStyle( this.dom );
30
+ newProjectSubmenu.setLeft( right + 'px' );
31
+ newProjectSubmenu.setTop( top - parseFloat( paddingTop ) + 'px' );
32
+ newProjectSubmenu.setDisplay( 'block' );
33
+
34
+ } );
35
+ newProjectSubmenuTitle.onMouseOut( function () {
36
+
37
+ newProjectSubmenu.setDisplay( 'none' );
38
+
39
+ } );
40
+ options.add( newProjectSubmenuTitle );
41
+
42
+ const newProjectSubmenu = new UIPanel().setPosition( 'fixed' ).addClass( 'options' ).setDisplay( 'none' );
43
+ newProjectSubmenuTitle.add( newProjectSubmenu );
44
+
45
+ // New Project / Empty
46
+
47
+ let option = new UIRow().setTextContent( strings.getKey( 'menubar/file/new/empty' ) ).setClass( 'option' );
48
+ option.onClick( function () {
49
+
50
+ if ( confirm( strings.getKey( 'prompt/file/open' ) ) ) {
51
+
52
+ editor.clear();
53
+
54
+ }
55
+
56
+ } );
57
+ newProjectSubmenu.add( option );
58
+
59
+ //
60
+
61
+ newProjectSubmenu.add( new UIHorizontalRule() );
62
+
63
+ // New Project / ...
64
+
65
+ const examples = [
66
+ { title: 'menubar/file/new/Arkanoid', file: 'arkanoid.app.json' },
67
+ { title: 'menubar/file/new/Camera', file: 'camera.app.json' },
68
+ { title: 'menubar/file/new/Particles', file: 'particles.app.json' },
69
+ { title: 'menubar/file/new/Pong', file: 'pong.app.json' },
70
+ { title: 'menubar/file/new/Shaders', file: 'shaders.app.json' }
71
+ ];
72
+
73
+ const loader = new FileLoader();
74
+
75
+ for ( let i = 0; i < examples.length; i ++ ) {
76
+
77
+ ( function ( i ) {
78
+
79
+ const example = examples[ i ];
80
+
81
+ const option = new UIRow();
82
+ option.setClass( 'option' );
83
+ option.setTextContent( strings.getKey( example.title ) );
84
+ option.onClick( function () {
85
+
86
+ if ( confirm( strings.getKey( 'prompt/file/open' ) ) ) {
87
+
88
+ loader.load( 'examples/' + example.file, function ( text ) {
89
+
90
+ editor.clear();
91
+ editor.fromJSON( JSON.parse( text ) );
92
+
93
+ } );
94
+
95
+ }
96
+
97
+ } );
98
+ newProjectSubmenu.add( option );
99
+
100
+ } )( i );
101
+
102
+ }
103
+
104
+ // Open
105
+
106
+ const openProjectForm = document.createElement( 'form' );
107
+ openProjectForm.style.display = 'none';
108
+ document.body.appendChild( openProjectForm );
109
+
110
+ const openProjectInput = document.createElement( 'input' );
111
+ openProjectInput.multiple = false;
112
+ openProjectInput.type = 'file';
113
+ openProjectInput.accept = '.json';
114
+ openProjectInput.addEventListener( 'change', async function () {
115
+
116
+ const file = openProjectInput.files[ 0 ];
117
+
118
+ if ( file === undefined ) return;
119
+
120
+ try {
121
+
122
+ const json = JSON.parse( await file.text() );
123
+
124
+ async function onEditorCleared() {
125
+
126
+ await editor.fromJSON( json );
127
+
128
+ editor.signals.editorCleared.remove( onEditorCleared );
129
+
130
+ }
131
+
132
+ editor.signals.editorCleared.add( onEditorCleared );
133
+
134
+ editor.clear();
135
+
136
+ } catch ( e ) {
137
+
138
+ alert( strings.getKey( 'prompt/file/failedToOpenProject' ) );
139
+ console.error( e );
140
+
141
+ } finally {
142
+
143
+ form.reset();
144
+
145
+ }
146
+
147
+ } );
148
+
149
+ openProjectForm.appendChild( openProjectInput );
150
+
151
+ option = new UIRow()
152
+ .addClass( 'option' )
153
+ .setTextContent( strings.getKey( 'menubar/file/open' ) )
154
+ .onClick( function () {
155
+
156
+ if ( confirm( strings.getKey( 'prompt/file/open' ) ) ) {
157
+
158
+ openProjectInput.click();
159
+
160
+ }
161
+
162
+ } );
163
+
164
+ options.add( option );
165
+
166
+ // Save
167
+
168
+ option = new UIRow()
169
+ .addClass( 'option' )
170
+ .setTextContent( strings.getKey( 'menubar/file/save' ) )
171
+ .onClick( function () {
172
+
173
+ const json = editor.toJSON();
174
+ const blob = new Blob( [ JSON.stringify( json ) ], { type: 'application/json' } );
175
+ editor.utils.save( blob, 'project.json' );
176
+
177
+ } );
178
+
179
+ options.add( option );
180
+
181
+ //
182
+
183
+ options.add( new UIHorizontalRule() );
184
+
185
+ // Import
186
+
187
+ const form = document.createElement( 'form' );
188
+ form.style.display = 'none';
189
+ document.body.appendChild( form );
190
+
191
+ const fileInput = document.createElement( 'input' );
192
+ fileInput.multiple = true;
193
+ fileInput.type = 'file';
194
+ fileInput.addEventListener( 'change', function () {
195
+
196
+ editor.loader.loadFiles( fileInput.files );
197
+ form.reset();
198
+
199
+ } );
200
+ form.appendChild( fileInput );
201
+
202
+ option = new UIRow();
203
+ option.setClass( 'option' );
204
+ option.setTextContent( strings.getKey( 'menubar/file/import' ) );
205
+ option.onClick( function () {
206
+
207
+ fileInput.click();
208
+
209
+ } );
210
+ options.add( option );
211
+
212
+ // Export
213
+
214
+ const fileExportSubmenuTitle = new UIRow().setTextContent( strings.getKey( 'menubar/file/export' ) ).addClass( 'option' ).addClass( 'submenu-title' );
215
+ fileExportSubmenuTitle.onMouseOver( function () {
216
+
217
+ const { top, right } = this.dom.getBoundingClientRect();
218
+ const { paddingTop } = getComputedStyle( this.dom );
219
+ fileExportSubmenu.setLeft( right + 'px' );
220
+ fileExportSubmenu.setTop( top - parseFloat( paddingTop ) + 'px' );
221
+ fileExportSubmenu.setDisplay( 'block' );
222
+
223
+ } );
224
+ fileExportSubmenuTitle.onMouseOut( function () {
225
+
226
+ fileExportSubmenu.setDisplay( 'none' );
227
+
228
+ } );
229
+ options.add( fileExportSubmenuTitle );
230
+
231
+ const fileExportSubmenu = new UIPanel().setPosition( 'fixed' ).addClass( 'options' ).setDisplay( 'none' );
232
+ fileExportSubmenuTitle.add( fileExportSubmenu );
233
+
234
+ // Export DRC
235
+
236
+ option = new UIRow();
237
+ option.setClass( 'option' );
238
+ option.setTextContent( 'DRC' );
239
+ option.onClick( async function () {
240
+
241
+ const object = editor.selected;
242
+
243
+ if ( object === null || object.isMesh === undefined ) {
244
+
245
+ alert( strings.getKey( 'prompt/file/export/noMeshSelected' ) );
246
+ return;
247
+
248
+ }
249
+
250
+ const { DRACOExporter } = await import( 'three/addons/exporters/DRACOExporter.js' );
251
+
252
+ const exporter = new DRACOExporter();
253
+
254
+ const options = {
255
+ decodeSpeed: 5,
256
+ encodeSpeed: 5,
257
+ encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
258
+ quantization: [ 16, 8, 8, 8, 8 ],
259
+ exportUvs: true,
260
+ exportNormals: true,
261
+ exportColor: object.geometry.hasAttribute( 'color' )
262
+ };
263
+
264
+ const result = await exporter.parseAsync( object, options );
265
+ saveArrayBuffer( result, 'model.drc' );
266
+
267
+ } );
268
+ fileExportSubmenu.add( option );
269
+
270
+ // Export GLB
271
+
272
+ option = new UIRow();
273
+ option.setClass( 'option' );
274
+ option.setTextContent( 'GLB' );
275
+ option.onClick( async function () {
276
+
277
+ const scene = editor.scene;
278
+
279
+ if ( needsUniqueNames( scene ) ) { // see #25179
280
+
281
+ if ( confirm( strings.getKey( 'prompt/file/export/duplicateNames' ) ) === false ) return;
282
+
283
+ ensureUniqueNames( scene );
284
+
285
+ }
286
+
287
+ const animations = getAnimations( scene );
288
+
289
+ const optimizedAnimations = [];
290
+
291
+ for ( const animation of animations ) {
292
+
293
+ optimizedAnimations.push( animation.clone().optimize() );
294
+
295
+ }
296
+
297
+ const { GLTFExporter } = await import( 'three/addons/exporters/GLTFExporter.js' );
298
+
299
+ const exporter = new GLTFExporter();
300
+
301
+ exporter.parse( scene, function ( result ) {
302
+
303
+ saveArrayBuffer( result, 'scene.glb' );
304
+
305
+ }, undefined, { binary: true, animations: optimizedAnimations } );
306
+
307
+ } );
308
+ fileExportSubmenu.add( option );
309
+
310
+ // Export GLTF
311
+
312
+ option = new UIRow();
313
+ option.setClass( 'option' );
314
+ option.setTextContent( 'GLTF' );
315
+ option.onClick( async function () {
316
+
317
+ const scene = editor.scene;
318
+
319
+ if ( needsUniqueNames( scene ) ) { // see #25179
320
+
321
+ if ( confirm( strings.getKey( 'prompt/file/export/duplicateNames' ) ) === false ) return;
322
+
323
+ ensureUniqueNames( scene );
324
+
325
+ }
326
+
327
+ const animations = getAnimations( scene );
328
+
329
+ const optimizedAnimations = [];
330
+
331
+ for ( const animation of animations ) {
332
+
333
+ optimizedAnimations.push( animation.clone().optimize() );
334
+
335
+ }
336
+
337
+ const { GLTFExporter } = await import( 'three/addons/exporters/GLTFExporter.js' );
338
+
339
+ const exporter = new GLTFExporter();
340
+
341
+ exporter.parse( scene, function ( result ) {
342
+
343
+ saveString( JSON.stringify( result, null, 2 ), 'scene.gltf' );
344
+
345
+ }, undefined, { animations: optimizedAnimations } );
346
+
347
+
348
+ } );
349
+ fileExportSubmenu.add( option );
350
+
351
+ // Export OBJ
352
+
353
+ option = new UIRow();
354
+ option.setClass( 'option' );
355
+ option.setTextContent( 'OBJ' );
356
+ option.onClick( async function () {
357
+
358
+ const object = editor.selected;
359
+
360
+ if ( object === null ) {
361
+
362
+ alert( strings.getKey( 'prompt/file/export/noObjectSelected' ) );
363
+ return;
364
+
365
+ }
366
+
367
+ const { OBJExporter } = await import( 'three/addons/exporters/OBJExporter.js' );
368
+
369
+ const exporter = new OBJExporter();
370
+
371
+ saveString( exporter.parse( object ), 'model.obj' );
372
+
373
+ } );
374
+ fileExportSubmenu.add( option );
375
+
376
+ // Export PLY (ASCII)
377
+
378
+ option = new UIRow();
379
+ option.setClass( 'option' );
380
+ option.setTextContent( 'PLY' );
381
+ option.onClick( async function () {
382
+
383
+ const { PLYExporter } = await import( 'three/addons/exporters/PLYExporter.js' );
384
+
385
+ const exporter = new PLYExporter();
386
+
387
+ exporter.parse( editor.scene, function ( result ) {
388
+
389
+ saveArrayBuffer( result, 'model.ply' );
390
+
391
+ } );
392
+
393
+ } );
394
+ fileExportSubmenu.add( option );
395
+
396
+ // Export PLY (BINARY)
397
+
398
+ option = new UIRow();
399
+ option.setClass( 'option' );
400
+ option.setTextContent( 'PLY (BINARY)' );
401
+ option.onClick( async function () {
402
+
403
+ const { PLYExporter } = await import( 'three/addons/exporters/PLYExporter.js' );
404
+
405
+ const exporter = new PLYExporter();
406
+
407
+ exporter.parse( editor.scene, function ( result ) {
408
+
409
+ saveArrayBuffer( result, 'model-binary.ply' );
410
+
411
+ }, { binary: true } );
412
+
413
+ } );
414
+ fileExportSubmenu.add( option );
415
+
416
+ // Export STL (ASCII)
417
+
418
+ option = new UIRow();
419
+ option.setClass( 'option' );
420
+ option.setTextContent( 'STL' );
421
+ option.onClick( async function () {
422
+
423
+ const { STLExporter } = await import( 'three/addons/exporters/STLExporter.js' );
424
+
425
+ const exporter = new STLExporter();
426
+
427
+ saveString( exporter.parse( editor.scene ), 'model.stl' );
428
+
429
+ } );
430
+ fileExportSubmenu.add( option );
431
+
432
+ // Export STL (BINARY)
433
+
434
+ option = new UIRow();
435
+ option.setClass( 'option' );
436
+ option.setTextContent( 'STL (BINARY)' );
437
+ option.onClick( async function () {
438
+
439
+ const { STLExporter } = await import( 'three/addons/exporters/STLExporter.js' );
440
+
441
+ const exporter = new STLExporter();
442
+
443
+ saveArrayBuffer( exporter.parse( editor.scene, { binary: true } ), 'model-binary.stl' );
444
+
445
+ } );
446
+ fileExportSubmenu.add( option );
447
+
448
+ // Export USDZ
449
+
450
+ option = new UIRow();
451
+ option.setClass( 'option' );
452
+ option.setTextContent( 'USDZ' );
453
+ option.onClick( async function () {
454
+
455
+ const { USDZExporter } = await import( 'three/addons/exporters/USDZExporter.js' );
456
+
457
+ const exporter = new USDZExporter();
458
+
459
+ saveArrayBuffer( await exporter.parseAsync( editor.scene ), 'model.usdz' );
460
+
461
+ } );
462
+ fileExportSubmenu.add( option );
463
+
464
+ //
465
+
466
+ function getAnimations( scene ) {
467
+
468
+ const animations = [];
469
+
470
+ scene.traverse( function ( object ) {
471
+
472
+ animations.push( ... object.animations );
473
+
474
+ } );
475
+
476
+ return animations;
477
+
478
+ }
479
+
480
+ function needsUniqueNames( scene ) {
481
+
482
+ const usedNames = new Set();
483
+ let duplicate = false;
484
+ let animated = false;
485
+
486
+ scene.traverse( function ( object ) {
487
+
488
+ if ( object.animations.length > 0 ) animated = true;
489
+
490
+ if ( object.name === '' ) return;
491
+
492
+ if ( usedNames.has( object.name ) ) duplicate = true;
493
+
494
+ usedNames.add( object.name );
495
+
496
+ } );
497
+
498
+ return duplicate && animated;
499
+
500
+ }
501
+
502
+ // Gives every object a unique name and keeps the animation tracks that
503
+ // reference them by name in sync. The renamed scene mirrors the result of a
504
+ // glTF round-trip, where the loader makes all names unique, too.
505
+
506
+ function ensureUniqueNames( scene ) {
507
+
508
+ // Resolve each track's target object up front, scoped to the object that
509
+ // owns the clip. This disambiguates colliding names before they change.
510
+
511
+ const trackBindings = [];
512
+
513
+ scene.traverse( function ( owner ) {
514
+
515
+ for ( const clip of owner.animations ) {
516
+
517
+ for ( const track of clip.tracks ) {
518
+
519
+ const nodeName = PropertyBinding.parseTrackName( track.name ).nodeName;
520
+ const target = PropertyBinding.findNode( owner, nodeName );
521
+
522
+ // References by UUID stay valid, so only track name-based ones.
523
+
524
+ if ( target !== null && target.name === nodeName ) {
525
+
526
+ trackBindings.push( { track, target, nodeName } );
527
+
528
+ }
529
+
530
+ }
531
+
532
+ }
533
+
534
+ } );
535
+
536
+ // Assign a unique name to every named object.
537
+
538
+ let changed = false;
539
+ const usedNames = new Set();
540
+
541
+ scene.traverse( function ( object ) {
542
+
543
+ if ( object.name === '' ) return;
544
+
545
+ if ( usedNames.has( object.name ) ) {
546
+
547
+ let suffix = 1, name;
548
+ do {
549
+
550
+ name = object.name + '_' + ( suffix ++ );
551
+
552
+ } while ( usedNames.has( name ) );
553
+
554
+ object.name = name;
555
+ changed = true;
556
+
557
+ }
558
+
559
+ usedNames.add( object.name );
560
+
561
+ } );
562
+
563
+ if ( changed === false ) return;
564
+
565
+ // Point the affected tracks at their renamed targets.
566
+
567
+ for ( const { track, target, nodeName } of trackBindings ) {
568
+
569
+ if ( target.name !== nodeName ) {
570
+
571
+ track.name = target.name + track.name.slice( nodeName.length );
572
+
573
+ }
574
+
575
+ }
576
+
577
+ editor.signals.sceneGraphChanged.dispatch();
578
+
579
+ }
580
+
581
+ return container;
582
+
583
+ }
584
+
585
+ export { MenubarFile };
@@ -0,0 +1,73 @@
1
+ import { UIPanel, UIRow } from './libs/ui.js';
2
+
3
+ function MenubarHelp( editor ) {
4
+
5
+ const strings = editor.strings;
6
+
7
+ const container = new UIPanel();
8
+ container.setClass( 'menu' );
9
+
10
+ const title = new UIPanel();
11
+ title.setClass( 'title' );
12
+ title.setTextContent( strings.getKey( 'menubar/help' ) );
13
+ container.add( title );
14
+
15
+ const options = new UIPanel();
16
+ options.setClass( 'options' );
17
+ container.add( options );
18
+
19
+ // Source code
20
+
21
+ let option = new UIRow();
22
+ option.setClass( 'option' );
23
+ option.setTextContent( strings.getKey( 'menubar/help/source_code' ) );
24
+ option.onClick( function () {
25
+
26
+ window.open( 'https://github.com/mrdoob/three.js/tree/master/editor', '_blank' );
27
+
28
+ } );
29
+ options.add( option );
30
+
31
+ /*
32
+ // Icon
33
+
34
+ let option = new UIRow();
35
+ option.setClass( 'option' );
36
+ option.setTextContent( strings.getKey( 'menubar/help/icons' ) );
37
+ option.onClick( function () {
38
+
39
+ window.open( 'https://www.flaticon.com/packs/interface-44', '_blank' );
40
+
41
+ } );
42
+ options.add( option );
43
+ */
44
+
45
+ // About
46
+
47
+ option = new UIRow();
48
+ option.setClass( 'option' );
49
+ option.setTextContent( strings.getKey( 'menubar/help/about' ) );
50
+ option.onClick( function () {
51
+
52
+ window.open( 'https://threejs.org', '_blank' );
53
+
54
+ } );
55
+ options.add( option );
56
+
57
+ // Manual
58
+
59
+ option = new UIRow();
60
+ option.setClass( 'option' );
61
+ option.setTextContent( strings.getKey( 'menubar/help/manual' ) );
62
+ option.onClick( function () {
63
+
64
+ window.open( 'https://github.com/mrdoob/three.js/wiki/Editor-Manual', '_blank' );
65
+
66
+ } );
67
+ options.add( option );
68
+
69
+ return container;
70
+
71
+ }
72
+
73
+ export { MenubarHelp };