@woosh/meep-engine 2.34.34 → 2.34.38
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.
- package/core/binary/BitSet.js +5 -0
- package/core/bvh2/BinaryNode.js +853 -1007
- package/core/bvh2/LeafNode.js +80 -82
- package/core/bvh2/Node.d.ts +3 -0
- package/core/bvh2/Node.js +111 -114
- package/core/bvh2/aabb3/AABB3.js +46 -1
- package/core/bvh2/aabb3/AABB3.spec.js +27 -1
- package/core/bvh2/aabb3/aabb3_from_v3_array.js +34 -0
- package/core/bvh2/aabb3/aabb3_from_v3_array_transformed.js +39 -0
- package/core/bvh2/aabb3/aabb3_intersects_frustum_degree.js +46 -0
- package/core/bvh2/transform/RotationOptimizer.js +2 -262
- package/core/bvh2/transform/tryRotateSingleNode.js +259 -0
- package/core/bvh2/traversal/ThreeFrustumsIntersectionBVHVisitor.js +16 -35
- package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +52 -0
- package/core/events/signal/Signal.js +1 -1
- package/core/geom/3d/aabb/aabb3_computeDistanceAbovePlane.js +1 -1
- package/core/geom/3d/aabb/aabb3_matrix4_project.js +3 -4
- package/core/geom/3d/aabb/aabb3_matrix4_project.spec.js +13 -0
- package/core/geom/3d/triangle/computeTriangleRayIntersection.js +1 -2
- package/core/geom/Quaternion.d.ts +2 -0
- package/core/geom/Vector3.js +22 -11
- package/engine/asset/loaders/ArrayBufferLoader.js +7 -0
- package/engine/asset/loaders/image/ImageRGBADataLoader.js +11 -6
- package/engine/ecs/EntityBuilder.js +12 -5
- package/engine/ecs/EntityComponentDataset.js +26 -6
- package/engine/ecs/attachment/AttachmentSystem.js +1 -1
- package/engine/ecs/attachment/TransformAttachmentBinding.js +0 -1
- package/engine/ecs/foliage/FoliageVisibilitySetBuilder.js +1 -31
- package/engine/ecs/foliage/InstancedFoliage.js +85 -152
- package/engine/ecs/foliage/ViewState.js +165 -0
- package/engine/ecs/foliage/ecs/Foliage2System.js +82 -19
- package/engine/ecs/foliage/ecs/InstancedMeshComponent.js +30 -0
- package/engine/ecs/foliage/ecs/InstancedMeshLayer.js +16 -0
- package/engine/ecs/foliage/ecs/InstancedMeshUtils.js +2 -2
- package/engine/ecs/parent/EntityNode.js +31 -49
- package/engine/ecs/sockets/AttachmentSockets.js +11 -1
- package/engine/ecs/transform-attachment/TransformAttachment.js +88 -0
- package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +59 -0
- package/engine/graphics/GraphicsEngine.js +23 -2
- package/engine/graphics/ecs/camera/Camera.js +33 -16
- package/engine/graphics/ecs/camera/unprojectPoint.js +14 -0
- package/engine/graphics/ecs/light/binding/three/ThreeLightBinding.js +22 -17
- package/engine/graphics/ecs/mesh/updateNodeByTransformAndBBB.js +21 -21
- package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +9 -1
- package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +4 -1
- package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +38 -8
- package/engine/graphics/ecs/water2/shader/AlexWaterShader.js +7 -1
- package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +18 -12
- package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -34
- package/engine/graphics/micron/build/fill_patch_geometry_data.js +12 -0
- package/engine/graphics/micron/format/MicronGeometry.js +21 -0
- package/engine/graphics/micron/format/MicronGeometryPatch.js +0 -7
- package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -3
- package/engine/graphics/micron/prototypeVirtualGeometry.js +69 -1
- package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +5 -1
- package/engine/graphics/render/layers/RenderLayer.js +22 -85
- package/engine/graphics/render/visibility/VisibilityComputer.js +23 -103
- package/engine/sound/ecs/emitter/SoundEmitterSystem.js +1 -1
- package/package.json +1 -1
- package/samples/terrain/editor.js +0 -1
package/core/bvh2/BinaryNode.js
CHANGED
|
@@ -25,1146 +25,1137 @@ import { traverseBinaryNodeUsingVisitor } from "./traversal/traverseBinaryNodeUs
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
*
|
|
28
|
-
* @
|
|
28
|
+
* @type {number}
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
|
-
Protected: 1,
|
|
32
|
-
Marked1: 2,
|
|
33
|
-
Marked2: 4,
|
|
34
|
-
Marked3: 8
|
|
35
|
-
};
|
|
36
|
-
|
|
30
|
+
let stackPointer = 0;
|
|
37
31
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @
|
|
32
|
+
*
|
|
33
|
+
* @type {Node[]}
|
|
40
34
|
*/
|
|
41
|
-
const
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
* @type {null|BinaryNode|LeafNode}
|
|
45
|
-
*/
|
|
46
|
-
this.parentNode = null;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
50
|
-
* @type {null|Node|LeafNode|BinaryNode}
|
|
51
|
-
*/
|
|
52
|
-
this.left = null;
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
55
|
-
* @type {null|Node|LeafNode|BinaryNode}
|
|
56
|
-
*/
|
|
57
|
-
this.right = null;
|
|
35
|
+
const stack = [];
|
|
58
36
|
|
|
59
|
-
|
|
37
|
+
export class BinaryNode extends Node{
|
|
38
|
+
constructor() {
|
|
39
|
+
super();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @type {null|BinaryNode|LeafNode}
|
|
44
|
+
*/
|
|
45
|
+
this.parentNode = null;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* @type {null|Node|LeafNode|BinaryNode}
|
|
50
|
+
*/
|
|
51
|
+
this.left = null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @type {null|Node|LeafNode|BinaryNode}
|
|
56
|
+
*/
|
|
57
|
+
this.right = null;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Number of leaf nodes in the hierarchy
|
|
61
|
+
* @type {number}
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
this.leafNodeCount = 0;
|
|
65
|
+
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
62
|
-
*
|
|
63
|
-
* @
|
|
68
|
+
* Same as traversePreOrder but without recursion. This runs faster thanks to avoidance of function call overhead. Especially useful for deeper trees.
|
|
69
|
+
* @param {BinaryNode~Visitor} visitor
|
|
70
|
+
* @param [thisArg]
|
|
71
|
+
* @deprecated use visitor-based traversal instead of callback-based
|
|
64
72
|
*/
|
|
65
|
-
|
|
73
|
+
traversePreOrderUsingStack(visitor, thisArg) {
|
|
74
|
+
let visitCount = 0;
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
this.y0 = 0;
|
|
69
|
-
this.z0 = 0;
|
|
76
|
+
const stackOffset = stackPointer;
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
78
|
+
stack[stackPointer++] = this;
|
|
79
|
+
let n;
|
|
80
|
+
while (stackPointer-- > stackOffset) {
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
visitCount++;
|
|
77
83
|
|
|
78
|
-
|
|
84
|
+
n = stack[stackPointer];
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
* @readonly
|
|
82
|
-
* @type {boolean}
|
|
83
|
-
*/
|
|
84
|
-
BinaryNode.prototype.isBinaryNode = true;
|
|
86
|
+
const traverseDeeper = visitor.call(thisArg, n);
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.modifiers &= ~modifier;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
88
|
+
if (traverseDeeper !== false && n.isBinaryNode) {
|
|
89
|
+
if (n.right !== null) {
|
|
90
|
+
stack[stackPointer++] = n.right;
|
|
91
|
+
}
|
|
92
|
+
if (n.left !== null) {
|
|
93
|
+
stack[stackPointer++] = n.left;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
};
|
|
98
|
+
// drop stack frame
|
|
99
|
+
stackPointer = stackOffset;
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
};
|
|
101
|
+
return visitCount;
|
|
102
|
+
}
|
|
106
103
|
|
|
107
|
-
/**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Traverse leaf nodes in a fast manner
|
|
106
|
+
* @param {function(node:LeafNode)} visitor
|
|
107
|
+
* @param {*} [thisArg]
|
|
108
|
+
*/
|
|
109
|
+
traverseLeavesPreOrderUsingStack(visitor, thisArg) {
|
|
110
|
+
let visitCount = 0;
|
|
114
111
|
|
|
112
|
+
const stackOffset = stackPointer;
|
|
115
113
|
|
|
116
|
-
|
|
117
|
-
this.left = null;
|
|
118
|
-
this.right = null;
|
|
114
|
+
stack[stackPointer++] = this;
|
|
119
115
|
|
|
120
|
-
|
|
116
|
+
let n;
|
|
121
117
|
|
|
122
|
-
|
|
123
|
-
};
|
|
118
|
+
while (stackPointer-- > stackOffset) {
|
|
124
119
|
|
|
125
|
-
|
|
126
|
-
*
|
|
127
|
-
* @param {Node|BinaryNode} left
|
|
128
|
-
* @param {Node|BinaryNode} right
|
|
129
|
-
*/
|
|
130
|
-
BinaryNode.prototype.setChildren = function (left, right) {
|
|
131
|
-
this.left = left;
|
|
132
|
-
this.right = right;
|
|
120
|
+
visitCount++;
|
|
133
121
|
|
|
134
|
-
|
|
122
|
+
n = stack[stackPointer];
|
|
135
123
|
|
|
136
|
-
|
|
124
|
+
if (n.isLeafNode) {
|
|
125
|
+
visitor.call(thisArg, n);
|
|
126
|
+
} else {
|
|
127
|
+
//a binary node
|
|
128
|
+
if (n.right !== null) {
|
|
129
|
+
stack[stackPointer++] = n.right;
|
|
130
|
+
}
|
|
131
|
+
if (n.left !== null) {
|
|
132
|
+
stack[stackPointer++] = n.left;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
};
|
|
137
|
+
// drop stack frame
|
|
138
|
+
stackPointer = stackOffset;
|
|
141
139
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* @param {Node} child
|
|
145
|
-
*/
|
|
146
|
-
BinaryNode.prototype.setChildLeft = function (child) {
|
|
147
|
-
assert.isNull(this.left, 'left');
|
|
140
|
+
return visitCount;
|
|
141
|
+
}
|
|
148
142
|
|
|
149
|
-
this.left = child;
|
|
150
143
|
|
|
151
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Contains no children
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
isEmpty() {
|
|
149
|
+
return this.left === null && this.right === null;
|
|
150
|
+
}
|
|
152
151
|
|
|
153
|
-
|
|
152
|
+
reset() {
|
|
153
|
+
this.left = null;
|
|
154
|
+
this.right = null;
|
|
154
155
|
|
|
155
|
-
|
|
156
|
-
}
|
|
156
|
+
this.leafNodeCount = 0;
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
* @param {Node} child
|
|
161
|
-
*/
|
|
162
|
-
BinaryNode.prototype.setChildRight = function (child) {
|
|
163
|
-
assert.isNull(this.right, 'right');
|
|
158
|
+
this.setNegativelyInfiniteBounds();
|
|
159
|
+
}
|
|
164
160
|
|
|
165
|
-
|
|
161
|
+
/**
|
|
162
|
+
*
|
|
163
|
+
* @param {Node|BinaryNode} left
|
|
164
|
+
* @param {Node|BinaryNode} right
|
|
165
|
+
*/
|
|
166
|
+
setChildren(left, right) {
|
|
167
|
+
this.left = left;
|
|
168
|
+
this.right = right;
|
|
166
169
|
|
|
167
|
-
|
|
170
|
+
this.updateLeafNodeCount()
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
this.refitFor2();
|
|
170
173
|
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
left.parentNode = this;
|
|
175
|
+
right.parentNode = this;
|
|
176
|
+
}
|
|
173
177
|
|
|
174
|
-
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
function inclusion_surface_area_heuristic(node, addition) {
|
|
181
|
-
const x0 = min2(node.x0, addition.x0);
|
|
182
|
-
const y0 = min2(node.y0, addition.y0);
|
|
183
|
-
const z0 = min2(node.z0, addition.z0);
|
|
178
|
+
/**
|
|
179
|
+
*
|
|
180
|
+
* @param {Node} child
|
|
181
|
+
*/
|
|
182
|
+
setChildLeft(child) {
|
|
183
|
+
assert.isNull(this.left, 'left');
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
const y1 = max2(node.y1, addition.y1);
|
|
187
|
-
const z1 = max2(node.z1, addition.z1);
|
|
185
|
+
this.left = child;
|
|
188
186
|
|
|
189
|
-
|
|
190
|
-
}
|
|
187
|
+
this.updateLeafNodeCount();
|
|
191
188
|
|
|
192
|
-
|
|
193
|
-
*
|
|
194
|
-
* @param {BinaryNode|LeafNode} node
|
|
195
|
-
* @param {AABB3} addition
|
|
196
|
-
*/
|
|
197
|
-
function inclusion_cost_lower_bound(node, addition) {
|
|
198
|
-
let n = node;
|
|
199
|
-
let r = boxSurfaceArea2(addition);
|
|
189
|
+
this.refit();
|
|
200
190
|
|
|
201
|
-
|
|
191
|
+
child.parentNode = this;
|
|
192
|
+
}
|
|
202
193
|
|
|
203
|
-
|
|
194
|
+
/**
|
|
195
|
+
*
|
|
196
|
+
* @param {Node} child
|
|
197
|
+
*/
|
|
198
|
+
setChildRight(child) {
|
|
199
|
+
assert.isNull(this.right, 'right');
|
|
204
200
|
|
|
205
|
-
|
|
206
|
-
}
|
|
201
|
+
this.right = child;
|
|
207
202
|
|
|
208
|
-
|
|
209
|
-
}
|
|
203
|
+
this.updateLeafNodeCount();
|
|
210
204
|
|
|
211
|
-
|
|
212
|
-
* For incremental insertion, a sibling needs to be found, newly inserted node will be parented together with this sibling
|
|
213
|
-
* @param {AABB3} box
|
|
214
|
-
* @returns {BinaryNode|LeafNode}
|
|
215
|
-
*/
|
|
216
|
-
BinaryNode.prototype.__insertion_findSiblingFor = function (box) {
|
|
205
|
+
this.refit();
|
|
217
206
|
|
|
218
|
-
|
|
219
|
-
|
|
207
|
+
child.parentNode = this;
|
|
208
|
+
}
|
|
220
209
|
|
|
221
|
-
|
|
222
|
-
|
|
210
|
+
/**
|
|
211
|
+
* For incremental insertion, a sibling needs to be found, newly inserted node will be parented together with this sibling
|
|
212
|
+
* @param {AABB3} box
|
|
213
|
+
* @returns {BinaryNode|LeafNode}
|
|
214
|
+
*/
|
|
215
|
+
__insertion_findSiblingFor(box) {
|
|
223
216
|
|
|
224
|
-
|
|
225
|
-
|
|
217
|
+
const open_set = [this];
|
|
218
|
+
let cursor = 1;
|
|
226
219
|
|
|
227
|
-
|
|
220
|
+
let best_cost = Infinity;
|
|
221
|
+
let best_node = this;
|
|
228
222
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
best_node = node;
|
|
232
|
-
}
|
|
223
|
+
while (cursor > 0) {
|
|
224
|
+
const node = open_set[--cursor];
|
|
233
225
|
|
|
234
|
-
|
|
226
|
+
const cost = inclusion_surface_area_heuristic(node, box);
|
|
235
227
|
|
|
236
|
-
if (
|
|
237
|
-
|
|
228
|
+
if (cost < best_cost) {
|
|
229
|
+
best_cost = cost;
|
|
230
|
+
best_node = node;
|
|
238
231
|
}
|
|
239
232
|
|
|
240
|
-
|
|
233
|
+
if (node.isBinaryNode) {
|
|
241
234
|
|
|
242
|
-
|
|
243
|
-
const c_low = inclusion_cost_lower_bound(node, box);
|
|
235
|
+
// consider pushing children into the queue
|
|
244
236
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
237
|
+
// compute lower bound for cost
|
|
238
|
+
const c_low = inclusion_cost_lower_bound(node, box);
|
|
239
|
+
|
|
240
|
+
if (c_low < best_cost) {
|
|
241
|
+
// worth-while to explore children
|
|
242
|
+
if (node.left !== null) {
|
|
243
|
+
open_set[cursor++] = node.left;
|
|
244
|
+
}
|
|
245
|
+
if (node.right !== null) {
|
|
246
|
+
open_set[cursor++] = node.right
|
|
247
|
+
}
|
|
252
248
|
}
|
|
253
249
|
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return best_node;
|
|
260
|
-
}
|
|
261
250
|
|
|
262
|
-
/**
|
|
263
|
-
*
|
|
264
|
-
* @param {AABB3} box
|
|
265
|
-
* @returns {Node|BinaryNode|LeafNode}
|
|
266
|
-
*/
|
|
267
|
-
BinaryNode.prototype.findParentFor = function (box) {
|
|
268
|
-
let n = this;
|
|
269
|
-
let aCost = 0;
|
|
270
|
-
let bCost = 0;
|
|
271
251
|
|
|
272
|
-
|
|
252
|
+
}
|
|
273
253
|
|
|
274
|
-
|
|
275
|
-
|
|
254
|
+
return best_node;
|
|
255
|
+
}
|
|
276
256
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
257
|
+
/**
|
|
258
|
+
*
|
|
259
|
+
* @param {AABB3} box
|
|
260
|
+
* @returns {Node|BinaryNode|LeafNode}
|
|
261
|
+
*/
|
|
262
|
+
findParentFor(box) {
|
|
263
|
+
let n = this;
|
|
264
|
+
let aCost = 0;
|
|
265
|
+
let bCost = 0;
|
|
281
266
|
|
|
282
|
-
|
|
283
|
-
const bIsBinary = b.isBinaryNode;
|
|
267
|
+
for (; ;) {
|
|
284
268
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (bIsBinary) {
|
|
288
|
-
if (b.isProtected()) {
|
|
289
|
-
return n;
|
|
290
|
-
} else {
|
|
291
|
-
n = b;
|
|
269
|
+
const a = n.left;
|
|
270
|
+
const b = n.right;
|
|
292
271
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return b;
|
|
272
|
+
if (a === null || b === null) {
|
|
273
|
+
//unbalanced node, good candidate already
|
|
274
|
+
return n;
|
|
297
275
|
}
|
|
298
|
-
} else if (bIsBinary && b.isProtected()) {
|
|
299
|
-
if (aIsBinary) {
|
|
300
|
-
n = a;
|
|
301
|
-
} else {
|
|
302
|
-
return a;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
276
|
|
|
306
|
-
|
|
307
|
-
|
|
277
|
+
const aIsBinary = a.isBinaryNode;
|
|
278
|
+
const bIsBinary = b.isBinaryNode;
|
|
308
279
|
|
|
309
|
-
|
|
280
|
+
aCost = a.costForInclusion(box);
|
|
281
|
+
bCost = b.costForInclusion(box);
|
|
310
282
|
|
|
311
|
-
if (
|
|
283
|
+
if (aCost === bCost) {
|
|
312
284
|
|
|
313
|
-
|
|
285
|
+
if (a.isLeafNode) {
|
|
314
286
|
|
|
315
|
-
|
|
287
|
+
return a;
|
|
316
288
|
|
|
317
|
-
|
|
289
|
+
} else if (b.isLeafNode) {
|
|
318
290
|
|
|
319
|
-
|
|
291
|
+
return b;
|
|
320
292
|
|
|
321
|
-
|
|
322
|
-
aCost = a.leafNodeCount / boxSurfaceArea2(a);
|
|
323
|
-
bCost = b.leafNodeCount / boxSurfaceArea2(b);
|
|
324
|
-
}
|
|
293
|
+
}
|
|
325
294
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
295
|
+
//change costs to be based on balance
|
|
296
|
+
aCost = a.leafNodeCount / boxSurfaceArea2(a);
|
|
297
|
+
bCost = b.leafNodeCount / boxSurfaceArea2(b);
|
|
298
|
+
}
|
|
331
299
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
return a;
|
|
300
|
+
if (aCost === bCost) {
|
|
301
|
+
//still the same, toss a coin
|
|
302
|
+
aCost = Math.random();
|
|
303
|
+
bCost = 1 - aCost;
|
|
337
304
|
}
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
|
|
305
|
+
|
|
306
|
+
if (aCost < bCost) {
|
|
307
|
+
if (aIsBinary) {
|
|
308
|
+
n = a;
|
|
309
|
+
} else {
|
|
310
|
+
return a;
|
|
311
|
+
}
|
|
341
312
|
} else {
|
|
342
|
-
|
|
313
|
+
if (bIsBinary) {
|
|
314
|
+
n = b;
|
|
315
|
+
} else {
|
|
316
|
+
return b;
|
|
317
|
+
}
|
|
343
318
|
}
|
|
344
319
|
}
|
|
345
320
|
}
|
|
346
|
-
};
|
|
347
321
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
322
|
+
traverse(visitor) {
|
|
323
|
+
if (this.left !== null) {
|
|
324
|
+
const cA = visitor(this.left);
|
|
325
|
+
if (cA !== false) {
|
|
326
|
+
this.left.traverse(visitor);
|
|
327
|
+
}
|
|
353
328
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
329
|
+
if (this.right !== null) {
|
|
330
|
+
const cB = visitor(this.right);
|
|
331
|
+
if (cB !== false) {
|
|
332
|
+
this.right.traverse(visitor);
|
|
333
|
+
}
|
|
359
334
|
}
|
|
360
335
|
}
|
|
361
|
-
};
|
|
362
336
|
|
|
363
|
-
/**
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
//left
|
|
369
|
-
if (this.left instanceof BinaryNode) {
|
|
370
|
-
this.left.traversePostOrder(visitor);
|
|
371
|
-
} else if (this.left instanceof LeafNode) {
|
|
372
|
-
visitor(this.left);
|
|
373
|
-
}
|
|
374
|
-
//right
|
|
375
|
-
if (this.right instanceof BinaryNode) {
|
|
376
|
-
this.right.traversePostOrder(visitor);
|
|
377
|
-
} else if (this.right instanceof LeafNode) {
|
|
378
|
-
visitor(this.right);
|
|
379
|
-
}
|
|
380
|
-
visitor(this);
|
|
381
|
-
};
|
|
382
|
-
BinaryNode.prototype.traversePreOrder = function (visitor) {
|
|
383
|
-
const carryOn = visitor(this);
|
|
384
|
-
if (carryOn !== false) {
|
|
337
|
+
/**
|
|
338
|
+
* Bottom-up tree traversal, children first
|
|
339
|
+
* @param visitor
|
|
340
|
+
*/
|
|
341
|
+
traversePostOrder(visitor) {
|
|
385
342
|
//left
|
|
386
343
|
if (this.left instanceof BinaryNode) {
|
|
387
|
-
this.left.
|
|
344
|
+
this.left.traversePostOrder(visitor);
|
|
388
345
|
} else if (this.left instanceof LeafNode) {
|
|
389
346
|
visitor(this.left);
|
|
390
347
|
}
|
|
391
348
|
//right
|
|
392
349
|
if (this.right instanceof BinaryNode) {
|
|
393
|
-
this.right.
|
|
350
|
+
this.right.traversePostOrder(visitor);
|
|
394
351
|
} else if (this.right instanceof LeafNode) {
|
|
395
352
|
visitor(this.right);
|
|
396
353
|
}
|
|
354
|
+
visitor(this);
|
|
397
355
|
}
|
|
398
|
-
};
|
|
399
|
-
|
|
400
356
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
357
|
+
traversePreOrder(visitor) {
|
|
358
|
+
const carryOn = visitor(this);
|
|
359
|
+
if (carryOn !== false) {
|
|
360
|
+
//left
|
|
361
|
+
if (this.left instanceof BinaryNode) {
|
|
362
|
+
this.left.traversePreOrder(visitor);
|
|
363
|
+
} else if (this.left instanceof LeafNode) {
|
|
364
|
+
visitor(this.left);
|
|
365
|
+
}
|
|
366
|
+
//right
|
|
367
|
+
if (this.right instanceof BinaryNode) {
|
|
368
|
+
this.right.traversePreOrder(visitor);
|
|
369
|
+
} else if (this.right instanceof LeafNode) {
|
|
370
|
+
visitor(this.right);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
408
374
|
|
|
375
|
+
refitFor2() {
|
|
376
|
+
const a = this.left;
|
|
377
|
+
const b = this.right;
|
|
409
378
|
|
|
410
|
-
|
|
411
|
-
|
|
379
|
+
this.x0 = min2(a.x0, b.x0);
|
|
380
|
+
this.y0 = min2(a.y0, b.y0);
|
|
381
|
+
this.z0 = min2(a.z0, b.z0);
|
|
412
382
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
383
|
+
this.x1 = max2(a.x1, b.x1);
|
|
384
|
+
this.y1 = max2(a.y1, b.y1);
|
|
385
|
+
this.z1 = max2(a.z1, b.z1);
|
|
416
386
|
}
|
|
417
387
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
* @param {*} [thisArg]
|
|
421
|
-
* @returns {number}
|
|
422
|
-
*/
|
|
423
|
-
function traversePreOrderUsingStack(visitor, thisArg) {
|
|
424
|
-
let visitCount = 0;
|
|
388
|
+
bubbleRefit() {
|
|
389
|
+
let n = this;
|
|
425
390
|
|
|
426
|
-
|
|
391
|
+
n.refit();
|
|
427
392
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
393
|
+
//bubble up
|
|
394
|
+
for (; ;) {
|
|
395
|
+
//record old node size
|
|
396
|
+
const _x0 = n.x0;
|
|
397
|
+
const _y0 = n.y0;
|
|
398
|
+
const _z0 = n.z0;
|
|
399
|
+
const _x1 = n.x1;
|
|
400
|
+
const _y1 = n.y1;
|
|
401
|
+
const _z1 = n.z1;
|
|
431
402
|
|
|
432
|
-
|
|
403
|
+
n = n.parentNode;
|
|
433
404
|
|
|
434
|
-
n
|
|
405
|
+
if (n === null) {
|
|
406
|
+
// hit the root
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
435
409
|
|
|
436
|
-
const traverseDeeper = visitor.call(thisArg, n);
|
|
437
410
|
|
|
438
|
-
if
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
411
|
+
//check if this node violates bounds of the parent
|
|
412
|
+
let flag = false;
|
|
413
|
+
|
|
414
|
+
if (_x0 < n.x0) {
|
|
415
|
+
n.x0 = _x0;
|
|
416
|
+
flag = true;
|
|
417
|
+
}
|
|
418
|
+
if (_y0 < n.y0) {
|
|
419
|
+
n.y0 = _y0;
|
|
420
|
+
flag = true;
|
|
421
|
+
}
|
|
422
|
+
if (_z0 < n.z0) {
|
|
423
|
+
n.z0 = _z0;
|
|
424
|
+
flag = true;
|
|
445
425
|
}
|
|
446
|
-
}
|
|
447
426
|
|
|
448
|
-
|
|
427
|
+
if (_x1 > n.x1) {
|
|
428
|
+
n.x1 = _x1;
|
|
429
|
+
flag = true;
|
|
430
|
+
}
|
|
431
|
+
if (_y1 > n.y1) {
|
|
432
|
+
n.y1 = _y1;
|
|
433
|
+
flag = true;
|
|
434
|
+
}
|
|
435
|
+
if (_z1 > n.z1) {
|
|
436
|
+
n.z1 = _z1;
|
|
437
|
+
flag = true;
|
|
438
|
+
}
|
|
449
439
|
|
|
450
|
-
|
|
440
|
+
if (!flag) {
|
|
441
|
+
//no violation, we can stop here
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
451
445
|
}
|
|
452
446
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
447
|
+
refit() {
|
|
448
|
+
if (this.left !== null && this.right !== null) {
|
|
449
|
+
this.refitFor2();
|
|
450
|
+
} else if (this.left !== null) {
|
|
451
|
+
this.copy(this.left);
|
|
452
|
+
} else if (this.right !== null) {
|
|
453
|
+
this.copy(this.right);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
462
456
|
|
|
457
|
+
sumSurfaceArea() {
|
|
458
|
+
let result = 0;
|
|
459
|
+
this.traversePreOrderUsingStack(function (node) {
|
|
460
|
+
const nodeArea = node.computeSurfaceArea();
|
|
461
|
+
result += nodeArea;
|
|
462
|
+
});
|
|
463
463
|
|
|
464
|
-
|
|
465
|
-
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
466
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
467
|
+
countDescendants() {
|
|
468
|
+
let result = 0;
|
|
469
|
+
this.traversePreOrderUsingStack(function (node) {
|
|
470
|
+
result++;
|
|
471
|
+
});
|
|
472
|
+
return result;
|
|
470
473
|
}
|
|
471
474
|
|
|
472
475
|
/**
|
|
473
|
-
* @
|
|
474
|
-
* @
|
|
475
|
-
* @returns {number}
|
|
476
|
+
* @deprecated Incorrect usage of SAH
|
|
477
|
+
* @return {number}
|
|
476
478
|
*/
|
|
477
|
-
|
|
478
|
-
let
|
|
479
|
+
computeSAH() {
|
|
480
|
+
let leftLeaves, rightLeaves, leftArea, rightArea;
|
|
479
481
|
|
|
480
|
-
const
|
|
482
|
+
const left = this.left;
|
|
481
483
|
|
|
482
|
-
|
|
484
|
+
if (left === null) {
|
|
485
|
+
leftArea = 0;
|
|
486
|
+
leftLeaves = 0;
|
|
487
|
+
} else {
|
|
488
|
+
leftArea = boxSurfaceArea2(left);
|
|
489
|
+
leftLeaves = left.leafNodeCount;
|
|
490
|
+
}
|
|
483
491
|
|
|
484
|
-
|
|
492
|
+
const right = this.right;
|
|
493
|
+
if (right === null) {
|
|
494
|
+
rightArea = 0;
|
|
495
|
+
rightLeaves = 0;
|
|
496
|
+
} else {
|
|
497
|
+
rightArea = boxSurfaceArea2(right);
|
|
498
|
+
rightLeaves = right.leafNodeCount;
|
|
499
|
+
}
|
|
485
500
|
|
|
486
|
-
|
|
501
|
+
const thisArea = boxSurfaceArea2(this);
|
|
487
502
|
|
|
488
|
-
|
|
503
|
+
return surfaceAreaHeuristic(thisArea, leftArea, rightArea, leftLeaves, rightLeaves);
|
|
504
|
+
}
|
|
489
505
|
|
|
490
|
-
|
|
506
|
+
updateLeafNodeCount() {
|
|
507
|
+
let node = this;
|
|
491
508
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
509
|
+
while (node !== null) {
|
|
510
|
+
let n = 0;
|
|
511
|
+
|
|
512
|
+
const right = node.right;
|
|
513
|
+
|
|
514
|
+
if (right !== null) {
|
|
515
|
+
if (right.isLeafNode) {
|
|
516
|
+
n++;
|
|
517
|
+
} else {
|
|
518
|
+
n += right.leafNodeCount;
|
|
498
519
|
}
|
|
499
|
-
|
|
500
|
-
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const left = this.left;
|
|
523
|
+
|
|
524
|
+
if (left !== null) {
|
|
525
|
+
if (left.isLeafNode) {
|
|
526
|
+
n++;
|
|
527
|
+
} else {
|
|
528
|
+
n += left.leafNodeCount;
|
|
501
529
|
}
|
|
502
530
|
}
|
|
503
|
-
}
|
|
504
531
|
|
|
505
|
-
|
|
532
|
+
node.leafNodeCount = n;
|
|
533
|
+
|
|
534
|
+
// propagate update up the tree
|
|
535
|
+
node = node.parentNode;
|
|
536
|
+
}
|
|
506
537
|
|
|
507
|
-
return visitCount;
|
|
508
538
|
}
|
|
509
539
|
|
|
510
|
-
|
|
511
|
-
|
|
540
|
+
/**
|
|
541
|
+
* As a result of rotation, this node 'becomes' the right node, and left node get's replaced by the right node with necessary adjustments
|
|
542
|
+
*/
|
|
543
|
+
rotateLeft() {
|
|
512
544
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
545
|
+
/**
|
|
546
|
+
*
|
|
547
|
+
* @type {BinaryNode}
|
|
548
|
+
*/
|
|
549
|
+
const r = this.right;
|
|
516
550
|
|
|
517
|
-
|
|
518
|
-
this.y0 = min2(a.y0, b.y0);
|
|
519
|
-
this.z0 = min2(a.z0, b.z0);
|
|
551
|
+
const l = this.left;
|
|
520
552
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
this.z1 = max2(a.z1, b.z1);
|
|
524
|
-
};
|
|
553
|
+
const rL = r.left;
|
|
554
|
+
const rR = r.right;
|
|
525
555
|
|
|
526
|
-
|
|
527
|
-
|
|
556
|
+
this.left = r;
|
|
557
|
+
this.right = rR;
|
|
528
558
|
|
|
529
|
-
|
|
559
|
+
if (rR !== null) {
|
|
560
|
+
rR.parentNode = this;
|
|
561
|
+
}
|
|
530
562
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const _y0 = n.y0;
|
|
536
|
-
const _z0 = n.z0;
|
|
537
|
-
const _x1 = n.x1;
|
|
538
|
-
const _y1 = n.y1;
|
|
539
|
-
const _z1 = n.z1;
|
|
563
|
+
r.left = l;
|
|
564
|
+
if (l !== null) {
|
|
565
|
+
l.parentNode = r;
|
|
566
|
+
}
|
|
540
567
|
|
|
541
|
-
|
|
568
|
+
r.right = rL;
|
|
542
569
|
|
|
543
|
-
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
570
|
+
r.updateLeafNodeCount();
|
|
546
571
|
|
|
547
|
-
|
|
572
|
+
r.bubbleRefit();
|
|
573
|
+
}
|
|
548
574
|
|
|
549
|
-
|
|
575
|
+
rotateRight() {
|
|
576
|
+
const r = this.right;
|
|
550
577
|
|
|
551
|
-
|
|
552
|
-
n.x0 = _x0;
|
|
553
|
-
flag = true;
|
|
554
|
-
}
|
|
555
|
-
if (_y0 < n.y0) {
|
|
556
|
-
n.y0 = _y0;
|
|
557
|
-
flag = true;
|
|
558
|
-
}
|
|
559
|
-
if (_z0 < n.z0) {
|
|
560
|
-
n.z0 = _z0;
|
|
561
|
-
flag = true;
|
|
562
|
-
}
|
|
578
|
+
const l = this.left;
|
|
563
579
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
n.y1 = _y1;
|
|
570
|
-
flag = true;
|
|
571
|
-
}
|
|
572
|
-
if (_z1 > n.z1) {
|
|
573
|
-
n.z1 = _z1;
|
|
574
|
-
flag = true;
|
|
580
|
+
const lL = l.left;
|
|
581
|
+
const lR = l.right;
|
|
582
|
+
|
|
583
|
+
if (lL !== null) {
|
|
584
|
+
lL.parentNode = this;
|
|
575
585
|
}
|
|
576
586
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
587
|
+
l.right = r;
|
|
588
|
+
if (r !== null) {
|
|
589
|
+
r.parentNode = l;
|
|
580
590
|
}
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
BinaryNode.prototype.refit = function () {
|
|
584
|
-
if (this.left !== null && this.right !== null) {
|
|
585
|
-
this.refitFor2();
|
|
586
|
-
} else if (this.left !== null) {
|
|
587
|
-
this.copy(this.left);
|
|
588
|
-
} else if (this.right !== null) {
|
|
589
|
-
this.copy(this.right);
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
591
|
|
|
593
|
-
|
|
594
|
-
*
|
|
595
|
-
* @param {Node} n0
|
|
596
|
-
* @param {Node} n1
|
|
597
|
-
* @return {number}
|
|
598
|
-
*/
|
|
599
|
-
function sortMortonCodes(n0, n1) {
|
|
600
|
-
return n0._mortonCode - n1._mortonCode;
|
|
601
|
-
}
|
|
592
|
+
l.left = lR;
|
|
602
593
|
|
|
603
|
-
|
|
604
|
-
let result = 0;
|
|
605
|
-
this.traversePreOrderUsingStack(function (node) {
|
|
606
|
-
const nodeArea = node.computeSurfaceArea();
|
|
607
|
-
result += nodeArea;
|
|
608
|
-
});
|
|
594
|
+
l.updateLeafNodeCount();
|
|
609
595
|
|
|
610
|
-
|
|
611
|
-
}
|
|
596
|
+
l.bubbleRefit();
|
|
597
|
+
}
|
|
612
598
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
599
|
+
/**
|
|
600
|
+
*
|
|
601
|
+
* @param {function(index:int):LeafNode} leafFactory
|
|
602
|
+
* @param {int} numNodes
|
|
603
|
+
*/
|
|
604
|
+
insertManyBoxes2(leafFactory, numNodes) {
|
|
605
|
+
let i, n;
|
|
606
|
+
//create leaf nodes
|
|
607
|
+
const nodes = new Array(numNodes);
|
|
620
608
|
|
|
609
|
+
for (i = 0; i < numNodes; i++) {
|
|
610
|
+
//leaf needs to be set up inside the callback
|
|
611
|
+
n = leafFactory(i);
|
|
621
612
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
* @param node
|
|
625
|
-
*/
|
|
626
|
-
function countLeaves(node) {
|
|
627
|
-
if (isLeaf(node)) {
|
|
628
|
-
return 1;
|
|
629
|
-
}
|
|
613
|
+
nodes[i] = n;
|
|
614
|
+
}
|
|
630
615
|
|
|
631
|
-
|
|
616
|
+
//sort leaves
|
|
617
|
+
arrayQuickSort(nodes, n => n.computeMortonCode(), null, 0, max2(0, numNodes - 1));
|
|
632
618
|
|
|
633
|
-
|
|
634
|
-
if (isLeaf(node)) {
|
|
635
|
-
result++;
|
|
636
|
-
}
|
|
637
|
-
});
|
|
619
|
+
let remaining_nodes = numNodes;
|
|
638
620
|
|
|
639
|
-
|
|
640
|
-
}
|
|
621
|
+
while (remaining_nodes > 2) {
|
|
641
622
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
623
|
+
//pair
|
|
624
|
+
for (i = 0; i < remaining_nodes - 1; i += 2) {
|
|
625
|
+
const a = nodes[i];
|
|
626
|
+
const b = nodes[i + 1];
|
|
627
|
+
n = new BinaryNode();
|
|
628
|
+
n.setChildren(a, b);
|
|
629
|
+
nodes[i >> 1] = n;
|
|
630
|
+
}
|
|
648
631
|
|
|
649
|
-
|
|
632
|
+
const numNodesMod2 = remaining_nodes & 1; //faster version of %2
|
|
650
633
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
leftArea = boxSurfaceArea2(left);
|
|
656
|
-
leftLeaves = left.leafNodeCount;
|
|
657
|
-
}
|
|
634
|
+
if (numNodesMod2 !== 0) {
|
|
635
|
+
//shift remaining node up so it will be considered later
|
|
636
|
+
nodes[i >> 1] = nodes[i];
|
|
637
|
+
}
|
|
658
638
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
rightArea = 0;
|
|
662
|
-
rightLeaves = 0;
|
|
663
|
-
} else {
|
|
664
|
-
rightArea = boxSurfaceArea2(right);
|
|
665
|
-
rightLeaves = right.leafNodeCount;
|
|
666
|
-
}
|
|
639
|
+
remaining_nodes = (remaining_nodes >> 1) + numNodesMod2;
|
|
640
|
+
}
|
|
667
641
|
|
|
668
|
-
|
|
642
|
+
//finally insert these boxes from this node
|
|
643
|
+
for (i = 0; i < remaining_nodes; i++) {
|
|
644
|
+
n = nodes[i];
|
|
645
|
+
this.insertNode(n);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
669
648
|
|
|
670
|
-
|
|
671
|
-
|
|
649
|
+
/**
|
|
650
|
+
*
|
|
651
|
+
* @param {LeafNode[]} result destination
|
|
652
|
+
* @param {number} x0
|
|
653
|
+
* @param {number} y0
|
|
654
|
+
* @param {number} z0
|
|
655
|
+
* @param {number} x1
|
|
656
|
+
* @param {number} y1
|
|
657
|
+
* @param {number} z1
|
|
658
|
+
* @returns {number} number of objects added to the result
|
|
659
|
+
*/
|
|
660
|
+
requestLeafIntersectionsAABB3(result, x0, y0, z0, x1, y1, z1) {
|
|
661
|
+
const startOffset = stackPointer;
|
|
672
662
|
|
|
673
|
-
|
|
674
|
-
let n = 0;
|
|
663
|
+
stack[stackPointer++] = this;
|
|
675
664
|
|
|
676
|
-
|
|
665
|
+
let count = 0;
|
|
677
666
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
} else {
|
|
682
|
-
n += right.leafNodeCount;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
667
|
+
while (stackPointer > startOffset) {
|
|
668
|
+
stackPointer--;
|
|
669
|
+
const node = stack[stackPointer];
|
|
685
670
|
|
|
686
|
-
|
|
671
|
+
if (!aabb3_intersect_aabb3(
|
|
672
|
+
x0, y0, z0, x1, y1, z1,
|
|
673
|
+
node.x0, node.y0, node.z0, node.x1, node.y1, node.z1
|
|
674
|
+
)) {
|
|
675
|
+
//no overlap
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
687
678
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
n++;
|
|
691
|
-
} else {
|
|
692
|
-
n += left.leafNodeCount;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
679
|
+
if (node.isLeafNode) {
|
|
680
|
+
result[count++] = node;
|
|
695
681
|
|
|
696
|
-
|
|
682
|
+
count++;
|
|
683
|
+
} else {
|
|
684
|
+
const left = node.left;
|
|
685
|
+
if (left !== null) {
|
|
686
|
+
stack[stackPointer++] = left;
|
|
687
|
+
}
|
|
697
688
|
|
|
698
|
-
|
|
699
|
-
|
|
689
|
+
const right = node.right;
|
|
690
|
+
if (right !== null) {
|
|
691
|
+
stack[stackPointer++] = right;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
700
695
|
|
|
701
|
-
|
|
702
|
-
// bubble up
|
|
703
|
-
parentNode.updateLeafNodeCount();
|
|
696
|
+
return count;
|
|
704
697
|
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
698
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
699
|
+
traverseSegmentLeafIntersections(startX, startY, startZ, endX, endY, endZ, visitor) {
|
|
700
|
+
this.traversePreOrder(function (node) {
|
|
701
|
+
let b = node.intersectSegment(startX, startY, startZ, endX, endY, endZ);
|
|
702
|
+
if (!b) {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
if (node instanceof LeafNode) {
|
|
706
|
+
visitor(node);
|
|
707
|
+
return false;
|
|
708
|
+
} else {
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
}
|
|
712
713
|
|
|
713
714
|
/**
|
|
714
715
|
*
|
|
715
|
-
* @
|
|
716
|
+
* @param {Frustum[]} frustums Collection of THREE.js Frustums
|
|
717
|
+
* @param {function(node:LeafNode, boolean)} visitor
|
|
718
|
+
* @returns {number}
|
|
719
|
+
* @deprecated Use {@link ThreeFrustumsIntersectionBVHVisitor}
|
|
716
720
|
*/
|
|
717
|
-
|
|
721
|
+
threeTraverseFrustumsIntersections(frustums, visitor) {
|
|
722
|
+
const numFrustums = frustums.length;
|
|
718
723
|
|
|
719
|
-
|
|
724
|
+
/**
|
|
725
|
+
*
|
|
726
|
+
* @param {LeafNode|BinaryNode} node
|
|
727
|
+
* @returns {boolean}
|
|
728
|
+
*/
|
|
729
|
+
function visitNode(node) {
|
|
730
|
+
let b = false;
|
|
731
|
+
let i = 0;
|
|
720
732
|
|
|
721
|
-
|
|
722
|
-
const rR = r.right;
|
|
733
|
+
for (; i < numFrustums; i++) {
|
|
723
734
|
|
|
724
|
-
|
|
725
|
-
this.right = rR;
|
|
735
|
+
const degree = node.intersectFrustumDegree(frustums[i]);
|
|
726
736
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
737
|
+
if (degree === 2) {
|
|
738
|
+
//completely inside frustum
|
|
730
739
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
740
|
+
if (node.isLeafNode) {
|
|
741
|
+
visitor(node, true);
|
|
742
|
+
} else {
|
|
743
|
+
node.traverseLeavesPreOrderUsingStack(fastVisitLeafNodeIntersection, visitor);
|
|
744
|
+
}
|
|
735
745
|
|
|
736
|
-
|
|
746
|
+
//prevent further traversal
|
|
747
|
+
return false;
|
|
737
748
|
|
|
738
|
-
|
|
749
|
+
} else if (degree === 1) {
|
|
739
750
|
|
|
740
|
-
|
|
741
|
-
|
|
751
|
+
//partially inside frustum
|
|
752
|
+
b = true;
|
|
742
753
|
|
|
743
|
-
|
|
744
|
-
|
|
754
|
+
}
|
|
755
|
+
}
|
|
745
756
|
|
|
746
|
-
|
|
757
|
+
if (!b) {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
747
760
|
|
|
748
|
-
|
|
749
|
-
|
|
761
|
+
if (node.isLeafNode) {
|
|
762
|
+
visitor(node, false);
|
|
763
|
+
}
|
|
750
764
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
765
|
+
return true;
|
|
766
|
+
}
|
|
754
767
|
|
|
755
|
-
|
|
756
|
-
if (r !== null) {
|
|
757
|
-
r.parentNode = l;
|
|
768
|
+
return this.traversePreOrderUsingStack(visitNode);
|
|
758
769
|
}
|
|
759
770
|
|
|
760
|
-
|
|
771
|
+
/**
|
|
772
|
+
* As recursion goes on, we remove frustums and planes from checks reducing total number of checks necessary
|
|
773
|
+
* @param {Array.<THREE.Frustum>} frustums
|
|
774
|
+
* @param {function} visitor
|
|
775
|
+
* @returns {number}
|
|
776
|
+
*/
|
|
777
|
+
threeTraverseFrustumsIntersections2(frustums, visitor) {
|
|
778
|
+
const numFrustums = frustums.length;
|
|
761
779
|
|
|
762
|
-
|
|
780
|
+
function visitLeaves(node) {
|
|
781
|
+
if (node instanceof LeafNode) {
|
|
782
|
+
visitor(node);
|
|
783
|
+
return false;
|
|
784
|
+
} else {
|
|
785
|
+
return true;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
763
788
|
|
|
764
|
-
|
|
765
|
-
|
|
789
|
+
function fastTraverseLeaves(node) {
|
|
790
|
+
if (node instanceof LeafNode) {
|
|
791
|
+
visitor(node);
|
|
792
|
+
} else {
|
|
793
|
+
node.traversePreOrderUsingStack(visitLeaves);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
766
796
|
|
|
767
|
-
/**
|
|
768
|
-
*
|
|
769
|
-
* @param {function(index:int):LeafNode} leafFactory
|
|
770
|
-
* @param {int} numNodes
|
|
771
|
-
*/
|
|
772
|
-
BinaryNode.prototype.insertManyBoxes2 = function (leafFactory, numNodes) {
|
|
773
|
-
let i, n;
|
|
774
|
-
//create leaf nodes
|
|
775
|
-
const nodes = new Array(numNodes);
|
|
776
797
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
798
|
+
function isFlagSet(mask, index) {
|
|
799
|
+
return mask & (1 << index) !== 0;
|
|
800
|
+
}
|
|
780
801
|
|
|
781
|
-
|
|
782
|
-
|
|
802
|
+
function clearFlag(mask, index) {
|
|
803
|
+
return mask & ~(1 << index);
|
|
804
|
+
}
|
|
783
805
|
|
|
784
|
-
|
|
785
|
-
|
|
806
|
+
function removeElementFromClone(original, clone, mask, originalIndex) {
|
|
807
|
+
if (clone === original) {
|
|
808
|
+
//clone planes
|
|
809
|
+
clone = clone.slice();
|
|
810
|
+
}
|
|
811
|
+
//remove the plane from new set
|
|
812
|
+
clone.splice(originalIndex, 1);
|
|
813
|
+
return clone;
|
|
814
|
+
}
|
|
786
815
|
|
|
787
|
-
|
|
816
|
+
function processVolumesIntersections(node, planeSets, planeSetCount) {
|
|
817
|
+
let newPlaneSets = planeSets;
|
|
818
|
+
for (let iPS = planeSetCount - 1; iPS >= 0; iPS--) {
|
|
819
|
+
|
|
820
|
+
const planes = planeSets[iPS];
|
|
821
|
+
let newPlanes = planes;
|
|
822
|
+
const numPlanes = planes.length;
|
|
823
|
+
|
|
824
|
+
//check plane set
|
|
825
|
+
for (let i = numPlanes - 1; i >= 0; i--) {
|
|
826
|
+
const plane = planes[i];
|
|
827
|
+
const planeSide = node.computePlaneSide(plane);
|
|
828
|
+
if (planeSide < 0) {
|
|
829
|
+
//completely inside
|
|
830
|
+
newPlanes = removeElementFromClone(planes, newPlanes, i);
|
|
831
|
+
} else if (planeSide > 0) {
|
|
832
|
+
//on the wrong side of the plane
|
|
833
|
+
//drop plane set
|
|
834
|
+
newPlaneSets = removeElementFromClone(planeSets, newPlaneSets, iPS);
|
|
835
|
+
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
788
839
|
|
|
789
|
-
|
|
840
|
+
//update plane sets as needed
|
|
841
|
+
if (newPlanes !== planes && newPlaneSets.indexOf(planes) !== -1) {
|
|
842
|
+
//replace
|
|
843
|
+
if (newPlaneSets === planeSets) {
|
|
844
|
+
//clone if needed
|
|
845
|
+
newPlaneSets = newPlaneSets.slice();
|
|
846
|
+
}
|
|
847
|
+
newPlaneSets[iPS] = newPlanes;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
790
850
|
|
|
791
|
-
|
|
792
|
-
for (i = 0; i < remaining_nodes - 1; i += 2) {
|
|
793
|
-
const a = nodes[i];
|
|
794
|
-
const b = nodes[i + 1];
|
|
795
|
-
n = new BinaryNode();
|
|
796
|
-
n.setChildren(a, b);
|
|
797
|
-
nodes[i >> 1] = n;
|
|
851
|
+
return newPlaneSets;
|
|
798
852
|
}
|
|
799
853
|
|
|
800
|
-
|
|
854
|
+
function traverseVolumeIntersections(node, planeSets, numPlaneSets) {
|
|
855
|
+
function internalVisitor(node) {
|
|
856
|
+
let newPlaneSet = processVolumesIntersections(node, planeSets, numPlaneSets);
|
|
857
|
+
if (newPlaneSet !== planeSets) {
|
|
858
|
+
let newPlaneSetCount = newPlaneSet.length;
|
|
859
|
+
if (newPlaneSetCount !== 0) {
|
|
860
|
+
//some intersections
|
|
861
|
+
|
|
862
|
+
//check if all plane sets are trivial (contain no test planes)
|
|
863
|
+
let trivialPlaneSets = 0;
|
|
864
|
+
for (let i = 0; i < newPlaneSetCount; i++) {
|
|
865
|
+
if (newPlaneSet[i].length === 0) {
|
|
866
|
+
trivialPlaneSets++;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (isLeaf(node)) {
|
|
871
|
+
visitor(node);
|
|
872
|
+
} else {
|
|
873
|
+
if (trivialPlaneSets === newPlaneSetCount) {
|
|
874
|
+
fastTraverseLeaves(node);
|
|
875
|
+
} else {
|
|
876
|
+
traverseVolumeIntersections(node, newPlaneSet, newPlaneSetCount);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
} else {
|
|
881
|
+
if (isLeaf(node)) {
|
|
882
|
+
visitor(node);
|
|
883
|
+
} else {
|
|
884
|
+
return true;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
801
890
|
|
|
802
|
-
|
|
803
|
-
//shift remaining node up so it will be considered later
|
|
804
|
-
nodes[i >> 1] = nodes[i];
|
|
891
|
+
node.traversePreOrderUsingStack(internalVisitor);
|
|
805
892
|
}
|
|
806
893
|
|
|
807
|
-
|
|
808
|
-
|
|
894
|
+
function startTraversal(node) {
|
|
895
|
+
let planeSets = [];
|
|
896
|
+
for (let i = 0; i < numFrustums; i++) {
|
|
897
|
+
const frustum = frustums[i];
|
|
898
|
+
const planes = frustum.planes;
|
|
899
|
+
planeSets.push(planes);
|
|
900
|
+
}
|
|
901
|
+
traverseVolumeIntersections(node, planeSets, numFrustums);
|
|
902
|
+
}
|
|
809
903
|
|
|
810
|
-
|
|
811
|
-
for (i = 0; i < remaining_nodes; i++) {
|
|
812
|
-
n = nodes[i];
|
|
813
|
-
this.insertNode(n);
|
|
904
|
+
startTraversal(this);
|
|
814
905
|
}
|
|
815
|
-
};
|
|
816
|
-
|
|
817
906
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
907
|
+
/**
|
|
908
|
+
*
|
|
909
|
+
* @param {number} startX
|
|
910
|
+
* @param {number} startY
|
|
911
|
+
* @param {number} startZ
|
|
912
|
+
* @param {number} directionX
|
|
913
|
+
* @param {number} directionY
|
|
914
|
+
* @param {number} directionZ
|
|
915
|
+
* @param {function(node:LeafNode)} visitor
|
|
916
|
+
* @param thisArg
|
|
917
|
+
* @deprecated use {@link RaycastBVHVisitor} instead
|
|
918
|
+
*/
|
|
919
|
+
traverseRayLeafIntersections(startX, startY, startZ, directionX, directionY, directionZ, visitor, thisArg) {
|
|
920
|
+
this.traversePreOrderUsingStack(function (node) {
|
|
921
|
+
let b = node.intersectRay(startX, startY, startZ, directionX, directionY, directionZ);
|
|
824
922
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
* @param {number} y0
|
|
830
|
-
* @param {number} z0
|
|
831
|
-
* @param {number} x1
|
|
832
|
-
* @param {number} y1
|
|
833
|
-
* @param {number} z1
|
|
834
|
-
* @returns {number} number of objects added to the result
|
|
835
|
-
*/
|
|
836
|
-
BinaryNode.prototype.requestLeafIntersectionsAABB3 = function (result, x0, y0, z0, x1, y1, z1) {
|
|
837
|
-
const startOffset = stackPointer;
|
|
923
|
+
if (!b) {
|
|
924
|
+
//no intersection, terminate this branch
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
838
927
|
|
|
839
|
-
|
|
928
|
+
if (isLeaf(node)) {
|
|
929
|
+
//leaf node, supply to visitor
|
|
930
|
+
visitor.call(thisArg, node);
|
|
931
|
+
return false;
|
|
932
|
+
} else {
|
|
933
|
+
//intermediate node, continue traversal
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
}
|
|
840
938
|
|
|
841
|
-
|
|
939
|
+
/**
|
|
940
|
+
* Fraction of balanced nodes in the tree (those that have both children)
|
|
941
|
+
* @returns {number}
|
|
942
|
+
*/
|
|
943
|
+
computeBalanceFactor() {
|
|
842
944
|
|
|
843
|
-
|
|
844
|
-
stackPointer--;
|
|
845
|
-
const node = stack[stackPointer];
|
|
945
|
+
const bvhVisitor = new BVHVisitor();
|
|
846
946
|
|
|
847
|
-
if (!aabb3_intersect_aabb3(
|
|
848
|
-
x0, y0, z0, x1, y1, z1,
|
|
849
|
-
node.x0, node.y0, node.z0, node.x1, node.y1, node.z1
|
|
850
|
-
)) {
|
|
851
|
-
//no overlap
|
|
852
|
-
continue;
|
|
853
|
-
}
|
|
854
947
|
|
|
855
|
-
|
|
856
|
-
result[count++] = node;
|
|
948
|
+
const depths = [];
|
|
857
949
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const left = node.left;
|
|
861
|
-
if (left !== null) {
|
|
862
|
-
stack[stackPointer++] = left;
|
|
863
|
-
}
|
|
950
|
+
bvhVisitor.visitLeaf = (leaf) => {
|
|
951
|
+
const depth = leaf.computeDepth();
|
|
864
952
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
stack[stackPointer++] = right;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
953
|
+
depths.push(depth);
|
|
954
|
+
};
|
|
871
955
|
|
|
872
|
-
|
|
873
|
-
};
|
|
956
|
+
traverseBinaryNodeUsingVisitor(this, bvhVisitor)
|
|
874
957
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
let b = node.intersectSegment(startX, startY, startZ, endX, endY, endZ);
|
|
878
|
-
if (!b) {
|
|
879
|
-
return false;
|
|
880
|
-
}
|
|
881
|
-
if (node instanceof LeafNode) {
|
|
882
|
-
visitor(node);
|
|
883
|
-
return false;
|
|
958
|
+
if (depths.length === 0) {
|
|
959
|
+
return 1;
|
|
884
960
|
} else {
|
|
885
|
-
return
|
|
961
|
+
return computeSampleStandardDeviation(depths);
|
|
886
962
|
}
|
|
887
|
-
});
|
|
888
|
-
};
|
|
889
963
|
|
|
890
|
-
|
|
891
|
-
* @private
|
|
892
|
-
* Used in intersection traversal methods. Should be invoked via Function.apply and Function.call only and not be called directly
|
|
893
|
-
* @param {LeafNode} node
|
|
894
|
-
*/
|
|
895
|
-
function fastVisitLeafNodeIntersection(node) {
|
|
896
|
-
this(node, true);
|
|
897
|
-
}
|
|
964
|
+
}
|
|
898
965
|
|
|
899
|
-
/**
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
966
|
+
/**
|
|
967
|
+
* @template T
|
|
968
|
+
* @param {number} x0
|
|
969
|
+
* @param {number} y0
|
|
970
|
+
* @param {number} z0
|
|
971
|
+
* @param {number} x1
|
|
972
|
+
* @param {number} y1
|
|
973
|
+
* @param {number} z1
|
|
974
|
+
* @param {T} value
|
|
975
|
+
* @returns {LeafNode}
|
|
976
|
+
*/
|
|
977
|
+
insert(x0, y0, z0, x1, y1, z1, value) {
|
|
978
|
+
const leaf = new LeafNode(value, x0, y0, z0, x1, y1, z1);
|
|
979
|
+
this.insertNode(leaf);
|
|
980
|
+
return leaf;
|
|
981
|
+
}
|
|
908
982
|
|
|
909
983
|
/**
|
|
910
984
|
*
|
|
911
|
-
* @param {
|
|
912
|
-
* @returns {boolean}
|
|
985
|
+
* @param {Node|BinaryNode|LeafNode} child
|
|
913
986
|
*/
|
|
914
|
-
|
|
915
|
-
let b = false;
|
|
916
|
-
let i = 0;
|
|
917
|
-
|
|
918
|
-
for (; i < numFrustums; i++) {
|
|
919
|
-
|
|
920
|
-
const degree = node.intersectFrustumDegree(frustums[i]);
|
|
921
|
-
|
|
922
|
-
if (degree === 2) {
|
|
923
|
-
//completely inside frustum
|
|
987
|
+
insertNode(child) {
|
|
924
988
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
}
|
|
989
|
+
assert.notNull(child, "child");
|
|
990
|
+
assert.defined(child, "child");
|
|
991
|
+
assert.isInstanceOf(child, Node, 'child', "Node");
|
|
992
|
+
assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
|
|
930
993
|
|
|
931
|
-
|
|
932
|
-
return false;
|
|
994
|
+
const sibling = this.__insertion_findSiblingFor(child);
|
|
933
995
|
|
|
934
|
-
|
|
996
|
+
const sibling_parent = sibling.parentNode;
|
|
997
|
+
if (sibling_parent === null) {
|
|
998
|
+
// root node
|
|
935
999
|
|
|
936
|
-
|
|
937
|
-
|
|
1000
|
+
if (sibling.left === null) {
|
|
1001
|
+
sibling.setChildLeft(child);
|
|
1002
|
+
} else if (sibling.right === null) {
|
|
1003
|
+
sibling.setChildRight(child);
|
|
1004
|
+
} else {
|
|
1005
|
+
const parent = new BinaryNode();
|
|
938
1006
|
|
|
1007
|
+
parent.setChildren(sibling.right, child);
|
|
1008
|
+
sibling.right = parent;
|
|
1009
|
+
parent.parentNode = sibling;
|
|
939
1010
|
}
|
|
940
|
-
}
|
|
941
1011
|
|
|
942
|
-
|
|
943
|
-
return false;
|
|
944
|
-
}
|
|
1012
|
+
} else {
|
|
945
1013
|
|
|
946
|
-
|
|
947
|
-
visitor(node, false);
|
|
948
|
-
}
|
|
1014
|
+
// non-root
|
|
949
1015
|
|
|
950
|
-
|
|
951
|
-
}
|
|
1016
|
+
if (sibling_parent.left === sibling && sibling_parent.right === null) {
|
|
952
1017
|
|
|
953
|
-
|
|
954
|
-
|
|
1018
|
+
// right node is free, take it
|
|
1019
|
+
sibling_parent.setChildRight(child);
|
|
955
1020
|
|
|
956
|
-
|
|
957
|
-
* As recursion goes on, we remove frustums and planes from checks reducing total number of checks necessary
|
|
958
|
-
* @param {Array.<THREE.Frustum>} frustums
|
|
959
|
-
* @param {function} visitor
|
|
960
|
-
* @returns {number}
|
|
961
|
-
*/
|
|
962
|
-
BinaryNode.prototype.threeTraverseFrustumsIntersections2 = function (frustums, visitor) {
|
|
963
|
-
const numFrustums = frustums.length;
|
|
1021
|
+
} else if (sibling_parent.right === sibling && sibling_parent.left === null) {
|
|
964
1022
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
visitor(node);
|
|
968
|
-
return false;
|
|
969
|
-
} else {
|
|
970
|
-
return true;
|
|
971
|
-
}
|
|
972
|
-
}
|
|
1023
|
+
// left node is free, take it
|
|
1024
|
+
sibling_parent.setChildLeft(child);
|
|
973
1025
|
|
|
974
|
-
|
|
975
|
-
if (node instanceof LeafNode) {
|
|
976
|
-
visitor(node);
|
|
977
|
-
} else {
|
|
978
|
-
node.traversePreOrderUsingStack(visitLeaves);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
1026
|
+
} else {
|
|
981
1027
|
|
|
1028
|
+
transplantNewCommonParent(sibling, child)
|
|
982
1029
|
|
|
983
|
-
|
|
984
|
-
return mask & (1 << index) !== 0;
|
|
985
|
-
}
|
|
1030
|
+
}
|
|
986
1031
|
|
|
987
|
-
|
|
988
|
-
return mask & ~(1 << index);
|
|
989
|
-
}
|
|
1032
|
+
sibling_parent.bubbleRefit();
|
|
990
1033
|
|
|
991
|
-
function removeElementFromClone(original, clone, mask, originalIndex) {
|
|
992
|
-
if (clone === original) {
|
|
993
|
-
//clone planes
|
|
994
|
-
clone = clone.slice();
|
|
995
1034
|
}
|
|
996
|
-
//remove the plane from new set
|
|
997
|
-
clone.splice(originalIndex, 1);
|
|
998
|
-
return clone;
|
|
999
1035
|
}
|
|
1000
1036
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const numPlanes = planes.length;
|
|
1008
|
-
|
|
1009
|
-
//check plane set
|
|
1010
|
-
for (let i = numPlanes - 1; i >= 0; i--) {
|
|
1011
|
-
const plane = planes[i];
|
|
1012
|
-
const planeSide = node.computePlaneSide(plane);
|
|
1013
|
-
if (planeSide < 0) {
|
|
1014
|
-
//completely inside
|
|
1015
|
-
newPlanes = removeElementFromClone(planes, newPlanes, i);
|
|
1016
|
-
} else if (planeSide > 0) {
|
|
1017
|
-
//on the wrong side of the plane
|
|
1018
|
-
//drop plane set
|
|
1019
|
-
newPlaneSets = removeElementFromClone(planeSets, newPlaneSets, iPS);
|
|
1020
|
-
|
|
1021
|
-
break;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1037
|
+
/**
|
|
1038
|
+
* @param {boolean} deep
|
|
1039
|
+
* @returns {BinaryNode}
|
|
1040
|
+
*/
|
|
1041
|
+
clone(deep = false) {
|
|
1042
|
+
const clone = new BinaryNode();
|
|
1024
1043
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
}
|
|
1032
|
-
newPlaneSets[iPS] = newPlanes;
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1044
|
+
clone.x0 = this.x0;
|
|
1045
|
+
clone.y0 = this.y0;
|
|
1046
|
+
clone.z0 = this.z0;
|
|
1047
|
+
clone.x1 = this.x1;
|
|
1048
|
+
clone.y1 = this.y1;
|
|
1049
|
+
clone.z1 = this.z1;
|
|
1035
1050
|
|
|
1036
|
-
|
|
1037
|
-
}
|
|
1051
|
+
clone.leafNodeCount = this.leafNodeCount;
|
|
1038
1052
|
|
|
1039
|
-
|
|
1040
|
-
function internalVisitor(node) {
|
|
1041
|
-
let newPlaneSet = processVolumesIntersections(node, planeSets, numPlaneSets);
|
|
1042
|
-
if (newPlaneSet !== planeSets) {
|
|
1043
|
-
let newPlaneSetCount = newPlaneSet.length;
|
|
1044
|
-
if (newPlaneSetCount !== 0) {
|
|
1045
|
-
//some intersections
|
|
1046
|
-
|
|
1047
|
-
//check if all plane sets are trivial (contain no test planes)
|
|
1048
|
-
let trivialPlaneSets = 0;
|
|
1049
|
-
for (let i = 0; i < newPlaneSetCount; i++) {
|
|
1050
|
-
if (newPlaneSet[i].length === 0) {
|
|
1051
|
-
trivialPlaneSets++;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1053
|
+
clone.parentNode = this.parentNode;
|
|
1054
1054
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
fastTraverseLeaves(node);
|
|
1060
|
-
} else {
|
|
1061
|
-
traverseVolumeIntersections(node, newPlaneSet, newPlaneSetCount);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
} else {
|
|
1066
|
-
if (isLeaf(node)) {
|
|
1067
|
-
visitor(node);
|
|
1068
|
-
} else {
|
|
1069
|
-
return true;
|
|
1070
|
-
}
|
|
1055
|
+
if (deep === true) {
|
|
1056
|
+
if (this.left !== null) {
|
|
1057
|
+
clone.left = this.left.clone(true);
|
|
1058
|
+
clone.left.parentNode = clone;
|
|
1071
1059
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1060
|
+
if (this.right !== null) {
|
|
1061
|
+
clone.right = this.right.clone(true);
|
|
1062
|
+
clone.right.parentNode = clone;
|
|
1063
|
+
}
|
|
1064
|
+
} else {
|
|
1065
|
+
clone.left = this.left;
|
|
1066
|
+
clone.right = this.right;
|
|
1074
1067
|
}
|
|
1075
1068
|
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1069
|
+
clone.modifiers = this.modifiers;
|
|
1078
1070
|
|
|
1079
|
-
|
|
1080
|
-
let planeSets = [];
|
|
1081
|
-
for (let i = 0; i < numFrustums; i++) {
|
|
1082
|
-
const frustum = frustums[i];
|
|
1083
|
-
const planes = frustum.planes;
|
|
1084
|
-
planeSets.push(planes);
|
|
1085
|
-
}
|
|
1086
|
-
traverseVolumeIntersections(node, planeSets, numFrustums);
|
|
1071
|
+
return clone;
|
|
1087
1072
|
}
|
|
1073
|
+
}
|
|
1088
1074
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1075
|
+
/**
|
|
1076
|
+
* @readonly
|
|
1077
|
+
* @type {boolean}
|
|
1078
|
+
*/
|
|
1079
|
+
BinaryNode.prototype.isBinaryNode = true;
|
|
1091
1080
|
|
|
1092
1081
|
/**
|
|
1093
|
-
*
|
|
1094
|
-
* @param {
|
|
1095
|
-
* @param {
|
|
1096
|
-
* @
|
|
1097
|
-
* @param {number} directionX
|
|
1098
|
-
* @param {number} directionY
|
|
1099
|
-
* @param {number} directionZ
|
|
1100
|
-
* @param {function(node:LeafNode)} visitor
|
|
1101
|
-
* @param thisArg
|
|
1102
|
-
* @deprecated use {@link RaycastBVHVisitor} instead
|
|
1082
|
+
* @see "Dynamic BVH" by Erin Catto / Blizzard
|
|
1083
|
+
* @param {AABB3} node
|
|
1084
|
+
* @param {AABB3} addition
|
|
1085
|
+
* @returns {number}
|
|
1103
1086
|
*/
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1087
|
+
function inclusion_surface_area_heuristic(node, addition) {
|
|
1088
|
+
const x0 = min2(node.x0, addition.x0);
|
|
1089
|
+
const y0 = min2(node.y0, addition.y0);
|
|
1090
|
+
const z0 = min2(node.z0, addition.z0);
|
|
1107
1091
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
}
|
|
1092
|
+
const x1 = max2(node.x1, addition.x1);
|
|
1093
|
+
const y1 = max2(node.y1, addition.y1);
|
|
1094
|
+
const z1 = max2(node.z1, addition.z1);
|
|
1112
1095
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
visitor.call(thisArg, node);
|
|
1116
|
-
return false;
|
|
1117
|
-
} else {
|
|
1118
|
-
//intermediate node, continue traversal
|
|
1119
|
-
return true;
|
|
1120
|
-
}
|
|
1121
|
-
});
|
|
1122
|
-
};
|
|
1096
|
+
return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
|
|
1097
|
+
}
|
|
1123
1098
|
|
|
1124
1099
|
/**
|
|
1125
|
-
*
|
|
1126
|
-
* @
|
|
1100
|
+
*
|
|
1101
|
+
* @param {BinaryNode|LeafNode} node
|
|
1102
|
+
* @param {AABB3} addition
|
|
1127
1103
|
*/
|
|
1128
|
-
|
|
1104
|
+
function inclusion_cost_lower_bound(node, addition) {
|
|
1105
|
+
let n = node;
|
|
1106
|
+
let r = boxSurfaceArea2(addition);
|
|
1129
1107
|
|
|
1130
|
-
|
|
1108
|
+
while (n !== null) {
|
|
1131
1109
|
|
|
1110
|
+
r += n.costForInclusion(addition);
|
|
1132
1111
|
|
|
1133
|
-
|
|
1112
|
+
n = n.parentNode;
|
|
1113
|
+
}
|
|
1134
1114
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1115
|
+
return r;
|
|
1116
|
+
}
|
|
1137
1117
|
|
|
1138
|
-
depths.push(depth);
|
|
1139
|
-
};
|
|
1140
1118
|
|
|
1141
|
-
|
|
1119
|
+
/**
|
|
1120
|
+
*
|
|
1121
|
+
* @param {Node} n0
|
|
1122
|
+
* @param {Node} n1
|
|
1123
|
+
* @return {number}
|
|
1124
|
+
*/
|
|
1125
|
+
function sortMortonCodes(n0, n1) {
|
|
1126
|
+
return n0._mortonCode - n1._mortonCode;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1142
1129
|
|
|
1143
|
-
|
|
1130
|
+
/**
|
|
1131
|
+
*
|
|
1132
|
+
* @param node
|
|
1133
|
+
*/
|
|
1134
|
+
function countLeaves(node) {
|
|
1135
|
+
if (isLeaf(node)) {
|
|
1144
1136
|
return 1;
|
|
1145
|
-
} else {
|
|
1146
|
-
return computeSampleStandardDeviation(depths);
|
|
1147
1137
|
}
|
|
1148
1138
|
|
|
1149
|
-
|
|
1139
|
+
let result = 0;
|
|
1140
|
+
|
|
1141
|
+
node.traversePreOrderUsingStack(function (node) {
|
|
1142
|
+
if (isLeaf(node)) {
|
|
1143
|
+
result++;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
return result;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
1150
|
|
|
1151
1151
|
/**
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
* @param {
|
|
1155
|
-
* @param {number} z0
|
|
1156
|
-
* @param {number} x1
|
|
1157
|
-
* @param {number} y1
|
|
1158
|
-
* @param {number} z1
|
|
1159
|
-
* @param {*} value
|
|
1160
|
-
* @returns {LeafNode}
|
|
1152
|
+
* @private
|
|
1153
|
+
* Used in intersection traversal methods. Should be invoked via Function.apply and Function.call only and not be called directly
|
|
1154
|
+
* @param {LeafNode} node
|
|
1161
1155
|
*/
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
this.insertNode(leaf);
|
|
1166
|
-
return leaf;
|
|
1167
|
-
};
|
|
1156
|
+
function fastVisitLeafNodeIntersection(node) {
|
|
1157
|
+
this(node, true);
|
|
1158
|
+
}
|
|
1168
1159
|
|
|
1169
1160
|
/**
|
|
1170
1161
|
*
|
|
@@ -1195,148 +1186,3 @@ function transplantNewCommonParent(node, child) {
|
|
|
1195
1186
|
return bNode;
|
|
1196
1187
|
}
|
|
1197
1188
|
|
|
1198
|
-
|
|
1199
|
-
/**
|
|
1200
|
-
*
|
|
1201
|
-
* @param {Node|BinaryNode|LeafNode} child
|
|
1202
|
-
*/
|
|
1203
|
-
BinaryNode.prototype.insertNode = function (child) {
|
|
1204
|
-
|
|
1205
|
-
assert.notNull(child, "child");
|
|
1206
|
-
assert.defined(child, "child");
|
|
1207
|
-
assert.isInstanceOf(child, Node, 'child', "Node");
|
|
1208
|
-
assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
|
|
1209
|
-
|
|
1210
|
-
const sibling = this.__insertion_findSiblingFor(child);
|
|
1211
|
-
|
|
1212
|
-
const sibling_parent = sibling.parentNode;
|
|
1213
|
-
if (sibling_parent === null) {
|
|
1214
|
-
// root node
|
|
1215
|
-
|
|
1216
|
-
if (sibling.left === null) {
|
|
1217
|
-
sibling.setChildLeft(child);
|
|
1218
|
-
} else if (sibling.right === null) {
|
|
1219
|
-
sibling.setChildRight(child);
|
|
1220
|
-
} else {
|
|
1221
|
-
const parent = new BinaryNode();
|
|
1222
|
-
|
|
1223
|
-
parent.setChildren(sibling.right, child);
|
|
1224
|
-
sibling.right = parent;
|
|
1225
|
-
parent.parentNode = sibling;
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
} else {
|
|
1229
|
-
|
|
1230
|
-
// non-root
|
|
1231
|
-
|
|
1232
|
-
if (sibling_parent.left === sibling && sibling_parent.right === null) {
|
|
1233
|
-
|
|
1234
|
-
// right node is free, take it
|
|
1235
|
-
sibling_parent.setChildRight(child);
|
|
1236
|
-
|
|
1237
|
-
} else if (sibling_parent.right === sibling && sibling_parent.left === null) {
|
|
1238
|
-
|
|
1239
|
-
// left node is free, take it
|
|
1240
|
-
sibling_parent.setChildLeft(child);
|
|
1241
|
-
|
|
1242
|
-
} else {
|
|
1243
|
-
|
|
1244
|
-
transplantNewCommonParent(sibling, child)
|
|
1245
|
-
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
sibling_parent.bubbleRefit();
|
|
1249
|
-
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
/**
|
|
1254
|
-
* @deprecated
|
|
1255
|
-
* @param {Node|BinaryNode|LeafNode} child
|
|
1256
|
-
*/
|
|
1257
|
-
BinaryNode.prototype.insertNode_old = function (child) {
|
|
1258
|
-
assert.notNull(child, "child");
|
|
1259
|
-
assert.defined(child, "child");
|
|
1260
|
-
assert.isInstanceOf(child, Node, 'child', "Node");
|
|
1261
|
-
assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
|
|
1262
|
-
|
|
1263
|
-
const node = this.findParentFor(child);
|
|
1264
|
-
|
|
1265
|
-
let bNode;
|
|
1266
|
-
|
|
1267
|
-
if (
|
|
1268
|
-
node.isBinaryNode &&
|
|
1269
|
-
!(node.isProtected() && node !== this) // protected node, can't insert into this subtree
|
|
1270
|
-
) {
|
|
1271
|
-
|
|
1272
|
-
if (node.left === null) {
|
|
1273
|
-
node.left = child;
|
|
1274
|
-
child.parentNode = node;
|
|
1275
|
-
node.bubbleExpandToFit(child);
|
|
1276
|
-
|
|
1277
|
-
} else if (node.right === null) {
|
|
1278
|
-
|
|
1279
|
-
node.right = child;
|
|
1280
|
-
child.parentNode = node;
|
|
1281
|
-
node.bubbleExpandToFit(child);
|
|
1282
|
-
|
|
1283
|
-
} else {
|
|
1284
|
-
//take right child and insert another binary node there
|
|
1285
|
-
bNode = new BinaryNode();
|
|
1286
|
-
bNode.setChildren(node.right, child);
|
|
1287
|
-
//
|
|
1288
|
-
node.right = bNode;
|
|
1289
|
-
bNode.parentNode = node;
|
|
1290
|
-
node.bubbleExpandToFit(bNode);
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
node.updateLeafNodeCount();
|
|
1294
|
-
} else {
|
|
1295
|
-
//need to do transplanting and introduce a new common parent
|
|
1296
|
-
transplantNewCommonParent(node, child);
|
|
1297
|
-
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
};
|
|
1301
|
-
|
|
1302
|
-
/**
|
|
1303
|
-
* @param {boolean} deep
|
|
1304
|
-
* @returns {BinaryNode}
|
|
1305
|
-
*/
|
|
1306
|
-
BinaryNode.prototype.clone = function (deep = false) {
|
|
1307
|
-
const clone = new BinaryNode();
|
|
1308
|
-
|
|
1309
|
-
clone.x0 = this.x0;
|
|
1310
|
-
clone.y0 = this.y0;
|
|
1311
|
-
clone.z0 = this.z0;
|
|
1312
|
-
clone.x1 = this.x1;
|
|
1313
|
-
clone.y1 = this.y1;
|
|
1314
|
-
clone.z1 = this.z1;
|
|
1315
|
-
|
|
1316
|
-
clone.leafNodeCount = this.leafNodeCount;
|
|
1317
|
-
|
|
1318
|
-
clone.parentNode = this.parentNode;
|
|
1319
|
-
|
|
1320
|
-
if (deep === true) {
|
|
1321
|
-
if (this.left !== null) {
|
|
1322
|
-
clone.left = this.left.clone(true);
|
|
1323
|
-
clone.left.parentNode = clone;
|
|
1324
|
-
}
|
|
1325
|
-
if (this.right !== null) {
|
|
1326
|
-
clone.right = this.right.clone(true);
|
|
1327
|
-
clone.right.parentNode = clone;
|
|
1328
|
-
}
|
|
1329
|
-
} else {
|
|
1330
|
-
clone.left = this.left;
|
|
1331
|
-
clone.right = this.right;
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
clone.modifiers = this.modifiers;
|
|
1335
|
-
|
|
1336
|
-
return clone;
|
|
1337
|
-
};
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
export {
|
|
1341
|
-
BinaryNode
|
|
1342
|
-
};
|