@woosh/meep-engine 2.35.10 → 2.35.14

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 (22) hide show
  1. package/core/binary/BinaryBuffer.js +65 -18
  2. package/engine/graphics/ecs/camera/Camera.js +1 -1
  3. package/engine/graphics/ecs/camera/CameraSystem.js +2 -2
  4. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +38 -3
  5. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +19 -4
  6. package/engine/graphics/ecs/path/testPathDisplaySystem.js +6 -0
  7. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +1 -0
  8. package/engine/graphics/ecs/path/tube/TubePathStyle.js +29 -2
  9. package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +42 -21
  10. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +41 -12
  11. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +43 -3
  12. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +28 -16
  13. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +11 -6
  14. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -1
  15. package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +45 -3
  16. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +1 -1
  17. package/engine/graphics/micron/render/v1/MaterialContext.js +1 -11
  18. package/engine/graphics/micron/render/v1/MaterialVertexSpec.js +55 -2
  19. package/engine/graphics/micron/render/v1/MicronRenderContext.js +25 -13
  20. package/engine/graphics/micron/render/v1/VGThreeRenderer.js +13 -1
  21. package/engine/navigation/ecs/components/Path.js +5 -6
  22. package/package.json +1 -1
@@ -3,6 +3,7 @@
3
3
  * @enum {boolean}
4
4
  */
5
5
  import { assert } from "../assert.js";
6
+ import { array_copy } from "../collection/array/copyArray.js";
6
7
 
7
8
  /**
8
9
  *
@@ -30,17 +31,50 @@ function BinaryBuffer() {
30
31
  */
31
32
  this.endianness = EndianType.BigEndian;
32
33
 
34
+ /**
35
+ *
36
+ * @type {number}
37
+ */
33
38
  this.position = 0;
39
+
40
+ /**
41
+ *
42
+ * @type {number}
43
+ */
34
44
  this.length = 0;
45
+
46
+ /**
47
+ *
48
+ * @type {number}
49
+ */
35
50
  this.capacity = 0;
36
51
 
37
52
  /**
38
53
  *
39
54
  * @type {ArrayBuffer}
55
+ * @private
40
56
  */
41
57
  this.data = new ArrayBuffer(0);
58
+
59
+ /**
60
+ *
61
+ * @type {DataView}
62
+ * @private
63
+ */
42
64
  this.dataView = new DataView(this.data);
43
65
 
66
+ /**
67
+ *
68
+ * @type {Uint8Array}
69
+ * @private
70
+ */
71
+ this.__data_uint8 = new Uint8Array(0);
72
+
73
+ /**
74
+ *
75
+ * @type {number}
76
+ * @private
77
+ */
44
78
  this.__growFactor = 1.1;
45
79
  }
46
80
 
@@ -50,6 +84,7 @@ function BinaryBuffer() {
50
84
  BinaryBuffer.prototype.fromArrayBuffer = function (arrayBuffer) {
51
85
  this.data = arrayBuffer;
52
86
  this.dataView = new DataView(arrayBuffer);
87
+ this.__data_uint8 = new Uint8Array(arrayBuffer);
53
88
 
54
89
  this.capacity = arrayBuffer.byteLength;
55
90
  //assume that all the data is useful
@@ -85,7 +120,7 @@ BinaryBuffer.prototype.trim = function () {
85
120
  * @param {number} capacity
86
121
  */
87
122
  BinaryBuffer.prototype.setCapacity = function (capacity) {
88
- const oldData = new Uint8Array(this.data);
123
+ const oldData = this.__data_uint8;
89
124
  const newData = new Uint8Array(capacity);
90
125
 
91
126
  //copy old data
@@ -93,7 +128,7 @@ BinaryBuffer.prototype.setCapacity = function (capacity) {
93
128
  newData.set(oldData.subarray(0, copyLength), 0);
94
129
 
95
130
  this.data = newData.buffer;
96
-
131
+ this.__data_uint8 = newData;
97
132
  this.dataView = new DataView(this.data);
98
133
 
99
134
  this.capacity = capacity;
@@ -680,11 +715,11 @@ BinaryBuffer.prototype.writeUint32LE = function (value) {
680
715
  /**
681
716
  *
682
717
  * @param {Uint8Array|Uint8ClampedArray} array
683
- * @param {number} sourceOffset
718
+ * @param {number} source_offset
684
719
  * @param {number} length
685
720
  */
686
- BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
687
- const source_end = sourceOffset + length;
721
+ BinaryBuffer.prototype.writeBytes = function (array, source_offset, length) {
722
+ const source_end = source_offset + length;
688
723
  assert.greaterThanOrEqual(array.length, source_end, 'source array underflow');
689
724
 
690
725
  const targetAddress = this.position;
@@ -693,18 +728,21 @@ BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
693
728
 
694
729
  this.ensureCapacity(end);
695
730
 
696
- const destination = new Uint8Array(this.data, targetAddress, length);
731
+ if (source_offset === 0 && array.length === length) {
732
+ // copying entire source array
697
733
 
698
- if (typeof array.subarray === "function") {
734
+ this.__data_uint8.set(array, targetAddress);
735
+
736
+ } else if (typeof array.subarray === "function") {
699
737
  // typed array, use "subarray" method
700
738
 
701
- destination.set(array.subarray(sourceOffset, source_end));
739
+ this.__data_uint8.set(array.subarray(source_offset, source_end), targetAddress);
702
740
 
703
741
  } else {
704
742
  // not a typed array, copy byte by byte manually
705
743
 
706
744
  for (let i = 0; i < length; i++) {
707
- destination[i] = array[sourceOffset + i];
745
+ this.__data_uint8[targetAddress + i] = array[source_offset + i];
708
746
  }
709
747
 
710
748
  }
@@ -714,14 +752,23 @@ BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
714
752
 
715
753
  /**
716
754
  *
717
- * @param {Uint8Array} target
718
- * @param {number} targetOffset
755
+ * @param {Uint8Array} destination
756
+ * @param {number} destination_offset
719
757
  * @param {number} length
720
758
  */
721
- BinaryBuffer.prototype.readBytes = function (target, targetOffset, length) {
722
- const end = this.position + length;
723
- const uint8Array = new Uint8Array(this.data);
724
- target.set(uint8Array.subarray(this.position, end), targetOffset);
759
+ BinaryBuffer.prototype.readBytes = function (destination, destination_offset, length) {
760
+ const source_position = this.position;
761
+
762
+ const end = source_position + length;
763
+
764
+ const uint8 = this.__data_uint8;
765
+
766
+ if (length < 128) {
767
+ // small copy
768
+ array_copy(uint8, source_position, destination, destination_offset, length);
769
+ } else {
770
+ destination.set(uint8.subarray(source_position, end), destination_offset);
771
+ }
725
772
 
726
773
  this.position = end;
727
774
  };
@@ -764,7 +811,7 @@ BinaryBuffer.prototype.writeUTF8String = function (string) {
764
811
 
765
812
  this.ensureCapacity(tlen + at);
766
813
 
767
- let target = new Uint8Array(this.data); // ... but at 8 byte offset
814
+ let target = this.__data_uint8; // ... but at 8 byte offset
768
815
 
769
816
 
770
817
  while (pos < len) {
@@ -791,7 +838,7 @@ BinaryBuffer.prototype.writeUTF8String = function (string) {
791
838
 
792
839
  this.ensureCapacity(tlen + startPosition);
793
840
 
794
- target = new Uint8Array(this.data);
841
+ target = this.__data_uint8;
795
842
  }
796
843
 
797
844
  if ((value & 0xffffff80) === 0) { // 1-byte
@@ -851,7 +898,7 @@ BinaryBuffer.prototype.readUTF8String = function () {
851
898
  return undefined;
852
899
  }
853
900
 
854
- const bytes = new Uint8Array(this.data);
901
+ const bytes = this.__data_uint8;
855
902
 
856
903
 
857
904
  let result = "";
@@ -62,7 +62,7 @@ export class Camera {
62
62
  * @type {number}
63
63
  * @private
64
64
  */
65
- this.__clip_far = 0;
65
+ this.__clip_far = 100;
66
66
 
67
67
  /**
68
68
  * Far clipping plane
@@ -297,7 +297,7 @@ export class CameraSystem extends System {
297
297
  * @param {Camera} c
298
298
  */
299
299
  function visitCameraEntity(c) {
300
- if (c.object !== null) {
300
+ if (c.object === null) {
301
301
  return;
302
302
  }
303
303
 
@@ -305,7 +305,7 @@ export class CameraSystem extends System {
305
305
  return;
306
306
  }
307
307
 
308
- if (c.autoClip) {
308
+ if (c.autoClip === true) {
309
309
  autoSetClippingPlanes(c, layers);
310
310
  } else {
311
311
  c.object.far = c.clip_far;
@@ -74,6 +74,13 @@ export class ShadedGeometrySystem extends System {
74
74
  * @private
75
75
  */
76
76
  this.__adapter_suppliers = new Map();
77
+
78
+ /**
79
+ * Keeps count of usage for each geometry by ID
80
+ * @type {Map<number, number>}
81
+ * @private
82
+ */
83
+ this.__geometry_usage_counters = new Map();
77
84
  }
78
85
 
79
86
  /**
@@ -166,7 +173,7 @@ export class ShadedGeometrySystem extends System {
166
173
  this.__render_layer.buildVisibleSet = ((destination, destination_offset, view) => {
167
174
  const ctx = this.__view_contexts.get(view);
168
175
 
169
- return ctx.collect(destination, destination_offset,graphics.renderer, view, this.__bvh);
176
+ return ctx.collect(destination, destination_offset, graphics.renderer, view, this.__bvh);
170
177
  });
171
178
 
172
179
  readyCallback();
@@ -210,6 +217,13 @@ export class ShadedGeometrySystem extends System {
210
217
  sg.__entity = entity;
211
218
 
212
219
  this.__bvh.insertNode(leaf_node);
220
+
221
+ // update usage count
222
+ const geometry_id = sg.geometry.id;
223
+ const count_existing = this.__geometry_usage_counters.get(geometry_id);
224
+ const count_new = count_existing !== undefined ? count_existing + 1 : 1;
225
+
226
+ this.__geometry_usage_counters.set(geometry_id, count_new);
213
227
  }
214
228
 
215
229
  /**
@@ -224,6 +238,21 @@ export class ShadedGeometrySystem extends System {
224
238
  t.scale.onChanged.remove(sg.updateTransform, sg);
225
239
 
226
240
  sg.bvh.disconnect();
241
+
242
+ const geometry_id = sg.geometry.id;
243
+ const count_existing = this.__geometry_usage_counters.get(geometry_id);
244
+
245
+ if (count_existing === 1) {
246
+
247
+ // last instance
248
+ this.__geometry_usage_counters.delete(geometry_id);
249
+
250
+ // release GPU memory
251
+ sg.geometry.dispose();
252
+
253
+ } else {
254
+ this.__geometry_usage_counters.set(geometry_id, count_existing - 1);
255
+ }
227
256
  }
228
257
 
229
258
  /**
@@ -313,13 +342,19 @@ export class ShadedGeometrySystem extends System {
313
342
  const local_direction_x = scratch_m4[0] * direction_x + scratch_m4[4] * direction_y + scratch_m4[8] * direction_z;
314
343
  const local_direction_y = scratch_m4[1] * direction_x + scratch_m4[5] * direction_y + scratch_m4[9] * direction_z;
315
344
  const local_direction_z = scratch_m4[2] * direction_x + scratch_m4[6] * direction_y + scratch_m4[10] * direction_z;
316
- // NOTE: direction is not normalized
345
+
346
+ const local_direction_magnitude = Math.hypot(local_direction_x, local_direction_y, local_direction_z);
347
+ const local_direction_magnitude_inverse = 1 / local_direction_magnitude;
348
+
349
+ const local_direction_normalized_x = local_direction_x * local_direction_magnitude_inverse;
350
+ const local_direction_normalized_y = local_direction_y * local_direction_magnitude_inverse;
351
+ const local_direction_normalized_z = local_direction_z * local_direction_magnitude_inverse;
317
352
 
318
353
  const geometry_hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_primitive(
319
354
  scratch_point,
320
355
  sg.geometry,
321
356
  local_origin_x, local_origin_y, local_origin_z,
322
- local_direction_x, local_direction_y, local_direction_z
357
+ local_direction_normalized_x, local_direction_normalized_y, local_direction_normalized_z
323
358
  );
324
359
 
325
360
  if (!geometry_hit_found) {
@@ -20,6 +20,9 @@ import { randomFromArray } from "../../../../../core/math/random/randomFromArray
20
20
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
21
21
  import { OrbitingBehavior } from "../../../../../../model/game/util/behavior/OrbitingBehavior.js";
22
22
  import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
23
+ import { MicronShadedGeometryRenderAdapter } from "../../../micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js";
24
+ import { MicronRenderContext } from "../../../micron/render/v1/MicronRenderContext.js";
25
+ import { randomIntegerBetween } from "../../../../../core/math/random/randomIntegerBetween.js";
23
26
 
24
27
  const eh = new EngineHarness();
25
28
 
@@ -50,7 +53,17 @@ async function main(engine) {
50
53
 
51
54
  const em = engine.entityManager;
52
55
 
53
- await em.addSystem(new ShadedGeometrySystem(engine));
56
+ const sgs = new ShadedGeometrySystem(engine);
57
+
58
+ const mrc = new MicronRenderContext();
59
+ sgs.register_render_adapter(2, () => {
60
+ const adapter = new MicronShadedGeometryRenderAdapter();
61
+ adapter.ctx = mrc;
62
+
63
+ return adapter;
64
+ });
65
+
66
+ await em.addSystem(sgs);
54
67
  await em.addSystem(new BehaviorSystem(engine));
55
68
 
56
69
  EngineHarness.buildBasics({
@@ -74,7 +87,7 @@ async function main(engine) {
74
87
 
75
88
  const random = seededRandom(0);
76
89
 
77
- const INSTANCE_COUNT = 0;
90
+ const INSTANCE_COUNT = 10000;
78
91
 
79
92
  const geometries = [];
80
93
 
@@ -87,7 +100,9 @@ async function main(engine) {
87
100
  const sg = ShadedGeometry.from(geometry, material);
88
101
 
89
102
  // sg.draw_method = DRAW_METHOD_PLAIN;
90
- sg.draw_method = DRAW_METHOD_INSTANCED;
103
+ // sg.draw_method = DRAW_METHOD_INSTANCED;
104
+ // sg.draw_method = 2;
105
+ sg.draw_method = randomIntegerBetween(random, 0, 2);
91
106
  // sg.draw_method = random() > 0.01 ? DRAW_METHOD_INSTANCED : DRAW_METHOD_PLAIN;
92
107
  sg.clearFlag(ShadedGeometryFlags.CastShadow | ShadedGeometryFlags.ReceiveShadow);
93
108
 
@@ -105,7 +120,7 @@ async function main(engine) {
105
120
  .build(ecd);
106
121
  }
107
122
 
108
- makeMovingShadedGeometry().build(ecd);
123
+ // makeMovingShadedGeometry().build(ecd);
109
124
 
110
125
  debug_button(engine, () => {
111
126
  const dot = convert_bvh_to_dot_format_string(engine.graphics.layers.getLayerByName('shaded-geometry').bvh);
@@ -316,6 +316,12 @@ function sample_style_tube() {
316
316
 
317
317
  style.width = 0.1;
318
318
 
319
+ style.path_mask = [
320
+ 0.1, 0.12,
321
+ 0.3, 0.8,
322
+ 0.9, 1
323
+ ];
324
+
319
325
  return style;
320
326
  }
321
327
 
@@ -13,4 +13,5 @@ export class TubePathStyle {
13
13
  public radial_resolution: number
14
14
  public cast_shadow: boolean
15
15
  public receive_shadow: boolean
16
+ public path_mask: number[]
16
17
  }
@@ -3,6 +3,8 @@ import { TubeMaterialType } from "./TubeMaterialType.js";
3
3
  import { assert } from "../../../../../core/assert.js";
4
4
  import { MatcapMaterialDefinition } from "./MatcapMaterialDefinition.js";
5
5
 
6
+ const DEFAULT_COLOR = Object.freeze({ r: 1, g: 1, b: 1 });
7
+
6
8
  export class TubePathStyle {
7
9
  constructor() {
8
10
  /**
@@ -59,6 +61,16 @@ export class TubePathStyle {
59
61
  * @type {boolean}
60
62
  */
61
63
  this.receive_shadow = false;
64
+
65
+ /**
66
+ * Which parts of the path to visualise, in normalized offsets
67
+ * @example [ 0, 1 ] = whole path
68
+ * @example [ 0, 0.5 ] = first half of the path
69
+ * @example [ 0.5, 0.6 ] = segment from 50% along the path, to 60% along the path
70
+ * @example [ 0, 0.1, 0.9, 1] = first 10% of the path, and last 10% of the path
71
+ * @type {number[]}
72
+ */
73
+ this.path_mask = [0, 1];
62
74
  }
63
75
 
64
76
  static fromJSON(j) {
@@ -70,7 +82,7 @@ export class TubePathStyle {
70
82
  }
71
83
 
72
84
  fromJSON({
73
- color,
85
+ color = DEFAULT_COLOR,
74
86
  material_type,
75
87
  material,
76
88
  opacity = 1,
@@ -78,9 +90,22 @@ export class TubePathStyle {
78
90
  resolution = 10,
79
91
  radial_resolution = 16,
80
92
  cast_shadow = false,
81
- receive_shadow = false
93
+ receive_shadow = false,
94
+ path_mask = [0, 1]
82
95
  }) {
83
96
  assert.enum(material_type, TubeMaterialType, 'material_type');
97
+ assert.isNumber(opacity, 'opacity');
98
+ assert.isNumber(width, 'width');
99
+ assert.isNumber(resolution, 'resolution');
100
+ assert.isNumber(radial_resolution, 'radial_resolution');
101
+ assert.isBoolean(cast_shadow, 'cast_shadow');
102
+ assert.isBoolean(receive_shadow, 'receive_shadow');
103
+
104
+ assert.defined(path_mask, 'path_mask');
105
+ assert.notNull(path_mask, 'path_mask');
106
+
107
+ assert.greaterThanOrEqual(path_mask.length, 2, 'path_mask length bust be at least 2');
108
+ assert.ok(path_mask.length % 2 === 0, 'path_mask length bust be multiple of 2');
84
109
 
85
110
  if (typeof color === 'string') {
86
111
  this.color.parse(color);
@@ -97,6 +122,8 @@ export class TubePathStyle {
97
122
  this.cast_shadow = cast_shadow;
98
123
  this.receive_shadow = receive_shadow;
99
124
 
125
+ this.path_mask = path_mask;
126
+
100
127
  switch (material_type) {
101
128
  case TubeMaterialType.Matcap:
102
129
  this.material = new MatcapMaterialDefinition();
@@ -9,6 +9,28 @@ import { build_geometry_catmullrom } from "./build_geometry_catmullrom.js";
9
9
  import { ShadedGeometry } from "../../../mesh-v2/ShadedGeometry.js";
10
10
  import { ShadedGeometryFlags } from "../../../mesh-v2/ShadedGeometryFlags.js";
11
11
 
12
+ /**
13
+ *
14
+ * @param {number} segment_start
15
+ * @param {number} segment_end
16
+ * @returns {BufferGeometry}
17
+ * @param {Path} path_component
18
+ * @param {TubePathStyle} style
19
+ */
20
+ function make_geometry_segment(path_component, style, segment_start, segment_end) {
21
+
22
+ const interpolation = path_component.interpolation;
23
+
24
+ if (interpolation === InterpolationType.Linear) {
25
+ return build_geometry_linear(path_component, style, segment_start, segment_end);
26
+ } else if (interpolation === InterpolationType.CatmullRom) {
27
+ return build_geometry_catmullrom(path_component, style, segment_start, segment_end);
28
+ } else {
29
+ throw new Error(`Unsupported interpolation type '${interpolation}'`);
30
+ }
31
+
32
+ }
33
+
12
34
  export class TubePathBuilder {
13
35
  constructor() {
14
36
  /**
@@ -80,20 +102,6 @@ export class TubePathBuilder {
80
102
  }
81
103
 
82
104
 
83
- /**
84
- * @type {THREE.BufferGeometry}
85
- */
86
- let geometry;
87
-
88
- if (path_component.interpolation === InterpolationType.Linear) {
89
- geometry = build_geometry_linear(three_points, pointCount, style);
90
- } else if (path_component.interpolation === InterpolationType.CatmullRom) {
91
- geometry = build_geometry_catmullrom(three_points, pointCount, style);
92
- } else {
93
- throw new Error(`Unsupported interpolation type '${path_component.interpolation}'`);
94
- }
95
-
96
-
97
105
  let material_def;
98
106
 
99
107
  if (style.material_type === TubeMaterialType.Standard) {
@@ -122,17 +130,30 @@ export class TubePathBuilder {
122
130
  material_def.opacity = style.opacity;
123
131
  }
124
132
 
125
- const entityBuilder = new EntityBuilder();
133
+ const segment_count = style.path_mask.length / 2;
134
+
135
+ for (let i = 0; i < segment_count; i++) {
136
+ const i2 = i * 2;
137
+
138
+ const segment_start = style.path_mask[i2];
139
+ const segment_end = style.path_mask[i2 + 1];
140
+
141
+ const geometry = make_geometry_segment(path_component, style, segment_start, segment_end);
126
142
 
127
- entityBuilder.add(new Transform());
143
+ const entityBuilder = new EntityBuilder();
128
144
 
129
- const shaded_geometry = ShadedGeometry.from(geometry, material_def);
145
+ entityBuilder.add(new Transform());
130
146
 
131
- shaded_geometry.writeFlag(ShadedGeometryFlags.CastShadow, style.cast_shadow);
132
- shaded_geometry.writeFlag(ShadedGeometryFlags.ReceiveShadow, style.receive_shadow);
147
+ const shaded_geometry = ShadedGeometry.from(geometry, material_def);
133
148
 
134
- entityBuilder.add(shaded_geometry);
149
+ shaded_geometry.writeFlag(ShadedGeometryFlags.CastShadow, style.cast_shadow);
150
+ shaded_geometry.writeFlag(ShadedGeometryFlags.ReceiveShadow, style.receive_shadow);
151
+
152
+ entityBuilder.add(shaded_geometry);
153
+
154
+ destination.push(entityBuilder);
155
+
156
+ }
135
157
 
136
- destination.push(entityBuilder);
137
158
  }
138
159
  }
@@ -1,32 +1,61 @@
1
- import { CatmullRomCurve3 } from "three";
2
1
  import { computeFrenetFrames } from "./computeFrenetFrames.js";
3
2
  import { makeTubeGeometry } from "./makeTubeGeometry.js";
3
+ import Vector3 from "../../../../../../core/geom/Vector3.js";
4
4
 
5
5
  /**
6
6
  *
7
- * @param {THREE.Vector3[]} points
8
- * @param {number} point_count
7
+ * @param {Path} path
9
8
  * @param {TubePathStyle} style
9
+ * @param {number} segment_start
10
+ * @param {number} segment_end
10
11
  * @return {THREE.BufferGeometry}
11
12
  */
12
- export function build_geometry_catmullrom(points, point_count, style) {
13
+ export function build_geometry_catmullrom(path, style, segment_start, segment_end) {
13
14
 
14
- const path = new CatmullRomCurve3(points, false, "centripetal");
15
-
16
- const sample_points = [];
15
+ const point_count = path.getPointCount();
17
16
 
18
17
  // resample curve
19
18
  const total_points = Math.ceil(point_count * style.resolution);
20
19
 
21
- for (let i = 0; i < total_points; i++) {
20
+ const path_length = path.length;
21
+
22
+ const point = new Vector3();
23
+
24
+ const points_f32 = [];
25
+
26
+
27
+ let added_points = 0;
28
+ let i;
29
+
30
+ // initial segment
31
+ path.sample_catmull_rom(point, segment_start * path_length);
32
+
33
+ point.writeToArray(points_f32, added_points * 3);
34
+
35
+ added_points++;
36
+
37
+ const max_i = segment_end * (total_points - 1);
38
+ for (i = Math.ceil(segment_start * total_points + 0.0001); i < max_i; i++) {
22
39
  const t = i / (total_points - 1);
23
40
 
24
- const point = path.getPoint(t);
41
+ path.sample_catmull_rom(point, path_length * t);
42
+
43
+ point.writeToArray(points_f32, added_points * 3);
44
+
45
+ added_points++
46
+ }
47
+
48
+ if (((i - 1) / (total_points - 1)) - segment_end > 0.0001) {
49
+ // end
50
+
51
+ path.sample_catmull_rom(point, segment_end * path_length);
52
+
53
+ point.writeToArray(points_f32, added_points * 3);
25
54
 
26
- sample_points.push(point);
55
+ added_points++;
27
56
  }
28
57
 
29
- const frames = computeFrenetFrames(sample_points, false);
58
+ const frames = computeFrenetFrames(points_f32, false);
30
59
 
31
- return makeTubeGeometry(sample_points, frames.normals, frames.binormals, style.width, style.radial_resolution, false);
60
+ return makeTubeGeometry(points_f32, frames.normals, frames.binormals, style.width, style.radial_resolution, false);
32
61
  }
@@ -1,14 +1,54 @@
1
1
  import { computeFrenetFrames } from "./computeFrenetFrames.js";
2
2
  import { makeTubeGeometry } from "./makeTubeGeometry.js";
3
+ import Vector3 from "../../../../../../core/geom/Vector3.js";
4
+
5
+
6
+ const scratch_array_0 = [];
7
+ const scratch_array_1 = [];
8
+
9
+ const scratch_v3 = new Vector3();
3
10
 
4
11
  /**
5
12
  *
6
- * @param {THREE.Vector3[]} points
7
- * @param {number} point_count
13
+ * @param {Path} path
8
14
  * @param {TubePathStyle} style
15
+ * @param {number} segment_start
16
+ * @param {number} segment_end
9
17
  * @return {THREE.BufferGeometry}
10
18
  */
11
- export function build_geometry_linear(points, point_count, style) {
19
+ export function build_geometry_linear(path, style, segment_start, segment_end) {
20
+ const points = [];
21
+
22
+ const path_length = path.length;
23
+
24
+ path.find_index_and_normalized_distance(scratch_array_0, segment_start * path_length);
25
+ path.find_index_and_normalized_distance(scratch_array_1, segment_end * path_length);
26
+
27
+ let points_added = 0;
28
+
29
+ if (scratch_array_0[1] !== 0) {
30
+ path.sample_linear(scratch_v3, segment_start * path_length);
31
+
32
+ scratch_v3.writeToArray(points, points_added * 3);
33
+ points_added++;
34
+
35
+ // offset first sampled point, we're already past it
36
+ scratch_array_0[0]++;
37
+ }
38
+
39
+ const max_i = scratch_array_1[0];
40
+ for (let i = scratch_array_0[0]; i <= max_i; i++) {
41
+ path.readPositionToArray(i, points, points_added * 3);
42
+ points_added++;
43
+ }
44
+
45
+ if (scratch_array_1[1] !== 0) {
46
+ //add end
47
+ path.sample_linear(scratch_v3, segment_end * path_length);
48
+
49
+ scratch_v3.writeToArray(points, points_added * 3);
50
+ points_added++;
51
+ }
12
52
 
13
53
  const frames = computeFrenetFrames(points, false);
14
54
 
@@ -2,15 +2,17 @@ import { Vector3 } from "three";
2
2
  import { Matrix4 } from "three/src/math/Matrix4.js";
3
3
  import { max2 } from "../../../../../../core/math/max2.js";
4
4
  import { min2 } from "../../../../../../core/math/min2.js";
5
- import * as MathUtils from "three/src/math/MathUtils.js";
5
+ import { clamp } from "../../../../../../core/math/clamp.js";
6
+ import { vec3 } from "gl-matrix";
7
+ import { array_copy } from "../../../../../../core/collection/array/copyArray.js";
6
8
 
7
9
  /**
8
10
  * @see https://github.com/mrdoob/three.js/blob/c12c9a166a1369cdd58622fff2aff7e3a84305d7/src/extras/core/Curve.js#L260
9
- * @param {Vector3[]} points
11
+ * @param {Float32Array|number[]} points
10
12
  * @param {boolean} [closed]
11
13
  */
12
14
  export function computeFrenetFrames(points, closed = false) {
13
- // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
15
+ // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
14
16
 
15
17
  const normal = new Vector3();
16
18
 
@@ -21,10 +23,15 @@ export function computeFrenetFrames(points, closed = false) {
21
23
  const vec = new Vector3();
22
24
  const mat = new Matrix4();
23
25
 
24
- const segments = points.length - 1;
26
+ const point_count = points.length / 3;
27
+ const segments = point_count - 1;
25
28
 
26
- const v3_0 = new Vector3();
27
- const v3_1 = new Vector3();
29
+ const v3_0 = [];
30
+ const v3_1 = [];
31
+
32
+ const p0_a = [];
33
+ const pR_a = [];
34
+ const p1_a = [];
28
35
 
29
36
  /**
30
37
  *
@@ -36,18 +43,23 @@ export function computeFrenetFrames(points, closed = false) {
36
43
  const i0 = max2(i - 1, 0);
37
44
  const i1 = min2(i + 1, segments);
38
45
 
39
- const p0 = points[i0];
40
- const pR = points[i];
41
- const p1 = points[i1];
46
+ array_copy(points, i0 * 3, p0_a, 0, 3);
47
+ array_copy(points, i * 3, pR_a, 0, 3);
48
+ array_copy(points, i1 * 3, p1_a, 0, 3);
42
49
 
43
50
  // get points along the lines close to the reference
44
- v3_0.subVectors(p0, pR);
45
- v3_0.normalize();
46
51
 
47
- v3_1.subVectors(p1, pR);
48
- v3_1.normalize();
52
+ vec3.sub(v3_0, p0_a, pR_a);
53
+ vec3.normalize(v3_0, v3_0);
54
+
55
+ vec3.sub(v3_1, p1_a, pR_a);
56
+ vec3.normalize(v3_1, v3_1);
49
57
 
50
- destination.subVectors(v3_1, v3_0);
58
+ destination.set(
59
+ v3_1[0] - v3_0[0],
60
+ v3_1[1] - v3_0[1],
61
+ v3_1[2] - v3_0[2],
62
+ );
51
63
  destination.normalize();
52
64
  }
53
65
 
@@ -112,7 +124,7 @@ export function computeFrenetFrames(points, closed = false) {
112
124
 
113
125
  vec.normalize();
114
126
 
115
- const theta = Math.acos(MathUtils.clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors
127
+ const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors
116
128
 
117
129
  normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta));
118
130
 
@@ -126,7 +138,7 @@ export function computeFrenetFrames(points, closed = false) {
126
138
 
127
139
  if (closed === true) {
128
140
 
129
- let theta = Math.acos(MathUtils.clamp(normals[0].dot(normals[segments]), -1, 1));
141
+ let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1));
130
142
  theta /= segments;
131
143
 
132
144
  if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {
@@ -2,7 +2,7 @@ import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
2
2
 
3
3
  /**
4
4
  * @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
5
- * @param {Vector3[]} points
5
+ * @param {Float32Array|number[]} points
6
6
  * @param {Vector3[]} _normals
7
7
  * @param {Vector3[]} binormals
8
8
  * @param {number} radius
@@ -24,7 +24,8 @@ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegm
24
24
  const uvs = [];
25
25
  const indices = [];
26
26
 
27
- const tubularSegments = points.length - 1;
27
+ const point_count = points.length / 3;
28
+ const tubularSegments = point_count - 1;
28
29
 
29
30
  // create buffer data
30
31
 
@@ -69,7 +70,11 @@ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegm
69
70
 
70
71
  // we use getPointAt to sample evenly distributed points from the given path
71
72
 
72
- const P = points[i];
73
+ const i3 = i * 3;
74
+
75
+ const Px = points[i3];
76
+ const Py = points[i3 + 1];
77
+ const Pz = points[i3 + 2];
73
78
 
74
79
  // retrieve corresponding normal and binormal
75
80
 
@@ -97,9 +102,9 @@ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegm
97
102
 
98
103
  // vertex
99
104
 
100
- vertex.x = P.x + radius * normal.x;
101
- vertex.y = P.y + radius * normal.y;
102
- vertex.z = P.z + radius * normal.z;
105
+ vertex.x = Px + radius * normal.x;
106
+ vertex.y = Py + radius * normal.y;
107
+ vertex.z = Pz + radius * normal.z;
103
108
 
104
109
  vertices.push(vertex.x, vertex.y, vertex.z);
105
110
 
@@ -121,7 +121,7 @@ const PRINT_VERBOSE_STATS = false;
121
121
  /**
122
122
  *
123
123
  * @param {THREE.BufferGeometry} input
124
- * @returns {MicronGeometry}
124
+ * @returns {Promise<MicronGeometry>}
125
125
  */
126
126
  export async function buildMicronGeometryFromBufferGeometry(input) {
127
127
  const source_geometry = deinterleaveBufferGeometry(input);
@@ -1,5 +1,14 @@
1
1
  import { AbstractRenderAdapter } from "../../../ecs/mesh-v2/render/adapters/AbstractRenderAdapter.js";
2
2
  import Vector2 from "../../../../../core/geom/Vector2.js";
3
+ import { buildMicronGeometryFromBufferGeometry } from "../../build/buildMicronGeometryFromBufferGeometry.js";
4
+ import { ShadedGeometryFlags } from "../../../ecs/mesh-v2/ShadedGeometryFlags.js";
5
+ import { MaterialVertexSpec, MaterialVertexSpecFlags } from "../../render/v1/MaterialVertexSpec.js";
6
+
7
+ /**
8
+ *
9
+ * @type {MaterialVertexSpec}
10
+ */
11
+ const scratch_mvc = new MaterialVertexSpec();
3
12
 
4
13
  export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
5
14
 
@@ -21,19 +30,42 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
21
30
 
22
31
  /**
23
32
  *
24
- * @type {Set<MaterialContext>}
33
+ * @type {Map<number, Promise>}
25
34
  * @private
26
35
  */
27
- this.__active_contexts = new Set();
36
+ this.__geometry_building_promises = new Map();
28
37
  }
29
38
 
30
39
  add(sg) {
31
40
 
32
- const micron_geometry = this.__geometry_map.get(sg.geometry.id);
41
+ const geometry_id = sg.geometry.id;
42
+
43
+ const micron_geometry = this.__geometry_map.get(geometry_id);
44
+
45
+ if (micron_geometry === undefined) {
46
+ // no micron geometry
47
+ if (!this.__geometry_building_promises.has(geometry_id)) {
48
+ const p = buildMicronGeometryFromBufferGeometry(sg.geometry);
49
+
50
+ this.__geometry_building_promises.set(geometry_id, p);
51
+
52
+ p.then(mg => this.__geometry_map.set(geometry_id, mg));
53
+ }
54
+ return;
55
+ }
33
56
 
34
57
  // populate virtual geometry for this instance
35
58
  this.ctx.builder.buildForMesh(sg.entity, micron_geometry, sg.transform);
36
59
 
60
+ scratch_mvc.material = sg.material;
61
+ scratch_mvc.spec = micron_geometry.vertex_spec;
62
+ scratch_mvc.writeFlag(MaterialVertexSpecFlags.CastShadow, sg.getFlag(ShadedGeometryFlags.CastShadow));
63
+ scratch_mvc.writeFlag(MaterialVertexSpecFlags.ReceiveShadow, sg.getFlag(ShadedGeometryFlags.ReceiveShadow));
64
+
65
+ const material_context = this.ctx.obtain_material(scratch_mvc);
66
+
67
+ material_context.instances.add(sg.entity);
68
+
37
69
  }
38
70
 
39
71
  /**
@@ -69,6 +101,16 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
69
101
 
70
102
  build_end() {
71
103
  this.ctx.render();
104
+
105
+ const material_contexts = this.ctx.contexts_array;
106
+ const contexts_count = material_contexts.length;
107
+ for (let i = 0; i < contexts_count; i++) {
108
+ const mc = material_contexts[i];
109
+
110
+ if (mc.instances.size > 0) {
111
+ this.__objects[this.__object_count++] = mc.mesh;
112
+ }
113
+ }
72
114
  }
73
115
 
74
116
  dispose() {
@@ -125,7 +125,7 @@ export class ThreeInstancedAdapter {
125
125
 
126
126
  /**
127
127
  *
128
- * @param {Uint32Array[]} patches
128
+ * @param {Uint32Array} patches
129
129
  * @param {number} patch_count
130
130
  * @param {VirtualGeometry} virtual_geometry
131
131
  */
@@ -52,7 +52,7 @@ export class MaterialContext {
52
52
 
53
53
  /**
54
54
  *
55
- * @type {MicronGeometryPatchOccurrence[]}
55
+ * @type {Uint32Array|number[]}
56
56
  */
57
57
  this.__patches = [];
58
58
 
@@ -63,16 +63,6 @@ export class MaterialContext {
63
63
  this.__patch_count = 0;
64
64
  }
65
65
 
66
-
67
- /**
68
- *
69
- * @param {MicronGeometryPatchOccurrence} occurrence
70
- * @returns {boolean}
71
- */
72
- filter(occurrence) {
73
- return this.instances.has(occurrence.instance_id);
74
- }
75
-
76
66
  init() {
77
67
 
78
68
  const material = this.material.clone();
@@ -1,3 +1,8 @@
1
+ export const MaterialVertexSpecFlags = {
2
+ CastShadow: 1,
3
+ ReceiveShadow: 2
4
+ };
5
+
1
6
  export class MaterialVertexSpec {
2
7
  constructor() {
3
8
  /**
@@ -10,6 +15,52 @@ export class MaterialVertexSpec {
10
15
  * @type {THREE.Material|null}
11
16
  */
12
17
  this.material = null;
18
+
19
+ /**
20
+ *
21
+ * @type {number}
22
+ */
23
+ this.flags = 0;
24
+ }
25
+
26
+ /**
27
+ *
28
+ * @param {number|MaterialVertexSpecFlags} flag
29
+ * @returns {void}
30
+ */
31
+ setFlag(flag) {
32
+ this.flags |= flag;
33
+ }
34
+
35
+ /**
36
+ *
37
+ * @param {number|MaterialVertexSpecFlags} flag
38
+ * @returns {void}
39
+ */
40
+ clearFlag(flag) {
41
+ this.flags &= ~flag;
42
+ }
43
+
44
+ /**
45
+ *
46
+ * @param {number|MaterialVertexSpecFlags} flag
47
+ * @param {boolean} value
48
+ */
49
+ writeFlag(flag, value) {
50
+ if (value) {
51
+ this.setFlag(flag);
52
+ } else {
53
+ this.clearFlag(flag);
54
+ }
55
+ }
56
+
57
+ /**
58
+ *
59
+ * @param {number|MaterialVertexSpecFlags} flag
60
+ * @returns {boolean}
61
+ */
62
+ getFlag(flag) {
63
+ return (this.flags & flag) === flag;
13
64
  }
14
65
 
15
66
  /**
@@ -19,6 +70,7 @@ export class MaterialVertexSpec {
19
70
  copy(other) {
20
71
  this.spec = other.spec;
21
72
  this.material = other.material;
73
+ this.flags = other.flags;
22
74
  }
23
75
 
24
76
  /**
@@ -47,7 +99,7 @@ export class MaterialVertexSpec {
47
99
  }
48
100
 
49
101
  hash() {
50
- return this.spec.hash() ^ this.material.id;
102
+ return this.spec.hash() ^ this.material.id ^ this.flags;
51
103
  }
52
104
 
53
105
  equals(other) {
@@ -55,8 +107,9 @@ export class MaterialVertexSpec {
55
107
  return true;
56
108
  }
57
109
 
58
- return this.spec.equals(other.spec)
110
+ return this.flags === other.flags
59
111
  && this.material === other.material
112
+ && this.spec.equals(other.spec)
60
113
  ;
61
114
  }
62
115
  }
@@ -3,7 +3,7 @@ import { VirtualGeometryBuilder } from "../VirtualGeometryBuilder.js";
3
3
  import { HashMap } from "../../../../../core/collection/HashMap.js";
4
4
  import Signal from "../../../../../core/events/signal/Signal.js";
5
5
  import { MaterialContext } from "./MaterialContext.js";
6
- import { MaterialVertexSpec } from "./MaterialVertexSpec.js";
6
+ import { MaterialVertexSpec, MaterialVertexSpecFlags } from "./MaterialVertexSpec.js";
7
7
 
8
8
  /**
9
9
  *
@@ -39,6 +39,14 @@ export class MicronRenderContext {
39
39
  };
40
40
  }
41
41
 
42
+ /**
43
+ *
44
+ * @returns {MaterialContext[]}
45
+ */
46
+ get contexts_array() {
47
+ return this.__contexts_array;
48
+ }
49
+
42
50
  /**
43
51
  * Enable/disable wireframe rendering
44
52
  * @param {boolean} x
@@ -55,27 +63,27 @@ export class MicronRenderContext {
55
63
 
56
64
  /**
57
65
  *
58
- * @param {VertexDataSpec} spec
59
- * @param {Material} m
66
+ * @param {MaterialVertexSpec} descriptor
60
67
  * @returns {MaterialContext}
61
68
  */
62
- obtain_material(spec, m) {
63
- scratch_mvc.material = m;
64
- scratch_mvc.spec = spec;
69
+ obtain_material(descriptor) {
65
70
 
66
- const existing = this.material_contexts.get(scratch_mvc);
71
+ const existing = this.material_contexts.get(descriptor);
67
72
 
68
73
  if (existing !== undefined) {
69
74
  return existing;
70
75
  }
71
76
 
72
77
  // create a separate instance to be stored alongside the context
73
- const key = scratch_mvc.clone();
78
+ const key = descriptor.clone();
74
79
 
75
- const ctx = new MaterialContext(spec, m);
80
+ const ctx = new MaterialContext(descriptor.spec, descriptor.material);
76
81
 
77
82
  ctx.init();
78
83
 
84
+ ctx.mesh.castShadow = descriptor.getFlag(MaterialVertexSpecFlags.CastShadow);
85
+ ctx.mesh.receiveShadow = descriptor.getFlag(MaterialVertexSpecFlags.ReceiveShadow);
86
+
79
87
  this.material_contexts.set(key, ctx);
80
88
  this.__contexts_array.push(ctx);
81
89
 
@@ -95,7 +103,9 @@ export class MicronRenderContext {
95
103
  */
96
104
  const instance_to_ctx = new Map();
97
105
 
98
- for (let i = 0; i < context_count; i++) {
106
+ let i = 0;
107
+
108
+ for (i = 0; i < context_count; i++) {
99
109
  const ctx = contexts[i];
100
110
  ctx.__patch_count = 0;
101
111
 
@@ -109,7 +119,7 @@ export class MicronRenderContext {
109
119
  const patch_count = geometry.patch_count;
110
120
  const patches = geometry.patches;
111
121
 
112
- for (let i = 0; i < patch_count; i++) {
122
+ for (i = 0; i < patch_count; i++) {
113
123
  const i3 = i * 3;
114
124
 
115
125
  const instance_id = patches[i3];
@@ -118,7 +128,9 @@ export class MicronRenderContext {
118
128
 
119
129
  const ctx = instance_to_ctx.get(instance_id);
120
130
 
121
- const target_address = ctx.__patch_count * 3;
131
+ const target_index = ctx.__patch_count;
132
+ const target_address = target_index * 3;
133
+
122
134
  ctx.__patch_count++;
123
135
 
124
136
  const ctx_patches = ctx.__patches;
@@ -129,7 +141,7 @@ export class MicronRenderContext {
129
141
  }
130
142
 
131
143
  // build each context
132
- for (let i = 0; i < context_count; i++) {
144
+ for (i = 0; i < context_count; i++) {
133
145
  const ctx = contexts[i];
134
146
 
135
147
  ctx.build(geometry);
@@ -2,6 +2,13 @@ import Vector2 from "../../../../../core/geom/Vector2.js";
2
2
  import { MaterialContext } from "./MaterialContext.js";
3
3
  import { array_copy } from "../../../../../core/collection/array/copyArray.js";
4
4
  import { MicronRenderContext } from "./MicronRenderContext.js";
5
+ import { MaterialVertexSpec, MaterialVertexSpecFlags } from "./MaterialVertexSpec.js";
6
+
7
+ /**
8
+ *
9
+ * @type {MaterialVertexSpec}
10
+ */
11
+ const scratch_mvc = new MaterialVertexSpec();
5
12
 
6
13
  export class VGThreeRenderer {
7
14
  constructor() {
@@ -73,7 +80,12 @@ export class VGThreeRenderer {
73
80
  */
74
81
  const m = o;
75
82
 
76
- const ctx = render_context.obtain_material(m.geometry.vertex_spec, m.material);
83
+ scratch_mvc.material = m.material;
84
+ scratch_mvc.spec = m.geometry.vertex_spec;
85
+ scratch_mvc.writeFlag(MaterialVertexSpecFlags.CastShadow, m.castShadow);
86
+ scratch_mvc.writeFlag(MaterialVertexSpecFlags.ReceiveShadow, m.receiveShadow);
87
+
88
+ const ctx = render_context.obtain_material(scratch_mvc);
77
89
 
78
90
  ctx.instances.add(o.instance_id);
79
91
  }
@@ -436,7 +436,7 @@ class Path {
436
436
  */
437
437
  sample_linear(result, offset) {
438
438
 
439
- const found = this.__find_index_and_normalized_distance(scratch_array_0, offset);
439
+ const found = this.find_index_and_normalized_distance(scratch_array_0, offset);
440
440
 
441
441
  if (!found) {
442
442
  return false;
@@ -466,9 +466,8 @@ class Path {
466
466
  * @param {number[]} result
467
467
  * @param {number} absolute_distance absolute distance along the path
468
468
  * @returns {boolean}
469
- * @private
470
469
  */
471
- __find_index_and_normalized_distance(result, absolute_distance) {
470
+ find_index_and_normalized_distance(result, absolute_distance) {
472
471
  const pointCount = this.getPointCount();
473
472
 
474
473
  if (pointCount === 0) {
@@ -495,10 +494,10 @@ class Path {
495
494
  // we found v1
496
495
  const local_offset = absolute_distance - cursor;
497
496
 
498
- const local_normalize_offset = local_offset / distance;
497
+ const local_normalized_offset = local_offset / distance;
499
498
 
500
499
  result[0] = i - 1;
501
- result[1] = local_normalize_offset;
500
+ result[1] = local_normalized_offset;
502
501
  return true;
503
502
  }
504
503
 
@@ -525,7 +524,7 @@ class Path {
525
524
  * @returns {boolean}
526
525
  */
527
526
  sample_catmull_rom(result, offset) {
528
- const found = this.__find_index_and_normalized_distance(scratch_array_0, offset);
527
+ const found = this.find_index_and_normalized_distance(scratch_array_0, offset);
529
528
 
530
529
  if (!found) {
531
530
  return false;
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.35.10",
8
+ "version": "2.35.14",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",