@woosh/meep-engine 2.34.44 → 2.34.48

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 (31) hide show
  1. package/core/binary/BitSet.js +4 -2
  2. package/core/bvh2/BinaryNode.js +53 -25
  3. package/core/bvh2/traversal/bvh_traverse_pre_order_using_stack.js +43 -0
  4. package/core/bvh2/visual/convert_bvh_to_dot_format_string.js +50 -0
  5. package/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +1 -1
  6. package/core/geom/3d/topology/TopoMesh.js +0 -8
  7. package/core/geom/3d/topology/TopoVertex.js +7 -6
  8. package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +93 -5
  9. package/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.js +7 -4
  10. package/core/geom/3d/topology/simplify/quadratic/compute_edge_collapse_cost_quadratic.js +7 -1
  11. package/core/geom/3d/topology/simplify/simplifyTopoMesh.js +1 -14
  12. package/core/graph/convertGraphToDotString.js +4 -4
  13. package/core/graph/v2/Graph.js +4 -0
  14. package/engine/ecs/foliage/ecs/Foliage2System.js +22 -5
  15. package/engine/graphics/compute_geometry_polycount.js +21 -0
  16. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +64 -9
  17. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +4 -0
  18. package/engine/graphics/micron/build/hierarchy/merge_patches.js +1 -1
  19. package/engine/graphics/micron/convert_three_object_to_micron.js +2 -0
  20. package/engine/graphics/micron/format/MicronGeometry.js +1 -3
  21. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +176 -0
  22. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +1 -135
  23. package/engine/graphics/micron/prototypeVirtualGeometry.js +13 -8
  24. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +10 -43
  25. package/engine/graphics/micron/render/refinement/ActivePatchList.js +2 -8
  26. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +123 -0
  27. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +316 -11
  28. package/engine/graphics/render/layers/RenderLayer.js +22 -14
  29. package/engine/graphics/render/visibility/IncrementalDeltaSet.js +18 -5
  30. package/engine/graphics/render/visibility/VisibilityComputer.js +11 -15
  31. package/package.json +1 -1
@@ -533,12 +533,14 @@ BitSet.prototype.shift = function (distance, start_index, end_index) {
533
533
  * Sets all of the bits in this BitSet to false.
534
534
  */
535
535
  BitSet.prototype.reset = function () {
536
- if (this.__length <= 0) {
536
+ const current_length = this.__length;
537
+
538
+ if (current_length <= 0) {
537
539
  // no action required
538
540
  return;
539
541
  }
540
542
 
541
- this.__data_uint32.fill(0, 0, Math.ceil(this.__length / 32));
543
+ this.__data_uint32.fill(0, 0, Math.ceil(current_length / 32));
542
544
  this.__length = 0;
543
545
  };
544
546
 
@@ -211,44 +211,62 @@ export class BinaryNode extends Node {
211
211
  */
212
212
  __insertion_findSiblingFor(box) {
213
213
 
214
- const open_set = [this];
215
- let cursor = 1;
214
+ let node = this;
215
+ let cost_left = 0;
216
+ let cost_right = 0;
216
217
 
217
- let best_cost = Infinity;
218
- let best_node = this;
218
+ while (node.isBinaryNode) {
219
+ const left = node.left;
220
+ const right = node.right;
219
221
 
220
- while (cursor > 0) {
221
- const node = open_set[--cursor];
222
+ const area = aabb3_box_surface_area_2(node);
222
223
 
223
- const cost = inclusion_surface_area_heuristic(node, box);
224
+ const combined_area = combined_surface_area(node, box);
224
225
 
225
- if (cost < best_cost) {
226
- best_cost = cost;
227
- best_node = node;
228
- }
226
+ //cost of creating a new parent for this node and the new leaf
227
+ const cost = combined_area;
229
228
 
230
- if (node.isBinaryNode) {
229
+ // minimum cost of pushing the leaf further down the tree
230
+ const inheritance_cost = combined_area - area;
231
231
 
232
- // consider pushing children into the queue
232
+ // cost of descending left
233
+ if (left !== null) {
234
+ if (left.isLeafNode) {
235
+ cost_left = combined_surface_area(left, box) + inheritance_cost;
236
+ } else {
237
+ cost_left = combined_surface_area(left, box) - aabb3_box_surface_area_2(left) + inheritance_cost;
238
+ }
239
+ } else {
240
+ cost_left = Infinity;
241
+ }
233
242
 
234
- // compute lower bound for cost
235
- const c_low = inclusion_cost_lower_bound(node, box);
243
+ // cost of descending right
244
+ if (right !== null) {
236
245
 
237
- if (c_low < best_cost) {
238
- // worth-while to explore children
239
- if (node.left !== null) {
240
- open_set[cursor++] = node.left;
241
- }
242
- if (node.right !== null) {
243
- open_set[cursor++] = node.right
244
- }
246
+ if (right.isLeafNode) {
247
+ cost_right = combined_surface_area(right, box) + inheritance_cost;
248
+ } else {
249
+ cost_right = combined_surface_area(right, box) - aabb3_box_surface_area_2(right) + inheritance_cost;
245
250
  }
251
+ } else {
252
+ cost_right = Infinity;
246
253
  }
247
254
 
255
+ if (cost < cost_left && cost < cost_right) {
256
+ // descending either branch is too expensive
257
+ break;
258
+ }
248
259
 
260
+ if (cost_left < cost_right && left !== null) {
261
+ node = left;
262
+ } else if (right !== null) {
263
+ node = right;
264
+ } else {
265
+ break;
266
+ }
249
267
  }
250
268
 
251
- return best_node;
269
+ return node;
252
270
  }
253
271
 
254
272
  /**
@@ -1134,6 +1152,16 @@ BinaryNode.prototype.isBinaryNode = true;
1134
1152
  * @returns {number}
1135
1153
  */
1136
1154
  function inclusion_surface_area_heuristic(node, addition) {
1155
+ return combined_surface_area(node, addition) - aabb3_box_surface_area_2(node);
1156
+ }
1157
+
1158
+ /**
1159
+ *
1160
+ * @param {AABB3} node
1161
+ * @param {AABB3} addition
1162
+ * @returns {number}
1163
+ */
1164
+ function combined_surface_area(node, addition) {
1137
1165
  const x0 = min2(node.x0, addition.x0);
1138
1166
  const y0 = min2(node.y0, addition.y0);
1139
1167
  const z0 = min2(node.z0, addition.z0);
@@ -1142,7 +1170,7 @@ function inclusion_surface_area_heuristic(node, addition) {
1142
1170
  const y1 = max2(node.y1, addition.y1);
1143
1171
  const z1 = max2(node.z1, addition.z1);
1144
1172
 
1145
- return aabb3_compute_half_surface_area(x0, y0, z0, x1, y1, z1) - aabb3_box_surface_area_2(node);
1173
+ return aabb3_compute_half_surface_area(x0, y0, z0, x1, y1, z1);
1146
1174
  }
1147
1175
 
1148
1176
  /**
@@ -0,0 +1,43 @@
1
+ const stack = [];
2
+ let stackPointer = 0;
3
+
4
+ /**
5
+ *
6
+ * @param {BinaryNode} root
7
+ * @param {function(BinaryNode|LeafNode):*} visitor
8
+ * @param {*} [thisArg]
9
+ * @return {number}
10
+ */
11
+ export function bvh_traverse_pre_order_using_stack(root, visitor, thisArg) {
12
+ let visitCount = 0;
13
+
14
+ const stackOffset = stackPointer;
15
+
16
+ stack[stackPointer++] = root;
17
+
18
+ let n;
19
+
20
+ while (stackPointer-- > stackOffset) {
21
+
22
+ visitCount++;
23
+
24
+ n = stack[stackPointer];
25
+
26
+ visitor.call(thisArg, n);
27
+
28
+ if (!n.isLeafNode) {
29
+ //a binary node
30
+ if (n.right !== null) {
31
+ stack[stackPointer++] = n.right;
32
+ }
33
+ if (n.left !== null) {
34
+ stack[stackPointer++] = n.left;
35
+ }
36
+ }
37
+ }
38
+
39
+ // drop stack frame
40
+ stackPointer = stackOffset;
41
+
42
+ return visitCount;
43
+ }
@@ -0,0 +1,50 @@
1
+ import { convertGraphToDotString } from "../../graph/convertGraphToDotString.js";
2
+ import { bvh_traverse_pre_order_using_stack } from "../traversal/bvh_traverse_pre_order_using_stack.js";
3
+ import { Graph } from "../../graph/v2/Graph.js";
4
+ import { Edge, EdgeDirectionType } from "../../graph/Edge.js";
5
+
6
+ /**
7
+ *
8
+ * @param {BinaryNode} bvh
9
+ * @returns {Graph}
10
+ */
11
+ export function bvh_to_graph(bvh) {
12
+ //traverse twice, first collect all nodes, second time introduce edges
13
+ const graph = new Graph();
14
+
15
+ bvh_traverse_pre_order_using_stack(bvh, node => {
16
+ graph.addNode(node);
17
+ });
18
+
19
+ bvh_traverse_pre_order_using_stack(bvh, node => {
20
+ if (node.isBinaryNode) {
21
+
22
+ if (node.left !== null) {
23
+ const edge = new Edge(node, node.left);
24
+
25
+ edge.direction = EdgeDirectionType.Forward;
26
+
27
+ graph.addEdge(edge);
28
+ }
29
+ if (node.right !== null) {
30
+ const edge = new Edge(node, node.right);
31
+
32
+ edge.direction = EdgeDirectionType.Forward;
33
+
34
+ graph.addEdge(edge);
35
+ }
36
+ }
37
+ });
38
+
39
+ return graph;
40
+ }
41
+
42
+ /**
43
+ * Build a Graphviz DOT representation of the graph
44
+ * @param {BinaryNode} bvh
45
+ * @returns {string}
46
+ */
47
+ export function convert_bvh_to_dot_format_string(bvh) {
48
+ const graph = bvh_to_graph(bvh);
49
+ return convertGraphToDotString(graph);
50
+ }
@@ -16,7 +16,7 @@ export function aabb3_estimate_projected_area2(aabb, matrix) {
16
16
  for (let i = 0; i < 2; i++) {
17
17
  for (let j = 0; j < 3; j++) {
18
18
 
19
- const m_a = i * 4 + j;
19
+ const m_a = i + j* 4;
20
20
  const m_v = matrix[m_a];
21
21
 
22
22
  const a = m_v * aabb[j];
@@ -31,14 +31,6 @@ export class TopoMesh {
31
31
  this.__faces = new Set();
32
32
  }
33
33
 
34
- get edges() {
35
- throw new Error('Deprecated accessor, use getEdges instead');
36
- }
37
-
38
- get faces() {
39
- throw new Error('Deprecated accessor, use getEdges instead');
40
- }
41
-
42
34
  /**
43
35
  *
44
36
  * @returns {Set<TopoEdge>}
@@ -241,24 +241,25 @@ export class TopoVertex {
241
241
  const faces = victim.faces;
242
242
  const face_count = faces.length;
243
243
 
244
- for (let i = 0; i < face_count; i++) {
244
+ let i = 0;
245
+
246
+ for (; i < face_count; i++) {
245
247
  // take over the victim's faces
246
248
  const face = faces[i];
247
249
 
248
- // DEBUG
249
- face.computeNormal();
250
+
251
+ // face.computeNormal(); // DEBUG
250
252
  face.replaceVertex(victim, this);
251
253
 
252
254
  this.addUniqueFace(face);
253
255
 
254
- // DEBUG
255
- face.computeNormal();
256
+ // face.computeNormal(); // DEBUG
256
257
  }
257
258
 
258
259
  const edges = victim.edges;
259
260
  const edge_count = edges.length;
260
261
 
261
- for (let i = 0; i < edge_count; i++) {
262
+ for (i = 0; i < edge_count; i++) {
262
263
  // take over the victim's edges
263
264
  const edge = edges[i];
264
265
 
@@ -1,4 +1,7 @@
1
1
  import { v3_dot } from "../../../../v3_dot.js";
2
+ import { fabsf } from "../../../../../math/fabsf.js";
3
+ import { EPSILON } from "../../../../../math/MathUtils.js";
4
+ import { vec3 } from "gl-matrix";
2
5
 
3
6
  export class Quadratic3 {
4
7
  constructor() {
@@ -21,6 +24,74 @@ export class Quadratic3 {
21
24
  this.d2 = 0;
22
25
  }
23
26
 
27
+ /**
28
+ *
29
+ * @param {ArrayLike<number>|number[]|Float32Array} result
30
+ */
31
+ toVector3(result) {
32
+ result[0] = this.ad;
33
+ result[1] = this.bd;
34
+ result[2] = this.cd;
35
+ }
36
+
37
+ /**
38
+ *
39
+ * @param {ArrayLike<number>|number[]|Float32Array} m3
40
+ */
41
+ toTensorM3(m3) {
42
+ m3[0] = this.a2;
43
+ m3[1] = this.ab;
44
+ m3[2] = this.ac;
45
+
46
+ m3[3] = this.ab;
47
+ m3[4] = this.b2;
48
+ m3[5] = this.bc;
49
+
50
+ m3[6] = this.ac;
51
+ m3[7] = this.bc;
52
+ m3[8] = this.c2;
53
+ }
54
+
55
+ /**
56
+ * Equivalent to taking a tensor followed by matrix inversion
57
+ * @param {ArrayLike<number>|number[]|Float32Array} m3
58
+ * @param {number} epsilon
59
+ * @returns {boolean} whether operation was successful or not
60
+ */
61
+ toTensorM3Inverse(m3, epsilon) {
62
+ const a2 = this.a2;
63
+ const b2 = this.b2;
64
+ const c2 = this.c2;
65
+ const bc = this.bc;
66
+ const ab = this.ab;
67
+ const ac = this.ac;
68
+
69
+ const det = a2 * (b2 * c2 - bc * bc) -
70
+ ab * (ab * c2 - ac * bc) +
71
+ ac * (ab * bc - ac * b2);
72
+
73
+ if (fabsf(det) > epsilon) {
74
+
75
+ const invdet = 1.0 / det;
76
+
77
+ m3[0] = (b2 * c2 - bc * bc) * invdet;
78
+ m3[3] = (bc * ac - ab * c2) * invdet;
79
+ m3[6] = (ab * bc - b2 * ac) * invdet;
80
+
81
+ m3[1] = (ac * bc - ab * c2) * invdet;
82
+ m3[4] = (a2 * c2 - ac * ac) * invdet;
83
+ m3[7] = (ab * ac - a2 * bc) * invdet;
84
+
85
+ m3[2] = (ab * bc - ac * b2) * invdet;
86
+ m3[5] = (ac * ab - a2 * bc) * invdet;
87
+ m3[8] = (a2 * b2 - ab * ab) * invdet;
88
+
89
+ return true;
90
+ }
91
+
92
+ return false;
93
+ }
94
+
24
95
  /**
25
96
  * Set from plane expressed as a 4 component vector
26
97
  * @param {number} x plane normal
@@ -78,18 +149,35 @@ export class Quadratic3 {
78
149
  evaluate(x, y, z) {
79
150
  // Evaluate vAv + 2bv + c
80
151
 
81
- return x * x * this.a2 + 2 * x * y * this.ab + 2 * x * z * this.ac + 2 * x * this.ad
82
- + y * y * this.b2 + 2 * y * z * this.bc + 2 * y * this.bd
83
- + z * z * this.c2 + 2 * z * this.cd
84
- + this.d2;
152
+ return (x * x * this.a2) + (2 * x * y * this.ab) + (2 * x * z * this.ac) + (2 * x * this.ad) // A
153
+ + (y * y * this.b2) + (2 * y * z * this.bc) + (2 * y * this.bd) // B
154
+ + (z * z * this.c2) + (2 * z * this.cd) // C
155
+ + this.d2; // D
85
156
  }
86
157
 
87
158
  /**
159
+ * Find optimal vertex position based on the quadratic
160
+ *
161
+ * Based on Bleder's implementation: https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/blenlib/intern/quadric.c
88
162
  *
89
163
  * @param {number[]} out
164
+ * @returns {boolean}
90
165
  */
91
166
  optimize(out) {
92
- throw new Error('Not Implemented');
167
+ const m = new Float32Array(9);
168
+
169
+ if (this.toTensorM3Inverse(m, EPSILON)) {
170
+
171
+ this.toVector3(out);
172
+
173
+ vec3.transformMat3(out, out, m);
174
+ vec3.negate(out, out);
175
+
176
+ return true;
177
+ }
178
+
179
+ return false;
180
+
93
181
  }
94
182
 
95
183
  /**
@@ -31,7 +31,10 @@ export function build_vertex_quadratics(mesh, quadratics) {
31
31
  const mesh_vertices = mesh.vertices;
32
32
  const mesh_vertex_count = mesh_vertices.length;
33
33
 
34
- for (let i = 0; i < mesh_vertex_count; i++) {
34
+
35
+ let i, face;
36
+
37
+ for (i = 0; i < mesh_vertex_count; i++) {
35
38
  const mesh_vertex = mesh_vertices[i];
36
39
 
37
40
  const vertex_quadratic = new Quadratic3();
@@ -44,15 +47,15 @@ export function build_vertex_quadratics(mesh, quadratics) {
44
47
 
45
48
  const faces = mesh.getFaces();
46
49
 
47
- for (let face of faces) {
50
+ for (face of faces) {
48
51
 
49
52
  q.setFromFace(face);
50
53
 
51
54
  const face_vertices = face.vertices;
52
55
  const face_vertex_count = face_vertices.length;
53
56
 
54
- for (let j = 0; j < face_vertex_count; j++) {
55
- const face_vertex = face_vertices[j];
57
+ for (i = 0; i < face_vertex_count; i++) {
58
+ const face_vertex = face_vertices[i];
56
59
 
57
60
  // accumulate to adjacent vertex
58
61
  const face_vertex_quadratic = quadratics.get(face_vertex.index);
@@ -18,5 +18,11 @@ export function compute_edge_collapse_cost_quadratic(
18
18
  const q_v0 = q0.evaluate(target.x, target.y, target.z);
19
19
  const q_v1 = q1.evaluate(target.x, target.y, target.z);
20
20
 
21
- return q_v0 + q_v1;
21
+ /**
22
+ * 'cost' shouldn't be negative but happens sometimes with small values.
23
+ * @type {number}
24
+ */
25
+ const cost = Math.abs(q_v0 + q_v1);
26
+
27
+ return cost;
22
28
  }
@@ -23,7 +23,6 @@ export function extract_edge_cost(edge) {
23
23
  /**
24
24
  * @param {number} number_faces_to_remove minimum number of faces to removed
25
25
  * @param {BinaryHeap<EdgeCollapseCandidate>} heap
26
- * @param {EdgeCollapseCandidate[]} removed_edges
27
26
  * @param {TopoMesh} mesh
28
27
  * @param {Map<TopoEdge, EdgeCollapseCandidate>} edge_to_collapse_map
29
28
  * @param {Map<number, Quadratic3>} vertex_quadratics
@@ -32,7 +31,6 @@ export function extract_edge_cost(edge) {
32
31
  export function collapse_edges(
33
32
  number_faces_to_remove,
34
33
  heap,
35
- removed_edges,
36
34
  mesh,
37
35
  edge_to_collapse_map,
38
36
  vertex_quadratics,
@@ -64,8 +62,6 @@ export function collapse_edges(
64
62
  // continue;
65
63
  // }
66
64
 
67
- removed_edges.push(collapse_candidate);
68
-
69
65
  // find edges that need to be updated
70
66
  const victim_vertex = collapse_candidate.victim_vertex;
71
67
 
@@ -208,21 +204,15 @@ export function collapse_edges(
208
204
  * @param {TopoMesh} mesh
209
205
  * @param {number} num_faces_to_remove desired number of faces to remove
210
206
  * @param {Set<number>} [restricted_vertices] vertices that are not allowed to be removed
211
- * @returns {EdgeCollapseCandidate[]} collapsed edges
212
207
  */
213
208
  export function simplifyTopoMesh(mesh, num_faces_to_remove, restricted_vertices = new Set()) {
214
- /**
215
- *
216
- * @type {EdgeCollapseCandidate[]}
217
- */
218
- const removed_edges = [];
219
209
 
220
210
  const faces = mesh.getFaces();
221
211
  const face_count = faces.size;
222
212
 
223
213
  if (num_faces_to_remove <= 0 || face_count <= 0) {
224
214
  // we're already at the target
225
- return removed_edges;
215
+ return ;
226
216
  }
227
217
 
228
218
 
@@ -258,7 +248,6 @@ export function simplifyTopoMesh(mesh, num_faces_to_remove, restricted_vertices
258
248
  collapse_edges(
259
249
  num_faces_to_remove,
260
250
  open_set,
261
- removed_edges,
262
251
  mesh,
263
252
  edge_to_collapse_map,
264
253
  vertex_quadratics,
@@ -270,6 +259,4 @@ export function simplifyTopoMesh(mesh, num_faces_to_remove, restricted_vertices
270
259
  collapse_all_degenerate_edges(mesh.getEdges(), mesh);
271
260
 
272
261
  // debugValidateMesh(mesh);
273
-
274
- return removed_edges;
275
262
  }
@@ -2,7 +2,7 @@ import LineBuilder from "../codegen/LineBuilder.js";
2
2
  import { EdgeDirectionType } from "./Edge.js";
3
3
 
4
4
  /**
5
- * Build a DOT representation of the graph
5
+ * Build a Graphviz DOT representation of the graph
6
6
  * @param {Graph} graph
7
7
  * @returns {string}
8
8
  */
@@ -13,7 +13,7 @@ export function convertGraphToDotString(graph) {
13
13
 
14
14
  lb.indent();
15
15
 
16
- const nodes = graph.nodes;
16
+ const nodes = Array.from(graph.getNodes());
17
17
  const nodeCount = nodes.length;
18
18
 
19
19
  function nodeId(index) {
@@ -33,9 +33,9 @@ export function convertGraphToDotString(graph) {
33
33
 
34
34
  const node = nodes[i];
35
35
 
36
- const attachedEdges = graph.getAttachedEdges(node);
36
+ const attachedEdges = [];
37
37
 
38
- const nAttachedEdges = attachedEdges.length;
38
+ const nAttachedEdges = graph.getAttachedEdges(attachedEdges, node);
39
39
 
40
40
  for (let j = 0; j < nAttachedEdges; j++) {
41
41
  const edge = attachedEdges[j];
@@ -335,6 +335,10 @@ export class Graph {
335
335
  * @returns {number}
336
336
  */
337
337
  getAttachedEdges(result, node) {
338
+ assert.defined(result, 'result');
339
+ assert.isArray(result, 'result');
340
+ assert.defined(node, 'node');
341
+
338
342
  const context = this.__nodes.get(node);
339
343
 
340
344
  if (context === undefined) {
@@ -111,13 +111,26 @@ export class Foliage2System extends System {
111
111
 
112
112
  this.renderLayer = graphics.layers.create('instanced-mesh-system');
113
113
 
114
- graphics.views.elements.forEach(this.__handle_view_added, this);
114
+ /**
115
+ *
116
+ * @type {CameraViewManager}
117
+ */
118
+ const views = graphics.views;
119
+
120
+ /**
121
+ *
122
+ * @type {List<CameraView>}
123
+ */
124
+ const view_list = views.elements;
125
+
126
+ view_list.forEach(this.__handle_view_added, this);
115
127
 
116
- graphics.views.elements.on.added.add(this.__handle_view_added, this);
117
- graphics.views.elements.on.removed.add(this.__handle_view_removed, this);
128
+ view_list.on.added.add(this.__handle_view_added, this);
129
+ view_list.on.removed.add(this.__handle_view_removed, this);
118
130
 
119
- this.renderLayer.buildVisibleSet = (view, visitObject, thisArg) => {
131
+ this.renderLayer.buildVisibleSet = (destination, destination_offset, view) => {
120
132
  const data = this.data;
133
+ let additions = 0;
121
134
 
122
135
  for (let entity in data) {
123
136
 
@@ -147,11 +160,15 @@ export class Foliage2System extends System {
147
160
  }
148
161
 
149
162
  const mesh = group.mesh;
150
- visitObject.call(thisArg, mesh);
163
+
164
+ destination[additions + destination_offset] = mesh;
165
+ additions++;
151
166
  }
152
167
 
153
168
  }
154
169
  }
170
+
171
+ return additions;
155
172
  };
156
173
 
157
174
  readyCallback();
@@ -0,0 +1,21 @@
1
+ /**
2
+ *
3
+ * @param {THREE.BufferGeometry} geometry
4
+ * @returns {number}
5
+ */
6
+ export function compute_geometry_polycount(geometry) {
7
+ const index = geometry.getIndex();
8
+
9
+ if (index !== null) {
10
+ return index.count / 3;
11
+ }
12
+
13
+ const position_attribute = geometry.getAttribute('position');
14
+
15
+ if (position_attribute !== undefined) {
16
+ return position_attribute.count / 3;
17
+ }
18
+
19
+ // no valid way to discern polycount, assume 0
20
+ return 0;
21
+ }