@woosh/meep-engine 2.34.36 → 2.34.37
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 +852 -956
- package/core/bvh2/LeafNode.js +80 -82
- package/core/bvh2/Node.js +111 -114
- package/core/bvh2/aabb3/AABB3.spec.js +27 -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 +6 -2
- 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/water2/shader/AlexWaterShader.js +7 -1
- package/engine/graphics/particles/particular/engine/renderers/billboard/ParticleBillboardMaterial.js +5 -1
- package/package.json +1 -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
|
|
481
|
-
|
|
482
|
-
stack[stackPointer++] = this;
|
|
483
|
-
|
|
484
|
-
let n;
|
|
485
|
-
|
|
486
|
-
while (stackPointer-- > stackOffset) {
|
|
487
|
-
|
|
488
|
-
visitCount++;
|
|
482
|
+
const left = this.left;
|
|
489
483
|
|
|
490
|
-
|
|
484
|
+
if (left === null) {
|
|
485
|
+
leftArea = 0;
|
|
486
|
+
leftLeaves = 0;
|
|
487
|
+
} else {
|
|
488
|
+
leftArea = boxSurfaceArea2(left);
|
|
489
|
+
leftLeaves = left.leafNodeCount;
|
|
490
|
+
}
|
|
491
491
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (n.left !== null) {
|
|
500
|
-
stack[stackPointer++] = n.left;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
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;
|
|
503
499
|
}
|
|
504
500
|
|
|
505
|
-
|
|
501
|
+
const thisArea = boxSurfaceArea2(this);
|
|
506
502
|
|
|
507
|
-
return
|
|
503
|
+
return surfaceAreaHeuristic(thisArea, leftArea, rightArea, leftLeaves, rightLeaves);
|
|
508
504
|
}
|
|
509
505
|
|
|
510
|
-
|
|
511
|
-
|
|
506
|
+
updateLeafNodeCount() {
|
|
507
|
+
let node = this;
|
|
512
508
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const b = this.right;
|
|
509
|
+
while (node !== null) {
|
|
510
|
+
let n = 0;
|
|
516
511
|
|
|
517
|
-
|
|
518
|
-
this.y0 = min2(a.y0, b.y0);
|
|
519
|
-
this.z0 = min2(a.z0, b.z0);
|
|
512
|
+
const right = node.right;
|
|
520
513
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
514
|
+
if (right !== null) {
|
|
515
|
+
if (right.isLeafNode) {
|
|
516
|
+
n++;
|
|
517
|
+
} else {
|
|
518
|
+
n += right.leafNodeCount;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
525
521
|
|
|
526
|
-
|
|
527
|
-
let n = this;
|
|
522
|
+
const left = this.left;
|
|
528
523
|
|
|
529
|
-
|
|
524
|
+
if (left !== null) {
|
|
525
|
+
if (left.isLeafNode) {
|
|
526
|
+
n++;
|
|
527
|
+
} else {
|
|
528
|
+
n += left.leafNodeCount;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
530
531
|
|
|
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;
|
|
532
|
+
node.leafNodeCount = n;
|
|
540
533
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (n === null) {
|
|
544
|
-
// hit the root
|
|
545
|
-
return;
|
|
534
|
+
// propagate update up the tree
|
|
535
|
+
node = node.parentNode;
|
|
546
536
|
}
|
|
547
537
|
|
|
538
|
+
}
|
|
548
539
|
|
|
549
|
-
|
|
550
|
-
|
|
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() {
|
|
551
544
|
|
|
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
|
-
}
|
|
545
|
+
/**
|
|
546
|
+
*
|
|
547
|
+
* @type {BinaryNode}
|
|
548
|
+
*/
|
|
549
|
+
const r = this.right;
|
|
564
550
|
|
|
565
|
-
|
|
566
|
-
n.x1 = _x1;
|
|
567
|
-
flag = true;
|
|
568
|
-
}
|
|
569
|
-
if (_y1 > n.y1) {
|
|
570
|
-
n.y1 = _y1;
|
|
571
|
-
flag = true;
|
|
572
|
-
}
|
|
573
|
-
if (_z1 > n.z1) {
|
|
574
|
-
n.z1 = _z1;
|
|
575
|
-
flag = true;
|
|
576
|
-
}
|
|
551
|
+
const l = this.left;
|
|
577
552
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
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
|
-
};
|
|
553
|
+
const rL = r.left;
|
|
554
|
+
const rR = r.right;
|
|
593
555
|
|
|
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
|
-
}
|
|
556
|
+
this.left = r;
|
|
557
|
+
this.right = rR;
|
|
603
558
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const nodeArea = node.computeSurfaceArea();
|
|
608
|
-
result += nodeArea;
|
|
609
|
-
});
|
|
559
|
+
if (rR !== null) {
|
|
560
|
+
rR.parentNode = this;
|
|
561
|
+
}
|
|
610
562
|
|
|
611
|
-
|
|
612
|
-
|
|
563
|
+
r.left = l;
|
|
564
|
+
if (l !== null) {
|
|
565
|
+
l.parentNode = r;
|
|
566
|
+
}
|
|
613
567
|
|
|
614
|
-
|
|
615
|
-
let result = 0;
|
|
616
|
-
this.traversePreOrderUsingStack(function (node) {
|
|
617
|
-
result++;
|
|
618
|
-
});
|
|
619
|
-
return result;
|
|
620
|
-
};
|
|
568
|
+
r.right = rL;
|
|
621
569
|
|
|
570
|
+
r.updateLeafNodeCount();
|
|
622
571
|
|
|
623
|
-
|
|
624
|
-
*
|
|
625
|
-
* @param node
|
|
626
|
-
*/
|
|
627
|
-
function countLeaves(node) {
|
|
628
|
-
if (isLeaf(node)) {
|
|
629
|
-
return 1;
|
|
572
|
+
r.bubbleRefit();
|
|
630
573
|
}
|
|
631
574
|
|
|
632
|
-
|
|
575
|
+
rotateRight() {
|
|
576
|
+
const r = this.right;
|
|
633
577
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
578
|
+
const l = this.left;
|
|
579
|
+
|
|
580
|
+
const lL = l.left;
|
|
581
|
+
const lR = l.right;
|
|
582
|
+
|
|
583
|
+
if (lL !== null) {
|
|
584
|
+
lL.parentNode = this;
|
|
637
585
|
}
|
|
638
|
-
});
|
|
639
586
|
|
|
640
|
-
|
|
641
|
-
|
|
587
|
+
l.right = r;
|
|
588
|
+
if (r !== null) {
|
|
589
|
+
r.parentNode = l;
|
|
590
|
+
}
|
|
642
591
|
|
|
643
|
-
|
|
644
|
-
* @deprecated Incorrect usage of SAH
|
|
645
|
-
* @return {number}
|
|
646
|
-
*/
|
|
647
|
-
BinaryNode.prototype.computeSAH = function () {
|
|
648
|
-
let leftLeaves, rightLeaves, leftArea, rightArea;
|
|
592
|
+
l.left = lR;
|
|
649
593
|
|
|
650
|
-
|
|
594
|
+
l.updateLeafNodeCount();
|
|
651
595
|
|
|
652
|
-
|
|
653
|
-
leftArea = 0;
|
|
654
|
-
leftLeaves = 0;
|
|
655
|
-
} else {
|
|
656
|
-
leftArea = boxSurfaceArea2(left);
|
|
657
|
-
leftLeaves = left.leafNodeCount;
|
|
596
|
+
l.bubbleRefit();
|
|
658
597
|
}
|
|
659
598
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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);
|
|
668
608
|
|
|
669
|
-
|
|
609
|
+
for (i = 0; i < numNodes; i++) {
|
|
610
|
+
//leaf needs to be set up inside the callback
|
|
611
|
+
n = leafFactory(i);
|
|
670
612
|
|
|
671
|
-
|
|
672
|
-
}
|
|
613
|
+
nodes[i] = n;
|
|
614
|
+
}
|
|
673
615
|
|
|
674
|
-
|
|
675
|
-
|
|
616
|
+
//sort leaves
|
|
617
|
+
arrayQuickSort(nodes, n => n.computeMortonCode(), null, 0, max2(0, numNodes - 1));
|
|
676
618
|
|
|
677
|
-
|
|
678
|
-
let n = 0;
|
|
619
|
+
let remaining_nodes = numNodes;
|
|
679
620
|
|
|
680
|
-
|
|
621
|
+
while (remaining_nodes > 2) {
|
|
681
622
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
n
|
|
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;
|
|
687
630
|
}
|
|
688
|
-
}
|
|
689
631
|
|
|
690
|
-
|
|
632
|
+
const numNodesMod2 = remaining_nodes & 1; //faster version of %2
|
|
691
633
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
} else {
|
|
696
|
-
n += left.leafNodeCount;
|
|
634
|
+
if (numNodesMod2 !== 0) {
|
|
635
|
+
//shift remaining node up so it will be considered later
|
|
636
|
+
nodes[i >> 1] = nodes[i];
|
|
697
637
|
}
|
|
698
|
-
}
|
|
699
638
|
|
|
700
|
-
|
|
639
|
+
remaining_nodes = (remaining_nodes >> 1) + numNodesMod2;
|
|
640
|
+
}
|
|
701
641
|
|
|
702
|
-
//
|
|
703
|
-
|
|
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
|
+
}
|
|
704
647
|
}
|
|
705
648
|
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
/**
|
|
710
|
-
* As a result of rotation, this node 'becomes' the right node, and left node get's replaced by the right node with necessary adjustments
|
|
711
|
-
*/
|
|
712
|
-
BinaryNode.prototype.rotateLeft = function () {
|
|
713
|
-
|
|
714
649
|
/**
|
|
715
650
|
*
|
|
716
|
-
* @
|
|
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
|
|
717
659
|
*/
|
|
718
|
-
|
|
660
|
+
requestLeafIntersectionsAABB3(result, x0, y0, z0, x1, y1, z1) {
|
|
661
|
+
const startOffset = stackPointer;
|
|
662
|
+
|
|
663
|
+
stack[stackPointer++] = this;
|
|
664
|
+
|
|
665
|
+
let count = 0;
|
|
719
666
|
|
|
720
|
-
|
|
667
|
+
while (stackPointer > startOffset) {
|
|
668
|
+
stackPointer--;
|
|
669
|
+
const node = stack[stackPointer];
|
|
721
670
|
|
|
722
|
-
|
|
723
|
-
|
|
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
|
+
}
|
|
678
|
+
|
|
679
|
+
if (node.isLeafNode) {
|
|
680
|
+
result[count++] = node;
|
|
681
|
+
|
|
682
|
+
count++;
|
|
683
|
+
} else {
|
|
684
|
+
const left = node.left;
|
|
685
|
+
if (left !== null) {
|
|
686
|
+
stack[stackPointer++] = left;
|
|
687
|
+
}
|
|
724
688
|
|
|
725
|
-
|
|
726
|
-
|
|
689
|
+
const right = node.right;
|
|
690
|
+
if (right !== null) {
|
|
691
|
+
stack[stackPointer++] = right;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
727
695
|
|
|
728
|
-
|
|
729
|
-
rR.parentNode = this;
|
|
696
|
+
return count;
|
|
730
697
|
}
|
|
731
698
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
+
});
|
|
735
712
|
}
|
|
736
713
|
|
|
737
|
-
|
|
714
|
+
/**
|
|
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}
|
|
720
|
+
*/
|
|
721
|
+
threeTraverseFrustumsIntersections(frustums, visitor) {
|
|
722
|
+
const numFrustums = frustums.length;
|
|
738
723
|
|
|
739
|
-
|
|
724
|
+
/**
|
|
725
|
+
*
|
|
726
|
+
* @param {LeafNode|BinaryNode} node
|
|
727
|
+
* @returns {boolean}
|
|
728
|
+
*/
|
|
729
|
+
function visitNode(node) {
|
|
730
|
+
let b = false;
|
|
731
|
+
let i = 0;
|
|
740
732
|
|
|
741
|
-
|
|
742
|
-
};
|
|
733
|
+
for (; i < numFrustums; i++) {
|
|
743
734
|
|
|
744
|
-
|
|
745
|
-
const r = this.right;
|
|
735
|
+
const degree = node.intersectFrustumDegree(frustums[i]);
|
|
746
736
|
|
|
747
|
-
|
|
737
|
+
if (degree === 2) {
|
|
738
|
+
//completely inside frustum
|
|
748
739
|
|
|
749
|
-
|
|
750
|
-
|
|
740
|
+
if (node.isLeafNode) {
|
|
741
|
+
visitor(node, true);
|
|
742
|
+
} else {
|
|
743
|
+
node.traverseLeavesPreOrderUsingStack(fastVisitLeafNodeIntersection, visitor);
|
|
744
|
+
}
|
|
751
745
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
746
|
+
//prevent further traversal
|
|
747
|
+
return false;
|
|
755
748
|
|
|
756
|
-
|
|
757
|
-
if (r !== null) {
|
|
758
|
-
r.parentNode = l;
|
|
759
|
-
}
|
|
749
|
+
} else if (degree === 1) {
|
|
760
750
|
|
|
761
|
-
|
|
751
|
+
//partially inside frustum
|
|
752
|
+
b = true;
|
|
762
753
|
|
|
763
|
-
|
|
754
|
+
}
|
|
755
|
+
}
|
|
764
756
|
|
|
765
|
-
|
|
766
|
-
|
|
757
|
+
if (!b) {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
767
760
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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);
|
|
761
|
+
if (node.isLeafNode) {
|
|
762
|
+
visitor(node, false);
|
|
763
|
+
}
|
|
777
764
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
n = leafFactory(i);
|
|
765
|
+
return true;
|
|
766
|
+
}
|
|
781
767
|
|
|
782
|
-
|
|
768
|
+
return this.traversePreOrderUsingStack(visitNode);
|
|
783
769
|
}
|
|
784
770
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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;
|
|
789
779
|
|
|
790
|
-
|
|
780
|
+
function visitLeaves(node) {
|
|
781
|
+
if (node instanceof LeafNode) {
|
|
782
|
+
visitor(node);
|
|
783
|
+
return false;
|
|
784
|
+
} else {
|
|
785
|
+
return true;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
791
788
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
nodes[i >> 1] = n;
|
|
789
|
+
function fastTraverseLeaves(node) {
|
|
790
|
+
if (node instanceof LeafNode) {
|
|
791
|
+
visitor(node);
|
|
792
|
+
} else {
|
|
793
|
+
node.traversePreOrderUsingStack(visitLeaves);
|
|
794
|
+
}
|
|
799
795
|
}
|
|
800
796
|
|
|
801
|
-
const numNodesMod2 = remaining_nodes & 1; //faster version of %2
|
|
802
797
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
nodes[i >> 1] = nodes[i];
|
|
798
|
+
function isFlagSet(mask, index) {
|
|
799
|
+
return mask & (1 << index) !== 0;
|
|
806
800
|
}
|
|
807
801
|
|
|
808
|
-
|
|
809
|
-
|
|
802
|
+
function clearFlag(mask, index) {
|
|
803
|
+
return mask & ~(1 << index);
|
|
804
|
+
}
|
|
810
805
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
+
}
|
|
817
815
|
|
|
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
|
+
}
|
|
818
839
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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
|
+
}
|
|
825
850
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
* @param {LeafNode[]} result destination
|
|
829
|
-
* @param {number} x0
|
|
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;
|
|
851
|
+
return newPlaneSets;
|
|
852
|
+
}
|
|
839
853
|
|
|
840
|
-
|
|
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
|
+
}
|
|
841
869
|
|
|
842
|
-
|
|
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
|
+
}
|
|
843
887
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const node = stack[stackPointer];
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
847
890
|
|
|
848
|
-
|
|
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;
|
|
891
|
+
node.traversePreOrderUsingStack(internalVisitor);
|
|
854
892
|
}
|
|
855
893
|
|
|
856
|
-
|
|
857
|
-
|
|
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
|
+
}
|
|
858
903
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
904
|
+
startTraversal(this);
|
|
905
|
+
}
|
|
906
|
+
|
|
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);
|
|
922
|
+
|
|
923
|
+
if (!b) {
|
|
924
|
+
//no intersection, terminate this branch
|
|
925
|
+
return false;
|
|
864
926
|
}
|
|
865
927
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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;
|
|
869
935
|
}
|
|
870
|
-
}
|
|
936
|
+
});
|
|
871
937
|
}
|
|
872
938
|
|
|
873
|
-
|
|
874
|
-
|
|
939
|
+
/**
|
|
940
|
+
* Fraction of balanced nodes in the tree (those that have both children)
|
|
941
|
+
* @returns {number}
|
|
942
|
+
*/
|
|
943
|
+
computeBalanceFactor() {
|
|
875
944
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
945
|
+
const bvhVisitor = new BVHVisitor();
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
const depths = [];
|
|
949
|
+
|
|
950
|
+
bvhVisitor.visitLeaf = (leaf) => {
|
|
951
|
+
const depth = leaf.computeDepth();
|
|
952
|
+
|
|
953
|
+
depths.push(depth);
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
traverseBinaryNodeUsingVisitor(this, bvhVisitor)
|
|
957
|
+
|
|
958
|
+
if (depths.length === 0) {
|
|
959
|
+
return 1;
|
|
885
960
|
} else {
|
|
886
|
-
return
|
|
961
|
+
return computeSampleStandardDeviation(depths);
|
|
887
962
|
}
|
|
888
|
-
});
|
|
889
|
-
};
|
|
890
963
|
|
|
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
|
-
}
|
|
964
|
+
}
|
|
899
965
|
|
|
900
|
-
/**
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
+
}
|
|
909
982
|
|
|
910
983
|
/**
|
|
911
984
|
*
|
|
912
|
-
* @param {
|
|
913
|
-
* @returns {boolean}
|
|
985
|
+
* @param {Node|BinaryNode|LeafNode} child
|
|
914
986
|
*/
|
|
915
|
-
|
|
916
|
-
let b = false;
|
|
917
|
-
let i = 0;
|
|
918
|
-
|
|
919
|
-
for (; i < numFrustums; i++) {
|
|
920
|
-
|
|
921
|
-
const degree = node.intersectFrustumDegree(frustums[i]);
|
|
987
|
+
insertNode(child) {
|
|
922
988
|
|
|
923
|
-
|
|
924
|
-
|
|
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");
|
|
925
993
|
|
|
926
|
-
|
|
927
|
-
visitor(node, true);
|
|
928
|
-
} else {
|
|
929
|
-
node.traverseLeavesPreOrderUsingStack(fastVisitLeafNodeIntersection, visitor);
|
|
930
|
-
}
|
|
994
|
+
const sibling = this.__insertion_findSiblingFor(child);
|
|
931
995
|
|
|
932
|
-
|
|
933
|
-
|
|
996
|
+
const sibling_parent = sibling.parentNode;
|
|
997
|
+
if (sibling_parent === null) {
|
|
998
|
+
// root node
|
|
934
999
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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();
|
|
939
1006
|
|
|
1007
|
+
parent.setChildren(sibling.right, child);
|
|
1008
|
+
sibling.right = parent;
|
|
1009
|
+
parent.parentNode = sibling;
|
|
940
1010
|
}
|
|
941
|
-
}
|
|
942
1011
|
|
|
943
|
-
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
1012
|
+
} else {
|
|
946
1013
|
|
|
947
|
-
|
|
948
|
-
visitor(node, false);
|
|
949
|
-
}
|
|
1014
|
+
// non-root
|
|
950
1015
|
|
|
951
|
-
|
|
952
|
-
}
|
|
1016
|
+
if (sibling_parent.left === sibling && sibling_parent.right === null) {
|
|
953
1017
|
|
|
954
|
-
|
|
955
|
-
|
|
1018
|
+
// right node is free, take it
|
|
1019
|
+
sibling_parent.setChildRight(child);
|
|
956
1020
|
|
|
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;
|
|
1021
|
+
} else if (sibling_parent.right === sibling && sibling_parent.left === null) {
|
|
965
1022
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
visitor(node);
|
|
969
|
-
return false;
|
|
970
|
-
} else {
|
|
971
|
-
return true;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
1023
|
+
// left node is free, take it
|
|
1024
|
+
sibling_parent.setChildLeft(child);
|
|
974
1025
|
|
|
975
|
-
|
|
976
|
-
if (node instanceof LeafNode) {
|
|
977
|
-
visitor(node);
|
|
978
|
-
} else {
|
|
979
|
-
node.traversePreOrderUsingStack(visitLeaves);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
1026
|
+
} else {
|
|
982
1027
|
|
|
1028
|
+
transplantNewCommonParent(sibling, child)
|
|
983
1029
|
|
|
984
|
-
|
|
985
|
-
return mask & (1 << index) !== 0;
|
|
986
|
-
}
|
|
1030
|
+
}
|
|
987
1031
|
|
|
988
|
-
|
|
989
|
-
return mask & ~(1 << index);
|
|
990
|
-
}
|
|
1032
|
+
sibling_parent.bubbleRefit();
|
|
991
1033
|
|
|
992
|
-
function removeElementFromClone(original, clone, mask, originalIndex) {
|
|
993
|
-
if (clone === original) {
|
|
994
|
-
//clone planes
|
|
995
|
-
clone = clone.slice();
|
|
996
1034
|
}
|
|
997
|
-
//remove the plane from new set
|
|
998
|
-
clone.splice(originalIndex, 1);
|
|
999
|
-
return clone;
|
|
1000
1035
|
}
|
|
1001
1036
|
|
|
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
|
-
}
|
|
1037
|
+
/**
|
|
1038
|
+
* @param {boolean} deep
|
|
1039
|
+
* @returns {BinaryNode}
|
|
1040
|
+
*/
|
|
1041
|
+
clone(deep = false) {
|
|
1042
|
+
const clone = new BinaryNode();
|
|
1025
1043
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
newPlaneSets[iPS] = newPlanes;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
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;
|
|
1036
1050
|
|
|
1037
|
-
|
|
1038
|
-
}
|
|
1051
|
+
clone.leafNodeCount = this.leafNodeCount;
|
|
1039
1052
|
|
|
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
|
-
}
|
|
1053
|
+
clone.parentNode = this.parentNode;
|
|
1055
1054
|
|
|
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
|
-
}
|
|
1055
|
+
if (deep === true) {
|
|
1056
|
+
if (this.left !== null) {
|
|
1057
|
+
clone.left = this.left.clone(true);
|
|
1058
|
+
clone.left.parentNode = clone;
|
|
1072
1059
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
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;
|
|
1075
1067
|
}
|
|
1076
1068
|
|
|
1077
|
-
|
|
1078
|
-
}
|
|
1069
|
+
clone.modifiers = this.modifiers;
|
|
1079
1070
|
|
|
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);
|
|
1071
|
+
return clone;
|
|
1088
1072
|
}
|
|
1073
|
+
}
|
|
1089
1074
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1075
|
+
/**
|
|
1076
|
+
* @readonly
|
|
1077
|
+
* @type {boolean}
|
|
1078
|
+
*/
|
|
1079
|
+
BinaryNode.prototype.isBinaryNode = true;
|
|
1092
1080
|
|
|
1093
1081
|
/**
|
|
1094
|
-
*
|
|
1095
|
-
* @param {
|
|
1096
|
-
* @param {
|
|
1097
|
-
* @
|
|
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
|
|
1082
|
+
* @see "Dynamic BVH" by Erin Catto / Blizzard
|
|
1083
|
+
* @param {AABB3} node
|
|
1084
|
+
* @param {AABB3} addition
|
|
1085
|
+
* @returns {number}
|
|
1104
1086
|
*/
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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);
|
|
1108
1091
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
}
|
|
1092
|
+
const x1 = max2(node.x1, addition.x1);
|
|
1093
|
+
const y1 = max2(node.y1, addition.y1);
|
|
1094
|
+
const z1 = max2(node.z1, addition.z1);
|
|
1113
1095
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
visitor.call(thisArg, node);
|
|
1117
|
-
return false;
|
|
1118
|
-
} else {
|
|
1119
|
-
//intermediate node, continue traversal
|
|
1120
|
-
return true;
|
|
1121
|
-
}
|
|
1122
|
-
});
|
|
1123
|
-
};
|
|
1096
|
+
return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
|
|
1097
|
+
}
|
|
1124
1098
|
|
|
1125
1099
|
/**
|
|
1126
|
-
*
|
|
1127
|
-
* @
|
|
1100
|
+
*
|
|
1101
|
+
* @param {BinaryNode|LeafNode} node
|
|
1102
|
+
* @param {AABB3} addition
|
|
1128
1103
|
*/
|
|
1129
|
-
|
|
1104
|
+
function inclusion_cost_lower_bound(node, addition) {
|
|
1105
|
+
let n = node;
|
|
1106
|
+
let r = boxSurfaceArea2(addition);
|
|
1130
1107
|
|
|
1131
|
-
|
|
1108
|
+
while (n !== null) {
|
|
1132
1109
|
|
|
1110
|
+
r += n.costForInclusion(addition);
|
|
1133
1111
|
|
|
1134
|
-
|
|
1112
|
+
n = n.parentNode;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
return r;
|
|
1116
|
+
}
|
|
1135
1117
|
|
|
1136
|
-
bvhVisitor.visitLeaf = (leaf) => {
|
|
1137
|
-
const depth = leaf.computeDepth();
|
|
1138
1118
|
|
|
1139
|
-
|
|
1140
|
-
|
|
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
|
+
}
|
|
1141
1128
|
|
|
1142
|
-
traverseBinaryNodeUsingVisitor(this, bvhVisitor)
|
|
1143
1129
|
|
|
1144
|
-
|
|
1130
|
+
/**
|
|
1131
|
+
*
|
|
1132
|
+
* @param node
|
|
1133
|
+
*/
|
|
1134
|
+
function countLeaves(node) {
|
|
1135
|
+
if (isLeaf(node)) {
|
|
1145
1136
|
return 1;
|
|
1146
|
-
} else {
|
|
1147
|
-
return computeSampleStandardDeviation(depths);
|
|
1148
1137
|
}
|
|
1149
1138
|
|
|
1150
|
-
|
|
1139
|
+
let result = 0;
|
|
1140
|
+
|
|
1141
|
+
node.traversePreOrderUsingStack(function (node) {
|
|
1142
|
+
if (isLeaf(node)) {
|
|
1143
|
+
result++;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
return result;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1151
1150
|
|
|
1152
1151
|
/**
|
|
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}
|
|
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
|
|
1162
1155
|
*/
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
return leaf;
|
|
1167
|
-
};
|
|
1156
|
+
function fastVisitLeafNodeIntersection(node) {
|
|
1157
|
+
this(node, true);
|
|
1158
|
+
}
|
|
1168
1159
|
|
|
1169
1160
|
/**
|
|
1170
1161
|
*
|
|
@@ -1195,98 +1186,3 @@ function transplantNewCommonParent(node, child) {
|
|
|
1195
1186
|
return bNode;
|
|
1196
1187
|
}
|
|
1197
1188
|
|
|
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
|
-
};
|