@woosh/meep-engine 2.37.1 → 2.37.4

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.
@@ -763,6 +763,7 @@ class Quaternion {
763
763
  }
764
764
 
765
765
  /**
766
+ * XYZ order
766
767
  * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
767
768
  * @param {number} x angle in X axis in radians
768
769
  * @param {number} y angle in Y axis in radians
@@ -788,6 +789,36 @@ class Quaternion {
788
789
  this.set(_x, _y, _z, _w);
789
790
  }
790
791
 
792
+ /**
793
+ * XYZ order
794
+ * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
795
+ * @see http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
796
+ * @see https://github.com/mrdoob/three.js/blob/510705cde208b165fd87946b0f8504a1cd6dbe83/src/math/Quaternion.js#L206
797
+ * @param {number} x angle in X axis in radians
798
+ * @param {number} y angle in Y axis in radians
799
+ * @param {number} z angle in Z axis in radians
800
+ */
801
+ fromEulerAnglesXYZ(x, y, z) {
802
+ const scaled_x = x * 0.5;
803
+ const scaled_y = y * 0.5;
804
+ const scaled_z = z * 0.5;
805
+
806
+ const s1 = Math.sin(scaled_x);
807
+ const s2 = Math.sin(scaled_y);
808
+ const s3 = Math.sin(scaled_z);
809
+
810
+ const c1 = Math.cos(scaled_x);
811
+ const c2 = Math.cos(scaled_y);
812
+ const c3 = Math.cos(scaled_z);
813
+
814
+ const _x = s1 * c2 * c3 + c1 * s2 * s3;
815
+ const _y = c1 * s2 * c3 - s1 * c2 * s3;
816
+ const _z = c1 * c2 * s3 + s1 * s2 * c3;
817
+ const _w = c1 * c2 * c3 - s1 * s2 * s3;
818
+
819
+ this.set(_x, _y, _z, _w);
820
+ }
821
+
791
822
  /**
792
823
  * Writes Pitch(Y), Yaw(Z), Roll(X) to a given vector
793
824
  * @source http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
@@ -0,0 +1,3 @@
1
+ export class ModuleRegistry {
2
+
3
+ }
@@ -9,7 +9,6 @@ import List from "../../collection/list/List.js";
9
9
  import Signal from "../../events/signal/Signal.js";
10
10
  import { min2 } from "../../math/min2.js";
11
11
  import { Mark } from "./Mark.js";
12
- import { IllegalStateException } from "../../fsm/exceptions/IllegalStateException.js";
13
12
 
14
13
  /**
15
14
  * @template C, M
@@ -75,13 +74,6 @@ export class ActionProcessor {
75
74
  */
76
75
  this.onMarkRemoved = new Signal();
77
76
 
78
- /**
79
- *
80
- * @type {boolean}
81
- * @private
82
- */
83
- this.__action_in_progress = false;
84
-
85
77
  /**
86
78
  *
87
79
  * @type {Promise<never>}
@@ -172,10 +164,8 @@ export class ActionProcessor {
172
164
  * Insert a mark into the timeline, all actions between marks are grouped logically for UNDO and REDO mechanism
173
165
  * @param {D} description
174
166
  */
175
- mark(description) {
176
- if (this.__action_in_progress) {
177
- throw new IllegalStateException('Action is currently in progress');
178
- }
167
+ async mark(description) {
168
+ await this.__barrier();
179
169
 
180
170
  const marks = this.marks;
181
171
  const numMarks = marks.length;
@@ -265,6 +255,24 @@ export class ActionProcessor {
265
255
  * @throws {Error} when mark is non-unique
266
256
  */
267
257
  async navigateTo(mark_descriptor) {
258
+ await this.__barrier();
259
+
260
+ const p = this.__unsafe_navigateTo(mark_descriptor);
261
+
262
+ this.__last_promise = p;
263
+
264
+ return await p;
265
+ }
266
+
267
+ /**
268
+ * Navigate to a specific mark back or forward
269
+ * @param {M} mark_descriptor specific mark description
270
+ * @returns {boolean} true on success, false otherwise (e.g. mark not found)
271
+ * @throws {Error} when mark is non-unique
272
+ * @private
273
+ */
274
+ async __unsafe_navigateTo(mark_descriptor) {
275
+
268
276
  // find all matching marks
269
277
  const matching_marks = this.__find_mark_indices_by_description(mark_descriptor);
270
278
 
@@ -293,7 +301,7 @@ export class ActionProcessor {
293
301
 
294
302
  while (target > this.cursor) {
295
303
  // navigate forward
296
- await this.redo();
304
+ await this.__unsafe_redo();
297
305
 
298
306
  if (temp_cursor === this.cursor) {
299
307
  // no cursor change
@@ -307,7 +315,7 @@ export class ActionProcessor {
307
315
 
308
316
  while (target < this.cursor) {
309
317
  // navigate backwards
310
- await this.undo();
318
+ await this.__unsafe_undo();
311
319
 
312
320
  if (temp_cursor === this.cursor) {
313
321
  // no cursor change
@@ -323,6 +331,21 @@ export class ActionProcessor {
323
331
  }
324
332
 
325
333
  async undo() {
334
+ await this.__barrier();
335
+
336
+ const p = this.__unsafe_undo();
337
+
338
+ this.__last_promise = p;
339
+
340
+ await p;
341
+ }
342
+
343
+ /**
344
+ *
345
+ * @returns {Promise<void>}
346
+ * @private
347
+ */
348
+ async __unsafe_undo() {
326
349
  let markAddress = 0;
327
350
  /**
328
351
  *
@@ -351,7 +374,7 @@ export class ActionProcessor {
351
374
  this.cursor--;
352
375
  const action = this.history.get(this.cursor);
353
376
 
354
- await this.__action_revert(action);
377
+ await action.revert(this.context);
355
378
  }
356
379
 
357
380
  if (mark !== null) {
@@ -363,8 +386,23 @@ export class ActionProcessor {
363
386
  * redo previous sequence of actions on the timeline, up to the next mark
364
387
  */
365
388
  async redo() {
389
+ await this.__barrier();
390
+
391
+ const p = this.__unsafe_redo();
366
392
 
393
+ this.__last_promise = p;
394
+
395
+ await p;
396
+ }
397
+
398
+ /**
399
+ *
400
+ * @returns {Promise<void>}
401
+ * @private
402
+ */
403
+ async __unsafe_redo() {
367
404
  let mark = this.history.length;
405
+
368
406
  //find mark
369
407
  const marks = this.marks;
370
408
 
@@ -382,7 +420,7 @@ export class ActionProcessor {
382
420
  while (this.cursor < mark) {
383
421
  const action = this.history.get(this.cursor);
384
422
 
385
- await this.__action_apply(action);
423
+ await action.apply(this.context);
386
424
 
387
425
  this.historyMemoryUsage += action.computeByteSize();
388
426
 
@@ -395,15 +433,26 @@ export class ActionProcessor {
395
433
  * @param {Action[]} actions
396
434
  */
397
435
  async doMany(actions) {
398
- const n = actions.length;
436
+ await this.__barrier();
399
437
 
400
- for (let i = 0; i < n; i++) {
438
+ const p = new Promise(async (resolve, reject) => {
401
439
 
402
- const action = actions[i];
440
+ const n = actions.length;
403
441
 
404
- await this.do(action);
442
+ for (let i = 0; i < n; i++) {
405
443
 
406
- }
444
+ const action = actions[i];
445
+
446
+ await this.__unsafe_do(action);
447
+
448
+ }
449
+
450
+ resolve();
451
+ });
452
+
453
+ this.__last_promise = p;
454
+
455
+ await p;
407
456
  }
408
457
 
409
458
  /**
@@ -411,21 +460,13 @@ export class ActionProcessor {
411
460
  * @param {Action} action
412
461
  */
413
462
  async do(action) {
414
- await this.__action_apply(action);
463
+ await this.__barrier();
415
464
 
416
- this.historyMemoryUsage += action.computeByteSize();
465
+ const p = this.__unsafe_do(action);
417
466
 
418
- const history = this.history;
467
+ this.__last_promise = p;
419
468
 
420
- //doing action invalidates "future" branch of history, we need to clear all actions beyond cursor
421
- if (this.cursor < history.length) {
422
- history.crop(0, this.cursor);
423
- // TODO clear future marks
424
- }
425
-
426
- history.add(action);
427
-
428
- this.cursor++;
469
+ await p;
429
470
  }
430
471
 
431
472
  /**
@@ -434,63 +475,34 @@ export class ActionProcessor {
434
475
  * @returns {Promise<void>}
435
476
  * @private
436
477
  */
437
- async __action_apply(action) {
438
- try {
439
- await this.__last_promise;
440
- } catch (e) {
441
- // ignore
442
- }
478
+ async __unsafe_do(action) {
479
+ await action.apply(this.context);
443
480
 
444
- if (this.__action_in_progress) {
445
- throw new IllegalStateException();
446
- }
481
+ this.historyMemoryUsage += action.computeByteSize();
447
482
 
448
- this.__action_in_progress = true;
483
+ const history = this.history;
449
484
 
450
- let p;
451
- try {
452
- p = action.apply(this.context);
453
- } finally {
454
- this.__action_in_progress = false;
485
+ //doing action invalidates "future" branch of history, we need to clear all actions beyond cursor
486
+ if (this.cursor < history.length) {
487
+ history.crop(0, this.cursor);
488
+ // TODO clear future marks
455
489
  }
456
490
 
457
- if (p !== undefined) {
458
- this.__last_promise = p;
459
- await p;
460
- }
491
+ history.add(action);
461
492
 
493
+ this.cursor++;
462
494
  }
463
495
 
464
496
  /**
465
- *
466
- * @param {Action} action
497
+ * Await for synchronization barrier
467
498
  * @returns {Promise<void>}
468
499
  * @private
469
500
  */
470
- async __action_revert(action) {
501
+ async __barrier() {
471
502
  try {
472
503
  await this.__last_promise;
473
504
  } catch (e) {
474
505
  // ignore
475
506
  }
476
-
477
- if (this.__action_in_progress) {
478
- throw new IllegalStateException();
479
- }
480
-
481
- this.__action_in_progress = true;
482
-
483
- let p;
484
- try {
485
- p = action.revert(this.context);
486
- } finally {
487
- this.__action_in_progress = false;
488
- }
489
-
490
- if (p !== undefined) {
491
- this.__last_promise = p;
492
- await p;
493
- }
494
-
495
507
  }
496
508
  }
File without changes
@@ -0,0 +1,155 @@
1
+ import EmptyView from "../../../view/elements/EmptyView.js";
2
+ import LabelView from "../../../view/common/LabelView.js";
3
+
4
+ class FieldValueAdapter {
5
+ read(object, field_name) {
6
+
7
+ }
8
+
9
+ write(object, field_name, value) {
10
+
11
+ }
12
+ }
13
+
14
+ /**
15
+ * @template T
16
+ */
17
+ class FieldDescriptor {
18
+ constructor() {
19
+ /**
20
+ *
21
+ * @type {string}
22
+ */
23
+ this.name = "";
24
+
25
+ /**
26
+ * typeof field
27
+ * @type {Class<T>}
28
+ */
29
+ this.type = null;
30
+
31
+ /**
32
+ *
33
+ * @type {FieldValueAdapter|null}
34
+ */
35
+ this.adapter = null;
36
+
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @template T
42
+ * @param {T} value
43
+ */
44
+ function objectToClass(value) {
45
+ const type = typeof value;
46
+
47
+ switch (type) {
48
+ case "object":
49
+ return value.__ptoto__.constructor;
50
+ case "boolean":
51
+ return Boolean;
52
+ case "function":
53
+ return Function;
54
+ case "string":
55
+ return String;
56
+ case "number":
57
+ return Number;
58
+ default:
59
+ throw new Error(`Unsupported type '${type}'`);
60
+ }
61
+ }
62
+
63
+ /**
64
+ *
65
+ * @param {Object} object
66
+ * @param {string} field_name
67
+ * @returns {boolean}
68
+ */
69
+ function isPublicField(object, field_name) {
70
+ if (field_name.startsWith('_')) {
71
+ // private
72
+ return false;
73
+ }
74
+
75
+ return true;
76
+ }
77
+
78
+ /**
79
+ *
80
+ * @param {Object} object
81
+ * @returns {FieldDescriptor[]}
82
+ */
83
+ function extractObjectFields(object) {
84
+ const result = [];
85
+ const keys = Object.keys(object);
86
+
87
+ for (let i = 0; i < keys.length; i++) {
88
+ const key = keys[i];
89
+
90
+ if (!isPublicField(object, key)) {
91
+ continue;
92
+ }
93
+
94
+ const field_type = objectToClass(object[key]);
95
+
96
+ if (field_type === undefined) {
97
+ continue;
98
+ }
99
+
100
+ const descriptor = new FieldDescriptor();
101
+ descriptor.name = key;
102
+ descriptor.type = field_type;
103
+
104
+ result.push(descriptor);
105
+ }
106
+
107
+ return result;
108
+ }
109
+
110
+ /**
111
+ *
112
+ * @param {Object} object
113
+ * @param {FieldDescriptor} field
114
+ * @param {Map<*, (parent:*, field:FieldDescriptor)=>View>} registry
115
+ */
116
+ export function createFieldEditor(object, field, registry) {
117
+ const vResult = new EmptyView({});
118
+
119
+ vResult.addChild(new LabelView(field.name));
120
+
121
+ let vValue;
122
+
123
+ const factory = registry.get(field.type);
124
+
125
+ if (factory !== undefined) {
126
+ vValue = factory(object, field);
127
+ } else {
128
+ vValue = createObjectEditor(object, registry);
129
+ }
130
+
131
+ vResult.addChild(vValue);
132
+
133
+ return vResult;
134
+ }
135
+
136
+ /**
137
+ *
138
+ * @param {Object} object
139
+ * @param {Map<*, (parent:*, field:FieldDescriptor)=>View>} registry
140
+ */
141
+ export function createObjectEditor(object, registry) {
142
+ const fields = extractObjectFields(object);
143
+
144
+ const vResult = new EmptyView({});
145
+
146
+ for (let i = 0; i < fields.length; i++) {
147
+ const field = fields[i];
148
+
149
+ const vField = createFieldEditor(object, field, registry);
150
+
151
+ vResult.addChild(vField);
152
+ }
153
+
154
+ return vResult;
155
+ }
@@ -18,6 +18,7 @@ import EditorEntity from "../ecs/EditorEntity.js";
18
18
  import Terrain from "../../engine/ecs/terrain/ecs/Terrain.js";
19
19
  import { SurfacePoint3 } from "../../core/geom/3d/SurfacePoint3.js";
20
20
  import { makeScreenScissorFrustum } from "../../engine/graphics/camera/makeScreenScissorFrustum.js";
21
+ import { ShadedGeometrySystem } from "../../engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
21
22
 
22
23
  class SelectionView extends View {
23
24
  constructor() {
@@ -220,7 +221,8 @@ function findEntityOfNode(node) {
220
221
  */
221
222
  export function pickingEntitySelection(point, engine, camera) {
222
223
 
223
- const dataset = engine.entityManager.dataset;
224
+ const em = engine.entityManager;
225
+ const dataset = em.dataset;
224
226
 
225
227
  const graphicsEngine = engine.graphics;
226
228
 
@@ -262,6 +264,25 @@ export function pickingEntitySelection(point, engine, camera) {
262
264
  }
263
265
  }
264
266
 
267
+ /**
268
+ *
269
+ * @type {ShadedGeometrySystem|undefined}
270
+ */
271
+ const sgm = em.getSystem(ShadedGeometrySystem);
272
+
273
+ if (sgm !== undefined) {
274
+ const hits = sgm.raycast(
275
+ source.x, source.y, source.y,
276
+ rayDirection.x, rayDirection.y, rayDirection.z
277
+ );
278
+
279
+ for (let i = 0; i < hits.length; i++) {
280
+ const hit = hits[i];
281
+
282
+ tryAddEntity(hit.entity, hit.contact.position);
283
+ }
284
+ }
285
+
265
286
  //query render layers
266
287
  engine.graphics.layers.traverse(function (layer) {
267
288
  /**
@@ -7,13 +7,16 @@ import Quaternion from "../../../core/geom/Quaternion.js";
7
7
  import { decompose_matrix_4_array } from "../../../core/geom/3d/decompose_matrix_4_array.js";
8
8
  import { mat4 } from "gl-matrix";
9
9
  import { compose_matrix4_array } from "../../../core/geom/3d/compose_matrix4_array.js";
10
+ import { TransformFlags } from "./TransformFlags.js";
11
+ import { allocate_transform_m4 } from "../../graphics/ecs/mesh-v2/allocate_transform_m4.js";
10
12
 
11
13
  const delta = new Vector3();
12
14
 
13
- const scratch_buffer = new ArrayBuffer(32 * 4);
15
+ const scratch_buffer = new ArrayBuffer(16 * 4);
14
16
 
15
17
  const m4_0 = new Float32Array(scratch_buffer, 0, 16);
16
- const m4_1 = new Float32Array(scratch_buffer, 64, 16)
18
+
19
+ const FLAGS_DEFAULT = TransformFlags.AutomaticChangeDetection;
17
20
 
18
21
  export class Transform {
19
22
  /**
@@ -43,6 +46,79 @@ export class Transform {
43
46
  */
44
47
  this.scale = new Vector3(1, 1, 1);
45
48
 
49
+ /**
50
+ * @readonly
51
+ * @type {Float32Array}
52
+ */
53
+ this.matrix = allocate_transform_m4();
54
+
55
+ /**
56
+ * @protected
57
+ * @type {number}
58
+ */
59
+ this.flags = FLAGS_DEFAULT;
60
+
61
+ // watch changes
62
+ this.position.onChanged.add(this.__handle_component_change, this);
63
+ this.rotation.onChanged.add(this.__handle_component_change, this);
64
+ this.scale.onChanged.add(this.__handle_component_change, this);
65
+ }
66
+
67
+ /**
68
+ *
69
+ * @private
70
+ */
71
+ __handle_component_change() {
72
+ if (this.getFlag(TransformFlags.AutomaticChangeDetection)) {
73
+ this.setFlag(TransformFlags.MatrixNeedsUpdate);
74
+ }
75
+ }
76
+
77
+ /**
78
+ *
79
+ * @param {number|TransformFlags} flag
80
+ * @returns {void}
81
+ */
82
+ setFlag(flag) {
83
+ this.flags |= flag;
84
+ }
85
+
86
+ /**
87
+ *
88
+ * @param {number|TransformFlags} flag
89
+ * @returns {void}
90
+ */
91
+ clearFlag(flag) {
92
+ this.flags &= ~flag;
93
+ }
94
+
95
+ /**
96
+ *
97
+ * @param {number|TransformFlags} flag
98
+ * @param {boolean} value
99
+ */
100
+ writeFlag(flag, value) {
101
+ if (value) {
102
+ this.setFlag(flag);
103
+ } else {
104
+ this.clearFlag(flag);
105
+ }
106
+ }
107
+
108
+ /**
109
+ *
110
+ * @param {number|TransformFlags} flag
111
+ * @returns {boolean}
112
+ */
113
+ getFlag(flag) {
114
+ return (this.flags & flag) === flag;
115
+ }
116
+
117
+
118
+ updateMatrix() {
119
+ compose_matrix4_array(this.matrix, this.position, this.rotation, this.scale);
120
+
121
+ this.clearFlag(TransformFlags.MatrixNeedsUpdate);
46
122
  }
47
123
 
48
124
  /**
@@ -82,6 +158,8 @@ export class Transform {
82
158
  } else {
83
159
  this.scale.copy(Vector3.one);
84
160
  }
161
+
162
+ this.setFlag(TransformFlags.MatrixNeedsUpdate);
85
163
  }
86
164
 
87
165
  toJSON() {
@@ -100,6 +178,10 @@ export class Transform {
100
178
  this.position.copy(other.position);
101
179
  this.rotation.copy(other.rotation);
102
180
  this.scale.copy(other.scale);
181
+
182
+ this.matrix.set(other.matrix);
183
+
184
+ this.flags = other.flags;
103
185
  }
104
186
 
105
187
  /**
@@ -154,10 +236,15 @@ export class Transform {
154
236
  * @param {Transform} b
155
237
  */
156
238
  multiplyTransforms(a, b) {
157
- compose_matrix4_array(m4_0, a.position, a.rotation, a.scale);
158
- compose_matrix4_array(m4_1, b.position, b.rotation, b.scale);
239
+ if (a.getFlag(TransformFlags.MatrixNeedsUpdate)) {
240
+ a.updateMatrix();
241
+ }
159
242
 
160
- mat4.multiply(m4_0, m4_0, m4_1);
243
+ if (b.getFlag(TransformFlags.MatrixNeedsUpdate)) {
244
+ b.updateMatrix();
245
+ }
246
+
247
+ mat4.multiply(m4_0, a.matrix, b.matrix);
161
248
 
162
249
  this.fromMatrix4(m4_0);
163
250
  }
@@ -183,7 +270,17 @@ export class Transform {
183
270
  * @param {mat4|number[]|Float32Array} m
184
271
  */
185
272
  fromMatrix4(m) {
273
+ const ad = this.getFlag(TransformFlags.AutomaticChangeDetection);
274
+
275
+ this.clearFlag(TransformFlags.AutomaticChangeDetection);
276
+
277
+ this.matrix.set(m);
278
+
186
279
  decompose_matrix_4_array(m, this.position, this.rotation, this.scale);
280
+
281
+ this.clearFlag(TransformFlags.MatrixNeedsUpdate);
282
+
283
+ this.writeFlag(TransformFlags.AutomaticChangeDetection, ad);
187
284
  }
188
285
 
189
286
  /**
@@ -195,6 +292,10 @@ export class Transform {
195
292
  }
196
293
  }
197
294
 
295
+ /**
296
+ * @readonly
297
+ * @type {string}
298
+ */
198
299
  Transform.typeName = "Transform";
199
300
 
200
301
  /**
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @readonly
3
+ * @enum {number}
4
+ */
5
+ export const TransformFlags = {
6
+ MatrixNeedsUpdate: 1,
7
+ AutomaticChangeDetection: 2
8
+ };
@@ -2,16 +2,6 @@ import { System } from "../System.js";
2
2
  import { TransformAttachment, TransformAttachmentFlags } from "./TransformAttachment.js";
3
3
  import { Transform } from "../transform/Transform.js";
4
4
 
5
- /**
6
- *
7
- * @param {Transform} parent_transform
8
- * @param {Transform} attachment_transform
9
- * @param {Transform} child_transform
10
- */
11
- function update(parent_transform, attachment_transform, child_transform) {
12
- child_transform.multiplyTransforms(parent_transform, attachment_transform);
13
- }
14
-
15
5
  class UpdateContext {
16
6
  constructor() {
17
7
  /**
@@ -40,7 +30,7 @@ class UpdateContext {
40
30
  }
41
31
 
42
32
  update() {
43
- update(this.parent_transform, this.attachment.transform, this.transform);
33
+ this.transform.multiplyTransforms(this.parent_transform, this.attachment.transform);
44
34
  }
45
35
 
46
36
  link() {
@@ -3,6 +3,8 @@ import { AABB3 } from "../../../../core/bvh2/aabb3/AABB3.js";
3
3
  import { DrawMode } from "./DrawMode.js";
4
4
  import { ShadedGeometryFlags } from "./ShadedGeometryFlags.js";
5
5
  import { aabb3_matrix4_project } from "../../../../core/geom/3d/aabb/aabb3_matrix4_project.js";
6
+ import { allocate_transform_m4 } from "./allocate_transform_m4.js";
7
+ import { TransformFlags } from "../../../ecs/transform/TransformFlags.js";
6
8
 
7
9
  /**
8
10
  * @readonly
@@ -18,39 +20,6 @@ const FLAG_SET_EQUALITY = ShadedGeometryFlags.CastShadow
18
20
  | ShadedGeometryFlags.ReceiveShadow
19
21
  ;
20
22
 
21
- /**
22
- *
23
- * @type {number}
24
- */
25
- const TRANSFORM_ALLOCATOR_BUCKET_CAPACITY = 16;
26
- /**
27
- *
28
- * @type {ArrayBuffer|null}
29
- */
30
- let transform_bucket = null;
31
- /**
32
- *
33
- * @type {number}
34
- */
35
- let transform_bucket_cursor = TRANSFORM_ALLOCATOR_BUCKET_CAPACITY;
36
-
37
- /**
38
- * custom Float32Array allocator, allocated memory in continuous chunks
39
- * @returns {Float32Array}
40
- */
41
- function allocate_transform_m4() {
42
- if (transform_bucket_cursor >= TRANSFORM_ALLOCATOR_BUCKET_CAPACITY) {
43
- transform_bucket = new ArrayBuffer(TRANSFORM_ALLOCATOR_BUCKET_CAPACITY * 4 * 16);
44
- transform_bucket_cursor = 0;
45
- }
46
-
47
- const result = new Float32Array(transform_bucket, transform_bucket_cursor * 16 * 4, 16);
48
-
49
- transform_bucket_cursor++;
50
-
51
- return result;
52
- }
53
-
54
23
  export class ShadedGeometry {
55
24
  constructor() {
56
25
  /**
@@ -216,7 +185,7 @@ export class ShadedGeometry {
216
185
  * @return {null|number[]}
217
186
  */
218
187
  get transform() {
219
- return this.__transform;
188
+ return this.__c_transform.matrix;
220
189
  }
221
190
 
222
191
  /**
@@ -270,8 +239,14 @@ export class ShadedGeometry {
270
239
  }
271
240
 
272
241
  updateTransform() {
242
+ const t = this.__c_transform;
243
+
244
+ if (t.getFlag(TransformFlags.MatrixNeedsUpdate)) {
245
+ t.updateMatrix();
246
+ }
247
+
273
248
  //
274
- this.__c_transform.toMatrix4(this.__transform);
249
+ this.__transform.set(t.matrix);
275
250
 
276
251
  this.update_bounds();
277
252
 
@@ -22,6 +22,7 @@ import TaskState from "../../../../core/process/task/TaskState.js";
22
22
  import {
23
23
  bvh_query_user_data_overlaps_frustum
24
24
  } from "../../../../core/bvh2/bvh3/bvh_query_user_data_overlaps_frustum.js";
25
+ import { TransformFlags } from "../../../ecs/transform/TransformFlags.js";
25
26
 
26
27
 
27
28
  /**
@@ -250,7 +251,11 @@ export class ShadedGeometrySystem extends System {
250
251
  t.rotation.onChanged.add(sg.updateTransform, sg);
251
252
  t.scale.onChanged.add(sg.updateTransform, sg);
252
253
 
253
- t.toMatrix4(sg.__transform);
254
+ if (t.getFlag(TransformFlags.MatrixNeedsUpdate)) {
255
+ t.updateMatrix();
256
+ }
257
+
258
+ sg.__transform.set(t.matrix);
254
259
  sg.update_bounds();
255
260
 
256
261
  // remember entity for lookups
@@ -7,6 +7,7 @@ import { ShadedGeometry } from "../ShadedGeometry.js";
7
7
  import { ShadedGeometryFlags } from "../ShadedGeometryFlags.js";
8
8
  import { SGMeshEvents } from "./SGMeshEvents.js";
9
9
  import { ParentEntity } from "../../../../ecs/parent/ParentEntity.js";
10
+ import { EntityNode } from "../../../../ecs/parent/EntityNode.js";
10
11
 
11
12
 
12
13
  /**
@@ -76,7 +77,21 @@ export class SGMeshSystem extends System {
76
77
 
77
78
  // TODO check that entity still exists, as we get to this point asynchronously
78
79
 
79
- const entity_node = three_object_to_entity_composition(asset.create());
80
+ let entity_node = three_object_to_entity_composition(asset.create());
81
+
82
+ if (asset.has_root_transform) {
83
+ // apply root transform
84
+
85
+ const new_root = new EntityNode();
86
+
87
+ new_root.entity.add(new Transform());
88
+
89
+ entity_node.transform.copy(asset.root_transform);
90
+
91
+ new_root.addChild(entity_node);
92
+
93
+ entity_node = new_root;
94
+ }
80
95
 
81
96
  // attach parent entity to link hierarchy to this entity
82
97
  entity_node.entity.add(ParentEntity.from(entity));
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Larger bucket sizes provide better cache locality
3
+ * Larger bucket sizes also cause more hanging GC references, as if one matrix element is still held - ENTIRE bucket is still referenced
4
+ * @type {number}
5
+ */
6
+ const TRANSFORM_ALLOCATOR_BUCKET_CAPACITY = 16;
7
+
8
+ /**
9
+ *
10
+ * @type {ArrayBuffer|null}
11
+ */
12
+ let transform_bucket = null;
13
+
14
+ /**
15
+ *
16
+ * @type {number}
17
+ */
18
+ let transform_bucket_cursor = TRANSFORM_ALLOCATOR_BUCKET_CAPACITY;
19
+
20
+ const MATRIX_BYTE_SIZE = 4 * 16;
21
+
22
+ /**
23
+ *
24
+ * @type {Float32Array}
25
+ */
26
+ const MATRIX_IDENTITY = new Float32Array([
27
+ 1, 0, 0, 0,
28
+ 0, 1, 0, 0,
29
+ 0, 0, 1, 0,
30
+ 0, 0, 0, 1
31
+ ]);
32
+
33
+ /**
34
+ * custom Float32Array allocator, allocated memory in continuous chunks
35
+ * @returns {Float32Array}
36
+ */
37
+ export function allocate_transform_m4() {
38
+ if (transform_bucket_cursor >= TRANSFORM_ALLOCATOR_BUCKET_CAPACITY) {
39
+ transform_bucket = new ArrayBuffer(TRANSFORM_ALLOCATOR_BUCKET_CAPACITY * MATRIX_BYTE_SIZE);
40
+ transform_bucket_cursor = 0;
41
+ }
42
+
43
+ const result = new Float32Array(transform_bucket, transform_bucket_cursor * MATRIX_BYTE_SIZE, 16);
44
+
45
+ // initialize matrix
46
+ result.set(MATRIX_IDENTITY);
47
+
48
+ transform_bucket_cursor++;
49
+
50
+ return result;
51
+ }
@@ -4,10 +4,11 @@ import { GameAssetType } from "../../../../asset/GameAssetType.js";
4
4
  import { EngineConfiguration } from "../../../../EngineConfiguration.js";
5
5
  import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
6
6
  import { three_object_to_entity_composition } from "../three_object_to_entity_composition.js";
7
- import { AttachmentSystem } from "../../../../ecs/attachment/AttachmentSystem.js";
8
7
  import { TransformAttachmentSystem } from "../../../../ecs/transform-attachment/TransformAttachmentSystem.js";
9
8
  import { Camera } from "../../camera/Camera.js";
10
9
  import { MicronRenderPlugin } from "../../../micron/plugin/MicronRenderPlugin.js";
10
+ import { enableEditor } from "../../../../../editor/enableEditor.js";
11
+ import { MeshSystem } from "../../mesh/MeshSystem.js";
11
12
 
12
13
  const eh = new EngineHarness();
13
14
 
@@ -22,14 +23,20 @@ async function init(harness) {
22
23
  const config = new EngineConfiguration();
23
24
 
24
25
  config.addSystem(new ShadedGeometrySystem(engine));
25
- config.addSystem(new AttachmentSystem());
26
26
  config.addSystem(new TransformAttachmentSystem());
27
- config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
27
+ config.addSystem(new MeshSystem(engine));
28
28
 
29
- config.addPlugin( MicronRenderPlugin);
29
+ const gltfAssetLoader = new GLTFAssetLoader();
30
+
31
+ config.addLoader(GameAssetType.ModelGLTF_JSON, gltfAssetLoader);
32
+ config.addLoader(GameAssetType.ModelGLTF, gltfAssetLoader);
33
+
34
+ config.addPlugin(MicronRenderPlugin);
30
35
 
31
36
  await config.apply(engine);
32
37
 
38
+ enableEditor(engine);
39
+
33
40
  eh.initialize().then(main);
34
41
  }
35
42
 
@@ -51,12 +58,32 @@ async function load_gltf(path, engine) {
51
58
  const composition = three_object_to_entity_composition(three_object);
52
59
  console.timeEnd('composition conversion');
53
60
 
61
+ if (asset.has_root_transform) {
62
+ composition.transform.multiplyTransforms(composition.transform, asset.root_transform);
63
+ }
64
+
54
65
  console.time('composition build');
55
66
  // console.profile('composition build');
56
67
  composition.build(ecd);
57
68
  // console.profileEnd('composition build');
58
69
  console.timeEnd('composition build');
59
70
 
71
+
72
+ // console.log(composition);
73
+ // console.log(three_object);
74
+ //
75
+ // new EntityBuilder()
76
+ // .add(Mesh.fromJSON({
77
+ // url: path
78
+ // }))
79
+ // .add(Transform.fromJSON({
80
+ // position: {
81
+ // x: 5,
82
+ // y: 0,
83
+ // z: 0
84
+ // }
85
+ // }))
86
+ // .build(ecd);
60
87
  }
61
88
 
62
89
  async function timeout(t = 1) {
@@ -88,6 +115,16 @@ async function main(engine) {
88
115
  // load_gltf('moicon/06_04_hapro/05-ground-floor/model.gltf', engine);
89
116
  // load_gltf('moicon/06_04_hapro/04-1st-floor/model.gltf', engine);
90
117
  // load_gltf('moicon/06_04_hapro/03-2nd-floor/model.gltf', engine);
118
+
119
+ // for (let i = 1; i <= 6; i++) {
120
+ // load_gltf(`moicon/10_Feb_22_Demo/${i}/model.gltf`, engine);
121
+ // }
122
+ // load_gltf(`moicon/10_Feb_22_Demo/1/model.gltf`, engine);
123
+ // load_gltf(`moicon/10_Feb_22_Demo/2/model.gltf`, engine);
124
+ // load_gltf(`moicon/10_Feb_22_Demo/3/model.gltf`, engine);
125
+
126
+ // load_gltf(`data/models/samples/transform-hierarchy.glb`, engine);
127
+
91
128
  }
92
129
 
93
130
  init(eh);
@@ -15,8 +15,10 @@ export function three_object_to_entity_composition(root) {
15
15
  // copy object transform
16
16
  node_transform.position.copy(root.position);
17
17
  node_transform.scale.copy(root.scale);
18
- const euler = root.rotation;
19
- node_transform.rotation.fromEulerAngles(euler.x, euler.y, euler.z);
18
+ node_transform.rotation.copy(root.quaternion);
19
+
20
+ //
21
+ node_transform.matrix.set(root.matrix.elements);
20
22
 
21
23
  const entity = node.entity;
22
24
 
@@ -1,4 +1,4 @@
1
- import {ClassRegistry} from "../../../../../core/model/ClassRegistry";
1
+ import {ModuleRegistry} from "../../../../../core/model/ModuleRegistry";
2
2
  import {EntityPathMarkerDefinition} from "./EntityPathMarkerDefinition";
3
3
 
4
4
  export class EntityPathStyle {
@@ -8,5 +8,5 @@ export class EntityPathStyle {
8
8
 
9
9
  public spacing: number
10
10
 
11
- fromJSON(json: { marker_start: any, marker_end: any, marker_main: any, spacing?: number }, registry: ClassRegistry): void
11
+ fromJSON(json: { marker_start: any, marker_end: any, marker_main: any, spacing?: number }, registry: ModuleRegistry): void
12
12
  }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.37.1",
8
+ "version": "2.37.4",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",
@@ -1,3 +0,0 @@
1
- export class ClassRegistry {
2
-
3
- }