@woosh/meep-engine 2.39.13 → 2.39.16

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 (33) hide show
  1. package/core/binary/int32_to_binary_string.js +18 -0
  2. package/core/cache/Cache.js +1 -1
  3. package/core/geom/3d/decompose_matrix_4_array.js +3 -1
  4. package/core/geom/3d/ray/ray3_array_apply_matrix4.js +15 -1
  5. package/core/geom/3d/topology/bounds/computeTopoMeshBoundingSphere.js +2 -1
  6. package/editor/tools/v2/TransformControls.js +1782 -0
  7. package/editor/tools/v2/prototypeTransformControls.js +79 -0
  8. package/engine/asset/AssetManager.js +82 -23
  9. package/engine/ecs/parent/EntityNode.js +41 -13
  10. package/engine/ecs/terrain/ecs/Terrain.js +49 -39
  11. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +12 -5
  12. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +5 -1
  13. package/engine/ecs/transform/Transform.d.ts +2 -0
  14. package/engine/ecs/transform/Transform.js +6 -0
  15. package/engine/ecs/transform-attachment/TransformAttachment.js +13 -0
  16. package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +138 -16
  17. package/engine/graphics/FrameRunner.js +8 -2
  18. package/engine/graphics/ecs/mesh-v2/DrawMode.js +4 -0
  19. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +77 -0
  20. package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +10 -0
  21. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -30
  22. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +11 -0
  23. package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +8 -32
  24. package/engine/graphics/ecs/mesh-v2/render/optimization/RuntimeDrawMethodOptimizer.js +6 -0
  25. package/engine/graphics/geometry/buffered/makeGeometryIndexed.js +23 -0
  26. package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +16 -4
  27. package/engine/graphics/geometry/buffered/query/GeometryVisitor.js +8 -1
  28. package/engine/graphics/geometry/buffered/query/RaycastNearestHitComputingVisitor.js +14 -5
  29. package/engine/graphics/geometry/bvh/buffered/BinaryBVHFromBufferGeometry.js +56 -0
  30. package/engine/graphics/micron/build/hierarchy/build_merge_graph.js +1 -0
  31. package/engine/graphics/micron/build/hierarchy/computePatchMergeScore.js +18 -4
  32. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +15 -5
  33. package/package.json +1 -1
@@ -1,6 +1,13 @@
1
1
  import { System } from "../System.js";
2
2
  import { TransformAttachment, TransformAttachmentFlags } from "./TransformAttachment.js";
3
3
  import { Transform } from "../transform/Transform.js";
4
+ import { min2 } from "../../../core/math/min2.js";
5
+
6
+ /**
7
+ * @readonly
8
+ * @type {number}
9
+ */
10
+ const QUEUE_ITERATION_COUNT = 32;
4
11
 
5
12
  class UpdateContext {
6
13
  constructor() {
@@ -27,22 +34,63 @@ class UpdateContext {
27
34
  * @type {Transform|null}
28
35
  */
29
36
  this.parent_transform = null;
37
+
38
+ /**
39
+ *
40
+ * @type {EntityComponentDataset|null}
41
+ */
42
+ this.ecd = null;
43
+ }
44
+
45
+ toString() {
46
+ return `UpdateContext{ attachment:${this.attachment}, entity:${this.entity} }`;
30
47
  }
31
48
 
32
49
  update() {
33
50
  this.transform.multiplyTransforms(this.parent_transform, this.attachment.transform);
34
51
  }
35
52
 
53
+ /**
54
+ *
55
+ * @returns {boolean}
56
+ */
57
+ bind_parent() {
58
+
59
+ const parent_transform = this.ecd.getComponent(this.attachment.parent, Transform);
60
+
61
+ if (parent_transform === undefined) {
62
+ return false;
63
+ }
64
+
65
+ this.parent_transform = parent_transform;
66
+
67
+ return true;
68
+ }
69
+
36
70
  link() {
37
- this.parent_transform.position.onChanged.add(this.update, this);
38
- this.parent_transform.rotation.onChanged.add(this.update, this);
39
- this.parent_transform.scale.onChanged.add(this.update, this);
71
+ const t_parent = this.parent_transform;
72
+
73
+ t_parent.position.onChanged.add(this.update, this);
74
+ t_parent.rotation.onChanged.add(this.update, this);
75
+ t_parent.scale.onChanged.add(this.update, this);
76
+
77
+ const t_attachment = this.attachment.transform;
78
+ t_attachment.position.onChanged.add(this.update,this);
79
+ t_attachment.rotation.onChanged.add(this.update,this);
80
+ t_attachment.scale.onChanged.add(this.update,this);
40
81
  }
41
82
 
42
83
  unlink() {
43
- this.parent_transform.position.onChanged.remove(this.update, this);
44
- this.parent_transform.rotation.onChanged.remove(this.update, this);
45
- this.parent_transform.scale.onChanged.remove(this.update, this);
84
+ const transform = this.parent_transform;
85
+
86
+ transform.position.onChanged.remove(this.update, this);
87
+ transform.rotation.onChanged.remove(this.update, this);
88
+ transform.scale.onChanged.remove(this.update, this);
89
+
90
+ const t_attachment = this.attachment.transform;
91
+ t_attachment.position.onChanged.remove(this.update,this);
92
+ t_attachment.rotation.onChanged.remove(this.update,this);
93
+ t_attachment.scale.onChanged.remove(this.update,this);
46
94
  }
47
95
  }
48
96
 
@@ -58,6 +106,54 @@ export class TransformAttachmentSystem extends System {
58
106
  * @private
59
107
  */
60
108
  this.__contexts = [];
109
+
110
+ /**
111
+ *
112
+ * @type {UpdateContext[]}
113
+ * @private
114
+ */
115
+ this.__queue = [];
116
+ this.__queue_size = 0;
117
+ this.__queue_cursor = 0;
118
+ }
119
+
120
+ /**
121
+ *
122
+ * @param {UpdateContext} ctx
123
+ * @private
124
+ */
125
+ __finalize_link(ctx) {
126
+
127
+ ctx.link();
128
+
129
+ this.__contexts[ctx.entity] = ctx;
130
+
131
+ if ((ctx.attachment.flags & TransformAttachmentFlags.Immediate) !== 0) {
132
+ ctx.update();
133
+ }
134
+ }
135
+
136
+ /**
137
+ *
138
+ * @param {UpdateContext} ctx
139
+ * @private
140
+ */
141
+ __enqueue(ctx) {
142
+ this.__queue[this.__queue_size++] = ctx;
143
+ }
144
+
145
+ __dequeue_entity(entity) {
146
+ for (let i = 0; i < this.__queue_size; i++) {
147
+ const ctx = this.__queue[i];
148
+
149
+ if (ctx.entity === entity) {
150
+ this.__queue.splice(i, 1);
151
+ this.__queue_size--;
152
+ return true;
153
+ }
154
+ }
155
+
156
+ return false;
61
157
  }
62
158
 
63
159
  /**
@@ -68,22 +164,23 @@ export class TransformAttachmentSystem extends System {
68
164
  */
69
165
  link(attachment, transform, entity) {
70
166
  const ctx = new UpdateContext();
71
- ctx.system = this;
72
167
 
73
168
  ctx.attachment = attachment;
74
169
  ctx.transform = transform;
75
170
  ctx.entity = entity;
76
171
 
77
172
 
78
- ctx.parent_transform = this.entityManager.dataset.getComponent(attachment.parent, Transform);
173
+ const ecd = this.entityManager.dataset;
79
174
 
80
- ctx.link();
81
-
82
- this.__contexts[entity] = ctx;
175
+ ctx.ecd = ecd;
83
176
 
84
- if ((attachment.flags & TransformAttachmentFlags.Immediate) !== 0) {
85
- ctx.update();
177
+ if (ctx.bind_parent()) {
178
+ this.__finalize_link(ctx);
179
+ } else {
180
+ // failed to bind parent, queue up instead
181
+ this.__enqueue(ctx);
86
182
  }
183
+
87
184
  }
88
185
 
89
186
  /**
@@ -93,12 +190,37 @@ export class TransformAttachmentSystem extends System {
93
190
  * @param {number} entity
94
191
  */
95
192
  unlink(attachment, transform, entity) {
193
+ const ctx = this.__contexts[entity];
96
194
 
195
+ if (ctx !== undefined) {
97
196
 
98
- const ctx = this.__contexts[entity];
197
+ delete this.__contexts[entity];
99
198
 
100
- delete this.__contexts[entity];
199
+ ctx.unlink();
200
+
201
+ } else {
202
+ // no context found, check the queue
203
+ this.__dequeue_entity(entity);
204
+ }
101
205
 
102
- ctx.unlink();
206
+ }
207
+
208
+ update(timeDelta) {
209
+ const step_count = min2(this.__queue_size, QUEUE_ITERATION_COUNT);
210
+ for (let i = 0; i < step_count; i++) {
211
+ const index = this.__queue_cursor % this.__queue_size;
212
+
213
+ const ctx = this.__queue[index];
214
+
215
+ if (ctx.bind_parent()) {
216
+ this.__finalize_link(ctx);
217
+
218
+ this.__queue.splice(index, 1);
219
+ this.__queue_size--;
220
+
221
+ } else {
222
+ this.__queue_cursor++;
223
+ }
224
+ }
103
225
  }
104
226
  }
@@ -1,3 +1,5 @@
1
+ let global_count = 0;
2
+
1
3
  /**
2
4
  *
3
5
  */
@@ -33,7 +35,9 @@ export class FrameRunner {
33
35
  return false;
34
36
  }
35
37
 
36
- console.warn("FrameFunner.started");
38
+ console.warn(`FrameFunner.started[${global_count}]`);
39
+
40
+ global_count++;
37
41
 
38
42
  this.running = true;
39
43
 
@@ -66,7 +70,9 @@ export class FrameRunner {
66
70
  return false;
67
71
  }
68
72
 
69
- console.warn("FrameFunner.stopped");
73
+ global_count--;
74
+
75
+ console.warn(`FrameFunner.stopped[${global_count}]`);
70
76
 
71
77
  this.running = false;
72
78
  cancelAnimationFrame(this.animationFrameId);
@@ -1,3 +1,7 @@
1
+ /**
2
+ *
3
+ * @enum {number}
4
+ */
1
5
  export const DrawMode = {
2
6
  Triangles: 0,
3
7
  Lines: 2,
@@ -3,6 +3,9 @@ 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 { mat4 } from "gl-matrix";
7
+ import { ray3_array_apply_matrix4 } from "../../../../core/geom/3d/ray/ray3_array_apply_matrix4.js";
8
+ import { GeometrySpatialQueryAccelerator } from "../../geometry/buffered/query/GeometrySpatialQueryAccelerator.js";
6
9
 
7
10
  /**
8
11
  * @readonly
@@ -12,12 +15,27 @@ const scratch_aabb3_array = new Float32Array(6);
12
15
 
13
16
  const DEFAULT_FLAGS = ShadedGeometryFlags.CastShadow
14
17
  | ShadedGeometryFlags.ReceiveShadow
18
+ | ShadedGeometryFlags.Visible
15
19
  ;
16
20
 
17
21
  const FLAG_SET_EQUALITY = ShadedGeometryFlags.CastShadow
18
22
  | ShadedGeometryFlags.ReceiveShadow
23
+ | ShadedGeometryFlags.DrawMethodLocked
24
+ | ShadedGeometryFlags.Visible
19
25
  ;
20
26
 
27
+ /**
28
+ *
29
+ * @type {Float32Array|mat4}
30
+ */
31
+ const scratch_m4 = new Float32Array(16);
32
+
33
+ /**
34
+ * @readonly
35
+ * @type {Float32Array}
36
+ */
37
+ const scratch_ray_0 = new Float32Array(6);
38
+
21
39
  export class ShadedGeometry {
22
40
  constructor() {
23
41
  /**
@@ -160,6 +178,30 @@ export class ShadedGeometry {
160
178
  ;
161
179
  }
162
180
 
181
+ /**
182
+ *
183
+ * @param {ShadedGeometry} other
184
+ */
185
+ copy(other) {
186
+ this.geometry = other.geometry;
187
+ this.material = other.material;
188
+ this.draw_method = other.draw_method;
189
+ this.mode = other.mode;
190
+ this.flags = other.flags;
191
+ }
192
+
193
+ /**
194
+ *
195
+ * @returns {ShadedGeometry}
196
+ */
197
+ clone() {
198
+ const r = new ShadedGeometry();
199
+
200
+ r.copy(this);
201
+
202
+ return r;
203
+ }
204
+
163
205
  /**
164
206
  * Current cached entity that this component is attached to
165
207
  * @return {number}
@@ -251,6 +293,41 @@ export class ShadedGeometry {
251
293
 
252
294
  aabb3_matrix4_project(this.__bvh_aabb, scratch_aabb3_array, this.transform);
253
295
  }
296
+
297
+ /**
298
+ *
299
+ * @param {SurfacePoint3} contact if found, contact is written here
300
+ * @param {ArrayLike<number>|number[]|Float32Array} ray 6-tuple: [origin_x, origin_y, origin_z, direction_x, direction_y, direction_z]
301
+ * @param {ArrayLike<number>|number[]|Float32Array} transform_matrix4
302
+ * @returns {boolean}
303
+ */
304
+ query_raycast_nearest(contact, ray, transform_matrix4) {
305
+
306
+ // get transform in local space
307
+ mat4.invert(scratch_m4, transform_matrix4);
308
+
309
+ // transform ray to local space
310
+ if (!ray3_array_apply_matrix4(scratch_ray_0, ray, scratch_m4)) {
311
+ // invalid transform matrix
312
+ return false;
313
+ }
314
+
315
+ const hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_array(
316
+ contact,
317
+ this.geometry,
318
+ scratch_ray_0
319
+ );
320
+
321
+ if (!hit_found) {
322
+ return false;
323
+ }
324
+
325
+ // transform hit contact into global space
326
+ contact.applyMatrix4(transform_matrix4);
327
+
328
+ return true;
329
+
330
+ }
254
331
  }
255
332
 
256
333
  /**
@@ -7,4 +7,14 @@ export const ShadedGeometryFlags = {
7
7
 
8
8
  CastShadow: 2,
9
9
  ReceiveShadow: 4,
10
+
11
+ /**
12
+ * Draw method is not allowed to change if this is on
13
+ */
14
+ DrawMethodLocked: 8,
15
+
16
+ /**
17
+ * If set to false will not render
18
+ */
19
+ Visible:16
10
20
  };
@@ -1,7 +1,6 @@
1
1
  import { ShadedGeometry } from "./ShadedGeometry.js";
2
2
  import { Transform } from "../../../ecs/transform/Transform.js";
3
3
  import { System } from "../../../ecs/System.js";
4
- import { GeometrySpatialQueryAccelerator } from "../../geometry/buffered/query/GeometrySpatialQueryAccelerator.js";
5
4
  import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
6
5
  import { v3_distance_sqr } from "../../../../core/geom/v3_distance_sqr.js";
7
6
  import { mat4 } from "gl-matrix";
@@ -9,7 +8,6 @@ import { returnTrue } from "../../../../core/function/Functions.js";
9
8
  import { ShadedGeometryRendererContext } from "./render/ShadedGeometryRendererContext.js";
10
9
  import { assert } from "../../../../core/assert.js";
11
10
  import { ray3_array_compose } from "../../../../core/geom/3d/ray/ray3_array_compose.js";
12
- import { ray3_array_apply_matrix4 } from "../../../../core/geom/3d/ray/ray3_array_apply_matrix4.js";
13
11
  import {
14
12
  ExplicitBinaryBoundingVolumeHierarchy
15
13
  } from "../../../../core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js";
@@ -382,31 +380,18 @@ export class ShadedGeometrySystem extends System {
382
380
 
383
381
  const m4 = sg.transform;
384
382
 
385
- // get transform in local space
386
- mat4.invert(scratch_m4, m4);
387
-
388
383
  ray3_array_compose(
389
384
  scratch_ray_0,
390
385
  origin_x, origin_y, origin_z,
391
386
  direction_x, direction_y, direction_z
392
387
  );
393
388
 
394
- // transform ray to local space
395
- ray3_array_apply_matrix4(scratch_ray_0, scratch_ray_0, scratch_m4);
396
-
397
- const geometry_hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_array(
398
- scratch_point,
399
- sg.geometry,
400
- scratch_ray_0
401
- );
389
+ const geometry_hit_found = sg.query_raycast_nearest(scratch_point,scratch_ray_0,m4);
402
390
 
403
391
  if (!geometry_hit_found) {
404
392
  continue;
405
393
  }
406
394
 
407
- // transform hit contact into global space
408
- scratch_point.applyMatrix4(m4);
409
-
410
395
  result.push({
411
396
  entity,
412
397
  mesh: sg,
@@ -476,31 +461,18 @@ export class ShadedGeometrySystem extends System {
476
461
  */
477
462
  const m4 = sg.transform;
478
463
 
479
- // get transform in local space
480
- mat4.invert(scratch_m4, m4);
481
-
482
464
  ray3_array_compose(
483
465
  scratch_ray_0,
484
466
  origin_x, origin_y, origin_z,
485
467
  direction_x, direction_y, direction_z
486
468
  );
487
469
 
488
- // transform ray to local space
489
- ray3_array_apply_matrix4(scratch_ray_0, scratch_ray_0, scratch_m4);
490
-
491
- const geometry_hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_array(
492
- scratch_point,
493
- sg.geometry,
494
- scratch_ray_0
495
- );
470
+ const geometry_hit_found = sg.query_raycast_nearest(scratch_point,scratch_ray_0,m4);
496
471
 
497
472
  if (!geometry_hit_found) {
498
473
  continue;
499
474
  }
500
475
 
501
- // transform hit contact into global space
502
- scratch_point.applyMatrix4(m4);
503
-
504
476
  found_hit = true;
505
477
 
506
478
  const hit_position = scratch_point.position;
@@ -6,6 +6,7 @@ import { ShadedGeometry } from "../ShadedGeometry.js";
6
6
  import {
7
7
  bvh_query_user_data_overlaps_frustum
8
8
  } from "../../../../../core/bvh2/bvh3/bvh_query_user_data_overlaps_frustum.js";
9
+ import { ShadedGeometryFlags } from "../ShadedGeometryFlags.js";
9
10
 
10
11
  /**
11
12
  *
@@ -104,6 +105,16 @@ export class ShadedGeometryRendererContext {
104
105
  */
105
106
  const sg = ecd.getComponentByIndex(entity, sg_component_index);
106
107
 
108
+ if ((sg.flags & ShadedGeometryFlags.Visible) === 0) {
109
+ // not visible
110
+ continue;
111
+ }
112
+
113
+ if (!sg.material.visible) {
114
+ // material is not visible, skip
115
+ continue;
116
+ }
117
+
107
118
  const adapter = adapters[sg.draw_method];
108
119
 
109
120
  adapter.add(sg);
@@ -9,36 +9,6 @@ const INSTANCED_EQUALITY_FLAGS = ShadedGeometryFlags.CastShadow
9
9
  | ShadedGeometryFlags.ReceiveShadow
10
10
  ;
11
11
 
12
- /**
13
- *
14
- * @param {ShadedGeometry} a
15
- * @param {ShadedGeometry} b
16
- * @returns {number}
17
- */
18
- function compare(a, b) {
19
- const d_geometry = a.geometry.id - b.geometry.id;
20
- if (d_geometry !== 0) {
21
- return d_geometry;
22
- }
23
-
24
- const d_material = a.material.id - b.material.id;
25
- if (d_material !== 0) {
26
- return d_material;
27
- }
28
-
29
- const d_flags = (a.flags & INSTANCED_EQUALITY_FLAGS) - (b.flags & INSTANCED_EQUALITY_FLAGS);
30
- if (d_flags !== 0) {
31
- return d_flags;
32
- }
33
-
34
- const d_mode = a.mode - b.mode;
35
- if (d_mode !== 0) {
36
- return d_mode;
37
- }
38
-
39
- return 0;
40
- }
41
-
42
12
  export class InstancedRendererAdapter extends AbstractRenderAdapter {
43
13
  constructor() {
44
14
  super();
@@ -159,17 +129,23 @@ export class InstancedRendererAdapter extends AbstractRenderAdapter {
159
129
  clear() {
160
130
  super.clear();
161
131
 
162
- for (const activeMesh of this.__active_meshes) {
132
+ const meshes = this.__active_meshes;
133
+
134
+ for (const activeMesh of meshes) {
163
135
  activeMesh.setCount(0);
164
136
  }
165
137
 
166
- this.__active_meshes.clear();
138
+ meshes.clear();
167
139
  }
168
140
 
169
141
  dispose() {
142
+ this.clear();
143
+
170
144
  // release GPU memory
171
145
  this.__instanced_meshes.forEach((value, key) => {
172
146
  value.dispose();
173
147
  });
148
+
149
+ this.__instanced_meshes.clear();
174
150
  }
175
151
  }
@@ -1,4 +1,5 @@
1
1
  import { ShadedGeometry } from "../../ShadedGeometry.js";
2
+ import { ShadedGeometryFlags } from "../../ShadedGeometryFlags.js";
2
3
 
3
4
  export class RuntimeDrawMethodOptimizer {
4
5
  constructor() {
@@ -70,6 +71,11 @@ export class RuntimeDrawMethodOptimizer {
70
71
  return false;
71
72
  }
72
73
 
74
+ if (sg.getFlag(ShadedGeometryFlags.DrawMethodLocked)) {
75
+ // draw mode is locked, can't change
76
+ return false;
77
+ }
78
+
73
79
  const use_count = system.getGeometryUsageCounters().get(sg.geometry.id);
74
80
 
75
81
  /**
@@ -0,0 +1,23 @@
1
+ import { Float32BufferAttribute } from "three";
2
+
3
+ /**
4
+ *
5
+ * @param {THREE.BufferGeometry} geometry
6
+ */
7
+ export function makeGeometryIndexed(geometry) {
8
+ let index_attribute = geometry.getIndex();
9
+
10
+ if (index_attribute === null || index_attribute === undefined) {
11
+ const position_attribute = geometry.getAttribute('position');
12
+ // non-indexed geometry, build index
13
+ const array = new Float32Array(position_attribute.count * 3);
14
+
15
+ for (let i = 0; i < array.length; i++) {
16
+ array[i] = i;
17
+ }
18
+
19
+ index_attribute = new Float32BufferAttribute(array, 1, false);
20
+
21
+ geometry.setIndex(index_attribute);
22
+ }
23
+ }
@@ -1,5 +1,5 @@
1
1
  import { Cache } from "../../../../../core/cache/Cache.js";
2
- import { buildUnsorted } from "../../bvh/buffered/BinaryBVHFromBufferGeometry.js";
2
+ import { buildUnsorted, buildUnsortedUnindexed } from "../../bvh/buffered/BinaryBVHFromBufferGeometry.js";
3
3
  import { ClippingPlaneContainmentComputingVisitor } from "./ClippingPlaneContainmentComputingVisitor.js";
4
4
  import { RaycastNearestHitComputingVisitor } from "./RaycastNearestHitComputingVisitor.js";
5
5
  import { assert } from "../../../../../core/assert.js";
@@ -93,7 +93,15 @@ export class GeometrySpatialQueryAccelerator {
93
93
  assert.notNull(geometry, 'geometry');
94
94
  assert.defined(geometry, 'geometry');
95
95
 
96
- const bvh = this.__acquireBVH(geometry);
96
+
97
+ let bvh;
98
+
99
+ try {
100
+ bvh = this.__acquireBVH(geometry);
101
+ } catch (e) {
102
+ console.error(e);
103
+ return false;
104
+ }
97
105
 
98
106
  this.__visitor_raycast.reset();
99
107
 
@@ -174,9 +182,13 @@ export class GeometrySpatialQueryAccelerator {
174
182
  const index_attribute = geometry.getIndex();
175
183
 
176
184
  const de_interleaved_position_attribute = deinterleaveBufferAttribute(position_attribute);
177
- const de_interleaved_index_attribute = deinterleaveBufferAttribute(index_attribute);
178
-
179
185
  const vertices = de_interleaved_position_attribute.array;
186
+
187
+ if(index_attribute === undefined || index_attribute === null){
188
+ return buildUnsortedUnindexed(vertices);
189
+ }
190
+
191
+ const de_interleaved_index_attribute = deinterleaveBufferAttribute(index_attribute);
180
192
  const indices = de_interleaved_index_attribute.array;
181
193
 
182
194
  return buildUnsorted(vertices, indices);
@@ -60,7 +60,14 @@ export class GeometrySpatialAcceleratorVisitor extends IndexedBinaryBVHVisitor {
60
60
  setGeometry(geometry) {
61
61
  this.geometry = geometry;
62
62
 
63
- this.__buffer_indices = geometry.getIndex().array;
63
+ const index_attribute = geometry.getIndex();
64
+
65
+ if (index_attribute) {
66
+ this.__buffer_indices = index_attribute.array;
67
+ } else {
68
+ // no index attribute
69
+ this.__buffer_indices = null;
70
+ }
64
71
 
65
72
  const position_attribute = geometry.getAttribute('position');
66
73
 
@@ -110,11 +110,20 @@ export class RaycastNearestHitComputingVisitor extends GeometrySpatialAccelerato
110
110
 
111
111
  const indices = this.__buffer_indices;
112
112
 
113
- assert.lessThan(index3 + 2, indices.length, 'triangle index overflow, possibly geometry changed but tree was not rebuilt?');
114
-
115
- const a = indices[index3];
116
- const b = indices[index3 + 1];
117
- const c = indices[index3 + 2];
113
+ let a, b, c;
114
+
115
+ if(indices !== null) {
116
+ assert.lessThan(index3 + 2, indices.length, 'triangle index overflow, possibly geometry changed but tree was not rebuilt?');
117
+
118
+ a = indices[index3];
119
+ b = indices[index3 + 1];
120
+ c = indices[index3 + 2];
121
+ }else{
122
+ // implicit indices
123
+ a = index3;
124
+ b = index3+1;
125
+ c = index3+2;
126
+ }
118
127
 
119
128
  const vertices = this.__buffer_vertices;
120
129