@woosh/meep-engine 2.119.57 → 2.119.59

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 (49) hide show
  1. package/editor/ecs/component/createObjectEditor.js +2 -0
  2. package/editor/tools/TransformTool.js +6 -5
  3. package/editor/tools/v2/TransformMode.d.ts +7 -0
  4. package/editor/tools/v2/TransformMode.d.ts.map +1 -0
  5. package/package.json +1 -1
  6. package/src/core/collection/array/array_shift_back.d.ts +9 -0
  7. package/src/core/collection/array/array_shift_back.d.ts.map +1 -0
  8. package/src/core/collection/array/array_shift_back.js +20 -0
  9. package/src/core/events/signal/Signal.d.ts.map +1 -1
  10. package/src/core/events/signal/Signal.js +55 -1
  11. package/src/core/events/signal/SignalHandler.d.ts +5 -0
  12. package/src/core/events/signal/SignalHandler.d.ts.map +1 -1
  13. package/src/core/events/signal/SignalHandler.js +6 -0
  14. package/src/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.d.ts.map +1 -1
  15. package/src/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +2 -1
  16. package/src/engine/EngineHarness.d.ts +2 -2
  17. package/src/engine/EngineHarness.d.ts.map +1 -1
  18. package/src/engine/EngineHarness.js +1 -1
  19. package/src/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +1 -1
  20. package/src/engine/graphics/material/manager/MaterialManager.d.ts +6 -0
  21. package/src/engine/graphics/material/manager/MaterialManager.d.ts.map +1 -1
  22. package/src/engine/graphics/material/manager/MaterialManager.js +9 -0
  23. package/src/engine/graphics/sh3/gi/material/common.d.ts +1 -1
  24. package/src/engine/graphics/sh3/gi/material/common.js +1 -1
  25. package/src/engine/graphics/sh3/lpv/LightProbeVolume.d.ts +7 -1
  26. package/src/engine/graphics/sh3/lpv/LightProbeVolume.d.ts.map +1 -1
  27. package/src/engine/graphics/sh3/lpv/LightProbeVolume.js +60 -0
  28. package/src/engine/graphics/sh3/lpv/PathTracerProbeRenderer.d.ts.map +1 -1
  29. package/src/engine/graphics/sh3/lpv/PathTracerProbeRenderer.js +1 -0
  30. package/src/engine/graphics/sh3/lpv/build_probes_for_scene.d.ts +8 -0
  31. package/src/engine/graphics/sh3/lpv/build_probes_for_scene.d.ts.map +1 -1
  32. package/src/engine/graphics/sh3/lpv/build_probes_for_scene.js +38 -27
  33. package/src/engine/graphics/sh3/lpv/serialization/LightProbeVolumeSerializationAdapter.d.ts.map +1 -1
  34. package/src/engine/graphics/sh3/lpv/serialization/LightProbeVolumeSerializationAdapter.js +2 -0
  35. package/src/engine/graphics/sh3/lpv/util/lpv_visualise_probes.d.ts.map +1 -1
  36. package/src/engine/graphics/sh3/lpv/util/lpv_visualise_probes.js +6 -2
  37. package/src/engine/graphics/sh3/lpv_build_editor.d.ts +9 -0
  38. package/src/engine/graphics/sh3/lpv_build_editor.d.ts.map +1 -0
  39. package/src/engine/graphics/sh3/lpv_build_editor.js +695 -0
  40. package/src/engine/graphics/sh3/prototypeSH3Probe.js +14 -1
  41. package/src/engine/graphics/sh3/shader/SH3VisualisationMaterial.d.ts +1 -0
  42. package/src/engine/graphics/sh3/shader/SH3VisualisationMaterial.d.ts.map +1 -1
  43. package/src/engine/graphics/sh3/shader/SH3VisualisationMaterial.js +9 -0
  44. package/src/engine/graphics/sh3/visualise_spherical_harmonic_sphere.d.ts +1 -1
  45. package/src/engine/graphics/sh3/visualise_spherical_harmonic_sphere.d.ts.map +1 -1
  46. package/src/engine/graphics/sh3/visualise_spherical_harmonic_sphere.js +2 -5
  47. package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
  48. package/src/engine/input/devices/KeyboardDevice.js +23 -1
  49. package/editor/tools/SelectionTool.d.ts +0 -27
@@ -0,0 +1,695 @@
1
+ import { OctahedronBufferGeometry } from "three";
2
+ import { TransformControls } from "../../../../editor/tools/v2/TransformControls.js";
3
+ import { TransformMode } from "../../../../editor/tools/v2/TransformMode.js";
4
+ import { assert } from "../../../core/assert.js";
5
+ import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
6
+ import { downloadAsFile } from "../../../core/binary/downloadAsFile.js";
7
+ import { EndianType } from "../../../core/binary/EndianType.js";
8
+ import { SignalBinding } from "../../../core/events/signal/SignalBinding.js";
9
+ import { SimpleStateMachine } from "../../../core/fsm/simple/SimpleStateMachine.js";
10
+ import { SimpleStateMachineDescription } from "../../../core/fsm/simple/SimpleStateMachineDescription.js";
11
+ import { SurfacePoint3 } from "../../../core/geom/3d/SurfacePoint3.js";
12
+ import Vector3 from "../../../core/geom/Vector3.js";
13
+ import { Action } from "../../../core/process/undo/Action.js";
14
+ import { ActionProcessor } from "../../../core/process/undo/ActionProcessor.js";
15
+ import { ArrayBufferLoader } from "../../asset/loaders/ArrayBufferLoader.js";
16
+ import Tag from "../../ecs/components/Tag.js";
17
+ import Entity from "../../ecs/Entity.js";
18
+ import { Transform } from "../../ecs/transform/Transform.js";
19
+ import { EngineHarness } from "../../EngineHarness.js";
20
+ import Highlight from "../ecs/highlight/Highlight.js";
21
+ import { ShadedGeometryHighlightSystem } from "../ecs/highlight/system/ShadedGeometryHighlightSystem.js";
22
+ import { ShadedGeometry } from "../ecs/mesh-v2/ShadedGeometry.js";
23
+ import { ShadedGeometrySystem } from "../ecs/mesh-v2/ShadedGeometrySystem.js";
24
+ import { make_ray_from_viewport_position } from "../make_ray_from_viewport_position.js";
25
+ import { MaterialTransformer } from "./gi/material/MaterialTransformer.js";
26
+ import { lpv_volume_bake_via_task } from "./lpv/build_probes_for_scene.js";
27
+ import { LightProbeVolumeSerializationAdapter } from "./lpv/serialization/LightProbeVolumeSerializationAdapter.js";
28
+ import { sh3_make_shaded_geometry } from "./visualise_spherical_harmonic_sphere.js";
29
+
30
+ const DEFAULT_SH_DATA = new Float32Array(27);
31
+ DEFAULT_SH_DATA.fill(1);
32
+
33
+ class ActionProbeRemove extends Action {
34
+
35
+ entity = -1;
36
+
37
+ _index = -1;
38
+
39
+ /**
40
+ * @type {Entity}
41
+ * @private
42
+ */
43
+ _destroyed;
44
+
45
+ static from(entity) {
46
+ const r = new ActionProbeRemove();
47
+
48
+ r.entity = entity;
49
+
50
+ return r;
51
+ }
52
+
53
+ /**
54
+ *
55
+ * @param {EditorState} context
56
+ * @return {Promise<void>}
57
+ */
58
+ async apply(context) {
59
+
60
+ const probe_index = context.getProbeIndex(this.entity);
61
+
62
+ this._index = probe_index;
63
+
64
+ this._destroyed = context.removeProbe(probe_index);
65
+ }
66
+
67
+ /**
68
+ *
69
+ * @param {EditorState} context
70
+ * @return {Promise<void>}
71
+ */
72
+ async revert(context) {
73
+
74
+ this.entity = this._destroyed.id;
75
+
76
+ context.createProbe(this._destroyed);
77
+
78
+ this._destroyed = null;
79
+ this._index = -1;
80
+
81
+ }
82
+
83
+ }
84
+
85
+ class EditorState {
86
+ /**
87
+ *
88
+ * @type {Entity[]}
89
+ */
90
+ probes = [];
91
+ /**
92
+ *
93
+ * @type {LightProbeVolume|null}
94
+ */
95
+ volume = null
96
+
97
+ /**
98
+ * @type {Engine}
99
+ */
100
+ engine
101
+
102
+ mesh_needs_rebuilding = false;
103
+
104
+ ensureMesh() {
105
+ if (this.mesh_needs_rebuilding) {
106
+ this.volume.build_mesh();
107
+ this.mesh_needs_rebuilding = false;
108
+ }
109
+ }
110
+
111
+ /**
112
+ *
113
+ * @return {EntityComponentDataset}
114
+ */
115
+ get ecd() {
116
+ return this.engine.entityManager.dataset;
117
+ }
118
+
119
+ /**
120
+ *
121
+ * @param {Entity} entity
122
+ */
123
+ createProbe(entity) {
124
+
125
+ entity.build(this.ecd);
126
+
127
+ this.probes.push(entity);
128
+
129
+ const p = entity.getComponent(Transform).position;
130
+
131
+ const index = this.volume.add_point(...p);
132
+
133
+ assert.equal(index, this.probes.length - 1);
134
+
135
+ this.mesh_needs_rebuilding = true;
136
+ }
137
+
138
+ /**
139
+ *
140
+ * @param {number} index
141
+ * @return {Entity}
142
+ */
143
+ removeProbe(index) {
144
+ const [probe] = this.probes.splice(index, 1);
145
+
146
+ probe.destroy();
147
+
148
+ this.volume.remove_point(index);
149
+
150
+ this.mesh_needs_rebuilding = true;
151
+
152
+ return probe;
153
+ }
154
+
155
+ probeCoefficientsFromVolume(index) {
156
+ const probe = this.probes[index];
157
+
158
+ const sg = probe.getComponent(ShadedGeometry);
159
+
160
+ sg.material.fromArray(this.volume.harmonics, index * 27);
161
+ }
162
+
163
+ /**
164
+ *
165
+ * @param {number} index
166
+ */
167
+ probeToVolume(index) {
168
+
169
+ const probe = this.probes[index];
170
+ const t = probe.getComponent(Transform);
171
+
172
+ const existing_position = new Vector3();
173
+ existing_position.fromArray(this.volume.points, index * 3);
174
+
175
+ if (existing_position.equals(t.position)) {
176
+ // all good
177
+ return;
178
+ }
179
+
180
+ t.position.toArray(this.volume.points, index * 3);
181
+ this.volume.incrementVersion();
182
+
183
+ this.mesh_needs_rebuilding = true;
184
+ }
185
+
186
+ /**
187
+ *
188
+ * @param {number} entity_id
189
+ * @return {number}
190
+ */
191
+ getProbeIndex(entity_id) {
192
+ return this.probes.findIndex(p => p.id === entity_id);
193
+ }
194
+ }
195
+
196
+ /**
197
+ *
198
+ * @param {Engine} engine
199
+ * @param {LightProbeVolume} volume
200
+ * @param {string} [path_key]
201
+ * @param {number} [placement_surface_offset] How far to place new probes away from the surface
202
+ */
203
+ export async function lpv_build_editor({
204
+ engine,
205
+ volume,
206
+ path_key = "lpv",
207
+ placement_surface_offset = 1
208
+ }) {
209
+
210
+ engine.assetManager.registerLoader('binary', new ArrayBufferLoader());
211
+
212
+
213
+ let build_in_progress = false;
214
+
215
+ async function bake() {
216
+ if (build_in_progress) {
217
+ console.warn('build in progress');
218
+ return;
219
+ }
220
+ build_in_progress = true;
221
+
222
+ ctx.probes.forEach(p => p.destroy());
223
+
224
+ volume.build_mesh();
225
+
226
+ await lpv_volume_bake_via_task(volume, ecd, engine);
227
+
228
+ for (let i = 0; i < ctx.probes.length; i++) {
229
+ ctx.probeCoefficientsFromVolume(i);
230
+ }
231
+
232
+ ctx.probes.forEach(p => p.build(ecd));
233
+
234
+ update_volume_visual();
235
+
236
+ build_in_progress = false;
237
+
238
+ }
239
+
240
+ // create drop handle
241
+ document.body.addEventListener('dragover', (ev) => {
242
+ ev.preventDefault();
243
+ });
244
+ document.body.addEventListener("drop",
245
+ /**
246
+ *
247
+ * @param {DragEvent} ev
248
+ */
249
+ (ev) => {
250
+ ev.preventDefault();
251
+
252
+ let processed = false;
253
+
254
+ /**
255
+ *
256
+ * @param {File} file
257
+ */
258
+ function processFile(file) {
259
+ if (processed) {
260
+ return;
261
+ }
262
+
263
+ // URL @ Mozilla, webkitURL @ Chrome
264
+ const url = (window.webkitURL ? webkitURL : URL).createObjectURL(file);
265
+
266
+ engine.assetManager.promise(url, "binary").then(asset => {
267
+
268
+ const data = asset.create();
269
+
270
+ const buffer = BinaryBuffer.fromArrayBuffer(data);
271
+ buffer.endianness = EndianType.LittleEndian;
272
+
273
+ const adapter = new LightProbeVolumeSerializationAdapter();
274
+
275
+ adapter.deserialize(buffer, volume);
276
+
277
+ // assume mesh is invalid just in case
278
+ ctx.mesh_needs_rebuilding = true;
279
+
280
+ // reinit
281
+ reinit();
282
+ update_volume_visual();
283
+
284
+ });
285
+
286
+
287
+ processed = true;
288
+ }
289
+
290
+ if (ev.dataTransfer.items) {
291
+ // Use DataTransferItemList interface to access the file(s)
292
+ for (var i = 0; i < ev.dataTransfer.items.length; i++) {
293
+ // If dropped items aren't files, reject them
294
+ if (ev.dataTransfer.items[i].kind === 'file') {
295
+ var file = ev.dataTransfer.items[i].getAsFile();
296
+
297
+ processFile(file);
298
+ }
299
+ }
300
+ } else {
301
+ // Use DataTransfer interface to access the file(s)
302
+ for (var i = 0; i < ev.dataTransfer.files.length; i++) {
303
+ const file = ev.dataTransfer.files[i];
304
+
305
+ processFile(file);
306
+ }
307
+ }
308
+ })
309
+
310
+ const camera_controller = await EngineHarness.buildOrbitalCameraController({
311
+ engine
312
+ });
313
+ camera_controller.destroy();
314
+
315
+ const probe_size = 1;
316
+
317
+ const em = engine.entityManager;
318
+ const ecd = em.dataset;
319
+
320
+ em.addSystem(new ShadedGeometryHighlightSystem(engine));
321
+
322
+ ecd.registerComponentType(Highlight);
323
+
324
+ const PROBE_TAG_STRING = 'PLV/probe';
325
+
326
+ const geometry = new OctahedronBufferGeometry(1, 5);
327
+
328
+ const ctx = new EditorState();
329
+ ctx.probes = [];
330
+ ctx.volume = volume;
331
+ ctx.engine = engine;
332
+
333
+ /**
334
+ *
335
+ * @type {number[]}
336
+ */
337
+ const selected = [];
338
+
339
+ const actions = new ActionProcessor(ctx);
340
+
341
+ const controls = new TransformControls(engine.graphics.camera, engine.viewStack.el);
342
+ controls.setMode(TransformMode.Translate);
343
+ controls.build(ecd);
344
+ controls.detach();
345
+
346
+
347
+ function unselect() {
348
+ if (selected.length === 0) {
349
+ return;
350
+ }
351
+
352
+ for (let i = 0; i < selected.length; i++) {
353
+
354
+ ecd.removeComponentFromEntity(selected[i], Highlight);
355
+
356
+ }
357
+
358
+ selected.splice(0, selected.length);
359
+
360
+ }
361
+
362
+ /**
363
+ *
364
+ * @param {number[]} entities
365
+ */
366
+ function set_selection(entities) {
367
+ unselect();
368
+
369
+ for (let i = 0; i < entities.length; i++) {
370
+ const entity = entities[i];
371
+
372
+ ecd.addComponentToEntity(entity, Highlight.fromOne(1, 1, 0));
373
+ selected.push(entity);
374
+
375
+ }
376
+
377
+ }
378
+
379
+ function create_probe_entity(volume_index = -1) {
380
+
381
+ const entity = new Entity();
382
+
383
+ const transform = new Transform();
384
+
385
+ transform.scale.setScalar(probe_size);
386
+
387
+ entity.add(Tag.fromOne(PROBE_TAG_STRING));
388
+ entity.add(transform);
389
+
390
+ let sg;
391
+ if (volume_index >= 0) {
392
+
393
+ sg = sh3_make_shaded_geometry(
394
+ ctx.volume.harmonics, 27 * volume_index
395
+ );
396
+ } else {
397
+ sg = sh3_make_shaded_geometry(DEFAULT_SH_DATA, 0);
398
+ }
399
+
400
+ entity.add(sg);
401
+
402
+ return entity;
403
+ }
404
+
405
+ const smd = new SimpleStateMachineDescription();
406
+ const STATE_NAVIGATION = smd.createState();
407
+ const STATE_TRANSFORM = smd.createState();
408
+
409
+ smd.createEdge(STATE_NAVIGATION, STATE_TRANSFORM);
410
+ smd.createEdge(STATE_TRANSFORM, STATE_NAVIGATION);
411
+
412
+ const transform_proxy = new Entity().add(new Transform());
413
+ transform_proxy.build(ecd);
414
+
415
+ /**
416
+ *
417
+ * @param {number[]} entities
418
+ */
419
+ function attach_transform(entities) {
420
+ const entities_copy = entities.slice();
421
+
422
+ // figure out center mass
423
+ const center = new Vector3();
424
+ for (let i = 0; i < entities.length; i++) {
425
+ const entity = entities[i];
426
+
427
+ center.add(
428
+ ecd.getComponent(entity, Transform).position
429
+ );
430
+ }
431
+ center.multiplyScalar(1 / entities.length);
432
+ transform_proxy.getComponent(Transform).position.copy(center);
433
+
434
+ const last_position = new Vector3();
435
+ last_position.copy(center);
436
+
437
+ controls.attach(transform_proxy.id);
438
+ controls.entity.addEventListener("change", () => {
439
+ if (entities_copy.length === 0) {
440
+ return;
441
+ }
442
+
443
+ const new_position = transform_proxy.getComponent(Transform).position.clone();
444
+ const displacement = new_position.clone();
445
+ displacement.sub(last_position);
446
+
447
+ last_position.copy(new_position);
448
+
449
+ for (let i = 0; i < entities.length; i++) {
450
+ const entity = entities[i];
451
+ const index = ctx.getProbeIndex(entity);
452
+
453
+ const probe = ctx.probes[index];
454
+
455
+ probe.getComponent(Transform).position.add(displacement);
456
+
457
+ ctx.probeToVolume(index);
458
+ }
459
+ })
460
+ }
461
+
462
+ const sm = new SimpleStateMachine(smd);
463
+
464
+ const keys = engine.devices.keyboard.keys;
465
+
466
+ function register_shortcuts_common() {
467
+
468
+ const ctrl = keys.ctrl;
469
+
470
+ keys.l.down.add(reinit);
471
+ keys.b.down.add(bake);
472
+
473
+ keys.z.down.add(() => {
474
+
475
+ if (ctrl.is_down) {
476
+ actions.undo();
477
+ }
478
+
479
+ });
480
+
481
+ keys.y.down.add(() => {
482
+
483
+ if (ctrl.is_down) {
484
+ actions.redo();
485
+ }
486
+
487
+ });
488
+
489
+ keys.s.down.add(() => {
490
+
491
+ if (ctrl.is_down) {
492
+
493
+ const buffer = BinaryBuffer.fromEndianness(EndianType.LittleEndian);
494
+
495
+ const adapter = new LightProbeVolumeSerializationAdapter();
496
+
497
+ adapter.serialize(buffer, volume);
498
+
499
+ buffer.trim();
500
+
501
+ engine.storage.promiseStoreBinary(`lpv:${path_key}`, buffer.data);
502
+
503
+
504
+ const date = new Date();
505
+
506
+ downloadAsFile(buffer.data, `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}_${date.getHours()}-${date.getMinutes()}-${date.getSeconds()}.lpv`)
507
+ }
508
+
509
+ });
510
+ }
511
+
512
+
513
+ const state_bindings_nav = [
514
+ new SignalBinding(engine.devices.pointer.on.tap, () => {
515
+ const ray = make_ray_from_viewport_position(engine, engine.devices.pointer.position);
516
+
517
+ const sgm = em.getSystem(ShadedGeometrySystem);
518
+
519
+ const contact = new SurfacePoint3();
520
+
521
+ if (keys.ctrl.is_down) {
522
+ // create new entity
523
+ const entity = sgm.raycastNearest(
524
+ contact,
525
+ ...ray.origin,
526
+ ...ray.direction,
527
+ (entity) => {
528
+ return !(ecd.getComponent(entity, Tag)?.contains(PROBE_TAG_STRING));
529
+ }
530
+ );
531
+
532
+ if (entity !== undefined) {
533
+
534
+
535
+ const probe = create_probe_entity();
536
+
537
+ const p = new Vector3();
538
+ p.copy(contact.normal);
539
+ p.multiplyScalar(0.01);
540
+ p.add(contact.position);
541
+
542
+ const p2 = new SurfacePoint3();
543
+
544
+ // do raycast in opposite direction
545
+ const entity_2 = sgm.raycastNearest(
546
+ p2,
547
+ ...p,
548
+ ...contact.normal,
549
+ (entity) => {
550
+ return !(ecd.getComponent(entity, Tag)?.contains(PROBE_TAG_STRING));
551
+ }
552
+ );
553
+
554
+ if (entity_2 !== undefined && p2.position.distanceTo(p) < placement_surface_offset * 2) {
555
+
556
+ p.lerpVectors(p, p2.position, 0.5);
557
+
558
+ } else {
559
+ p.copy(contact.normal);
560
+ p.multiplyScalar(placement_surface_offset);
561
+ p.add(contact.position);
562
+ }
563
+
564
+ probe.getComponent(Transform).position.copy(p);
565
+
566
+ ctx.createProbe(probe);
567
+
568
+ set_selection([probe.id]);
569
+
570
+ }
571
+
572
+ } else {
573
+ // do select
574
+ const entity = sgm.raycastNearest(
575
+ contact,
576
+ ...ray.origin,
577
+ ...ray.direction,
578
+ (entity) => {
579
+ return ecd.getComponent(entity, Tag)?.contains(PROBE_TAG_STRING);
580
+ }
581
+ );
582
+
583
+ if (entity !== undefined) {
584
+
585
+ const entity_id = entity.entity;
586
+
587
+ if (keys.shift.is_down) {
588
+ if (selected.includes(entity_id)) {
589
+ set_selection(selected.filter(e => e !== entity_id));
590
+ } else {
591
+ set_selection(selected.slice().concat(entity_id));
592
+ }
593
+ } else {
594
+ set_selection([entity_id]);
595
+ }
596
+ }
597
+ }
598
+ }),
599
+ new SignalBinding(keys.x.down, () => {
600
+
601
+ if (selected.length === 0) {
602
+ return;
603
+ }
604
+
605
+ const entities = selected.slice();
606
+
607
+ unselect();
608
+
609
+ actions.mark('Delete');
610
+ for (let i = 0; i < entities.length; i++) {
611
+ actions.do(ActionProbeRemove.from(entities[i]));
612
+ }
613
+ }),
614
+ new SignalBinding(keys.e.down, () => {
615
+ if (selected.length === 0) {
616
+ return;
617
+ }
618
+
619
+ sm.navigateTo(STATE_TRANSFORM);
620
+ }),
621
+ new SignalBinding(keys.a.down, () => {
622
+ if (keys.ctrl.is_down) {
623
+ // select all
624
+ set_selection(ctx.probes.map(e => e.id));
625
+ }
626
+ })
627
+ ];
628
+
629
+ sm.addEventHandlerStateEntry(STATE_NAVIGATION, () => {
630
+ camera_controller.build(ecd);
631
+
632
+ state_bindings_nav.forEach(b => b.link());
633
+ });
634
+ sm.addEventHandlerStateExit(STATE_NAVIGATION, () => {
635
+ camera_controller.destroy();
636
+
637
+ state_bindings_nav.forEach(b => b.unlink())
638
+ });
639
+
640
+ const state_bindings_transform = [
641
+
642
+ new SignalBinding(keys.e.down, () => {
643
+ sm.navigateTo(STATE_NAVIGATION);
644
+ }),
645
+ ];
646
+
647
+ sm.addEventHandlerStateEntry(STATE_TRANSFORM, () => {
648
+ state_bindings_transform.forEach(b => b.link());
649
+
650
+ attach_transform(selected);
651
+ });
652
+ sm.addEventHandlerStateExit(STATE_TRANSFORM, () => {
653
+ state_bindings_transform.forEach(b => b.unlink());
654
+
655
+ controls.detach();
656
+ });
657
+
658
+ sm.setState(STATE_NAVIGATION);
659
+
660
+ function initialize_from_volume() {
661
+
662
+ for (let i = 0; i < volume.count; i++) {
663
+
664
+ const entity = create_probe_entity(i);
665
+
666
+ entity.getComponent(Transform).position.fromArray(volume.points, i * 3);
667
+
668
+ ctx.probes.push(entity);
669
+
670
+ entity.build(ecd);
671
+ }
672
+
673
+ }
674
+
675
+ function update_volume_visual() {
676
+ const transformers = engine.graphics.getMaterialManager().getCompilationSteps(MaterialTransformer);
677
+
678
+ ctx.ensureMesh();
679
+
680
+ transformers.forEach(t => t.update());
681
+ }
682
+
683
+ function reinit() {
684
+ unselect();
685
+ ctx.probes.forEach(p => p.destroy());
686
+ ctx.probes.splice(0, ctx.probes.length);
687
+ initialize_from_volume();
688
+
689
+ console.log(volume);
690
+ }
691
+
692
+
693
+ initialize_from_volume();
694
+ register_shortcuts_common();
695
+ }