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