DEADSOFTWARE

tree can render things (buggy)
[d2df-sdl.git] / src / game / z_aabbtree.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {$DEFINE aabbtree_many_asserts}
18 {$DEFINE aabbtree_query_count}
19 unit z_aabbtree;
21 interface
23 // ////////////////////////////////////////////////////////////////////////// //
24 type
25 Float = Single;
26 PFloat = ^Float;
28 TTreeFlesh = TObject;
31 // ////////////////////////////////////////////////////////////////////////// //
32 type
33 Ray2D = record
34 public
35 origX, origY: Float;
36 dirX, dirY: Float;
38 public
39 procedure normalizeDir ();
41 procedure setXYAngle (ax, ay: Float; aangle: Float); inline;
42 procedure setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Float); inline;
43 end;
45 // ////////////////////////////////////////////////////////////////////////// //
46 type
47 AABB2D = record
48 public
49 minX, minY, maxX, maxY: Float;
51 private
52 function getvalid (): Boolean; inline;
53 function getcenterX (): Float; inline;
54 function getcenterY (): Float; inline;
55 function getextentX (): Float; inline;
56 function getextentY (): Float; inline;
58 public
59 procedure setX0Y0X1Y1 (x0, y0, x1, y1: Float); inline;
60 procedure setXYWH (ax, ay, aw, ah: Float); inline;
62 procedure setMergeTwo (var aabb0, aabb1: AABB2D); inline;
64 function volume (): Float; inline;
66 procedure merge (var aabb: AABB2D); inline;
68 // return true if the current AABB contains the AABB given in parameter
69 function contains (var aabb: AABB2D): Boolean; inline; overload;
70 function contains (ax, ay: Float): Boolean; inline; overload;
72 // return true if the current AABB is overlapping with the AABB in parameter
73 // two AABBs overlap if they overlap in the two axes at the same time
74 function overlaps (var aabb: AABB2D): Boolean; inline; overload;
76 // ray direction must be normalized
77 function intersects (var ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload;
78 function intersects (ax, ay, bx, by: Float): Boolean; overload;
80 property valid: Boolean read getvalid;
81 property centerX: Float read getcenterX;
82 property centerY: Float read getcenterY;
83 property extentX: Float read getextentX;
84 property extentY: Float read getextentY;
85 end;
88 // ////////////////////////////////////////////////////////////////////////// //
89 (* Dynamic AABB tree (bounding volume hierarchy)
90 * based on the code from ReactPhysics3D physics library, http://www.reactphysics3d.com
91 * Copyright (c) 2010-2016 Daniel Chappuis
92 *
93 * This software is provided 'as-is', without any express or implied warranty.
94 * In no event will the authors be held liable for any damages arising from the
95 * use of this software.
96 *
97 * Permission is granted to anyone to use this software for any purpose,
98 * including commercial applications, and to alter it and redistribute it
99 * freely, subject to the following restrictions:
101 * 1. The origin of this software must not be misrepresented; you must not claim
102 * that you wrote the original software. If you use this software in a
103 * product, an acknowledgment in the product documentation would be
104 * appreciated but is not required.
106 * 2. Altered source versions must be plainly marked as such, and must not be
107 * misrepresented as being the original software.
109 * 3. This notice may not be removed or altered from any source distribution.
110 *)
111 // ////////////////////////////////////////////////////////////////////////// //
112 (*
113 * This class implements a dynamic AABB tree that is used for broad-phase
114 * collision detection. This data structure is inspired by Nathanael Presson's
115 * dynamic tree implementation in BulletPhysics. The following implementation is
116 * based on the one from Erin Catto in Box2D as described in the book
117 * "Introduction to Game Physics with Box2D" by Ian Parberry.
118 *)
119 type
120 PDTProxyRec = ^TDTProxyRec;
121 TDTProxyRec = record
122 private
123 mX, mY, mWidth, mHeight: Integer;
124 mQueryMark: DWord; // was this object visited at this query?
125 mObj: TObject;
126 mTag: Integer;
127 nextfree: Integer;
129 private
130 procedure setup (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
132 function getx1 (): Integer; inline;
133 function gety1 (): Integer; inline;
135 public
136 property x: Integer read mX;
137 property y: Integer read mY;
138 property width: Integer read mWidth;
139 property height: Integer read mHeight;
140 property x0: Integer read mX;
141 property y0: Integer read mY;
142 property x1: Integer read getx1;
143 property y1: Integer read gety1;
144 property obj: TObject read mObj;
145 property tag: Integer read mTag;
146 end;
148 // ////////////////////////////////////////////////////////////////////////// //
149 // Dynamic AABB Tree: can be used to speed up broad phase in various engines
150 type
151 TDynAABBTree = class(TObject)
152 private
153 type
154 PTreeNode = ^TTreeNode;
155 TTreeNode = record
156 public
157 const NullTreeNode = -1;
158 const Left = 0;
159 const Right = 1;
160 public
161 // a node is either in the tree (has a parent) or in the free nodes list (has a next node)
162 parentId: Integer;
163 //nextNodeId: Integer;
164 // a node is either a leaf (has data) or is an internal node (has children)
165 children: array [0..1] of Integer; // left and right child of the node (children[0] = left child)
166 //TODO: `flesh` can be united with `children`
167 flesh: TTreeFlesh;
168 // height of the node in the tree (-1 for free nodes)
169 height: SmallInt;
170 // fat axis aligned bounding box (AABB) corresponding to the node
171 aabb: AABB2D;
172 public
173 // return true if the node is a leaf of the tree
174 procedure clear ();
175 function leaf (): Boolean; inline;
176 function free (): Boolean; inline;
177 property nextNodeId: Integer read parentId write parentId;
178 //property flesh: Integer read children[0] write children[0];
179 end;
181 TVisitCheckerCB = function (node: PTreeNode): Boolean is nested;
182 TVisitVisitorCB = function (abody: TTreeFlesh): Boolean is nested;
184 public
185 // return `true` to stop
186 type TForEachLeafCB = function (abody: TTreeFlesh; const aabb: AABB2D): Boolean is nested; // WARNING! don't modify AABB here!
188 public
189 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
190 // also inflated in direction of the linear motion of the body by mutliplying the
191 // followin constant with the linear velocity and the elapsed time between two frames
192 const LinearMotionGapMultiplier = Float(1.7);
194 private
195 mNodes: array of TTreeNode; // nodes of the tree
196 mRootNodeId: Integer; // id of the root node of the tree
197 mFreeNodeId: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
198 mAllocCount: Integer; // number of allocated nodes in the tree
199 mNodeCount: Integer; // number of nodes in the tree
201 // extra AABB Gap used to allow the collision shape to move a little bit
202 // without triggering a large modification of the tree which can be costly
203 mExtraGap: Float;
205 private
206 function allocateNode (): Integer;
207 procedure releaseNode (nodeId: Integer);
208 procedure insertLeafNode (nodeId: Integer);
209 procedure removeLeafNode (nodeId: Integer);
210 function balanceSubTreeAtNode (nodeId: Integer): Integer;
211 function computeHeight (nodeId: Integer): Integer;
212 function insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer;
213 procedure setup ();
214 function visit (checker: TVisitCheckerCB; visitor: TVisitVisitorCB): Integer;
216 public
217 {$IFDEF aabbtree_query_count}
218 nodesVisited, nodesDeepVisited: Integer;
219 {$ENDIF}
221 public
222 // called when a overlapping node has been found during the call to forEachAABBOverlap()
223 // return `true` to stop
224 type TQueryOverlapCB = function (abody: TTreeFlesh): Boolean is nested;
225 type TSegQueryCallback = function (abody: TTreeFlesh; ax, ay, bx, by: Float): Float is nested; // return dist from (ax,ay) to abody
227 TSegmentQueryResult = record
228 dist: Float; // <0: nothing was hit
229 flesh: TTreeFlesh;
231 procedure reset (); inline;
232 function valid (): Boolean; inline;
233 end;
235 public
236 constructor Create (extraAABBGap: Float=0.0);
237 destructor Destroy (); override;
239 // clear all the nodes and reset the tree
240 procedure reset ();
242 function forEachLeaf (dg: TForEachLeafCB): Boolean; // WARNING! don't modify AABB/tree here!
243 procedure getRootAABB (var aabb: AABB2D);
245 function isValidId (id: Integer): Boolean; inline;
246 function getNodeObjectId (nodeid: Integer): TTreeFlesh; inline;
247 procedure getNodeFatAABB (var aabb: AABB2D; nodeid: Integer); inline;
249 // return `false` for invalid flesh
250 function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; virtual; abstract;
252 // insert an object into the tree
253 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
254 // AABB for static object will not be "fat" (simple optimization)
255 // WARNING! inserting the same object several times *WILL* break everything!
256 function insertObject (flesh: TTreeFlesh; staticObject: Boolean=false): Integer;
258 // remove an object from the tree
259 // WARNING: ids of removed objects can be reused on later insertions!
260 procedure removeObject (nodeId: Integer);
262 (** update the dynamic tree after an object has moved.
264 * if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
265 * otherwise, the corresponding node is removed and reinserted into the tree.
266 * the method returns true if the object has been reinserted into the tree.
267 * the `dispX` and `dispY` parameters are the linear velocity of the AABB multiplied by the elapsed time between two frames.
268 * if the `forceReinsert` parameter is `true`, we force a removal and reinsertion of the node
269 * (this can be useful if the shape AABB has become much smaller than the previous one for instance).
271 * note that you should call this method if body's AABB was modified, even if the body wasn't moved.
273 * if `forceReinsert` = `true` and both `dispX` and `dispY` are zeroes, convert object to "static" (don't extrude AABB).
275 * return `true` if the tree was modified.
276 *)
277 function updateObject (nodeId: Integer; dispX, dispY: Float; forceReinsert: Boolean=false): Boolean;
279 procedure aabbQuery (ax, ay, aw, ah: Float; cb: TQueryOverlapCB);
280 function pointQuery (ax, ay: Float; cb: TQueryOverlapCB): TTreeFlesh;
281 function segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: Float; cb: TSegQueryCallback): Boolean;
283 function computeTreeHeight (): Integer; // compute the height of the tree
285 property extraGap: Float read mExtraGap write mExtraGap;
286 property nodeCount: Integer read mNodeCount;
287 property nodeAlloced: Integer read mAllocCount;
288 end;
291 implementation
293 uses
294 SysUtils;
297 // ////////////////////////////////////////////////////////////////////////// //
298 function minI (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end;
299 function maxI (a, b: Integer): Integer; inline; begin if (a > b) then result := a else result := b; end;
302 // ////////////////////////////////////////////////////////////////////////// //
303 procedure TDTProxyRec.setup (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
304 begin
305 mX := aX;
306 mY := aY;
307 mWidth := aWidth;
308 mHeight := aHeight;
309 mQueryMark := 0;
310 mObj := aObj;
311 mTag := aTag;
312 nextfree := -1;
313 end;
315 function TDTProxyRec.getx1 (): Integer; begin result := mX+mWidth-1; end;
316 function TDTProxyRec.gety1 (): Integer; begin result := mY+mHeight-1; end;
319 // ////////////////////////////////////////////////////////////////////////// //
320 procedure Ray2D.normalizeDir ();
321 var
322 invlen: Float;
323 begin
324 invlen := Float(1.0)/sqrt(dirX*dirX+dirY*dirY);
325 dirX *= invlen;
326 dirY *= invlen;
327 end;
329 procedure Ray2D.setXYAngle (ax, ay: Float; aangle: Float);
330 begin
331 origX := ax;
332 origY := ay;
333 dirX := cos(aangle);
334 dirY := sin(aangle);
335 end;
337 procedure Ray2D.setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Float);
338 begin
339 origX := ax0;
340 origY := ay0;
341 dirX := ax1-ax0;
342 dirY := ay1-ay0;
343 normalizeDir();
344 end;
347 // ////////////////////////////////////////////////////////////////////////// //
348 function AABB2D.getvalid (): Boolean; begin result := (minX <= maxX) and (minY <= maxY); end;
350 function AABB2D.getcenterX (): Float; begin result := (minX+maxX)/2; end;
351 function AABB2D.getcenterY (): Float; begin result := (minY+maxY)/2; end;
352 function AABB2D.getextentX (): Float; begin result := (maxX-minX)+1; end;
353 function AABB2D.getextentY (): Float; begin result := (maxY-minY)+1; end;
356 procedure AABB2D.setX0Y0X1Y1 (x0, y0, x1, y1: Float);
357 begin
358 if (x0 < x1) then begin minX := x0; maxX := x1; end else begin minX := x1; maxX := x0; end;
359 if (y0 < y1) then begin minY := y0; maxY := y1; end else begin minY := y1; maxY := y0; end;
360 end;
363 procedure AABB2D.setXYWH (ax, ay, aw, ah: Float);
364 begin
365 if (aw < 0) then aw := 0;
366 if (ah < 0) then ah := 0;
367 minX := ax;
368 minY := ay;
369 maxX := ax+aw-1;
370 maxY := ay+ah-1;
371 end;
374 procedure AABB2D.setMergeTwo (var aabb0, aabb1: AABB2D);
375 var
376 x0, y0, x1, y1: Float;
377 begin
378 if (aabb0.minX < aabb1.minX) then x0 := aabb0.minX else x0 := aabb1.minX;
379 if (aabb0.minY < aabb1.minY) then y0 := aabb0.minY else y0 := aabb1.minY;
381 if (aabb0.maxX > aabb1.maxX) then x1 := aabb0.maxX else x1 := aabb1.maxX;
382 if (aabb0.maxY > aabb1.maxY) then y1 := aabb0.maxY else y1 := aabb1.maxY;
384 minX := x0;
385 minY := y0;
386 maxX := x1;
387 maxY := y1;
388 end;
391 function AABB2D.volume (): Float;
392 var
393 diffX, diffY: Float;
394 begin
395 diffX := maxX-minX;
396 diffY := maxY-minY;
397 result := diffX*diffY;
398 end;
401 procedure AABB2D.merge (var aabb: AABB2D);
402 begin
403 if (minX > aabb.minX) then minX := aabb.minX;
404 if (minY > aabb.minY) then minY := aabb.minY;
405 if (maxX < aabb.maxX) then maxX := aabb.maxX;
406 if (maxY < aabb.maxY) then maxY := aabb.maxY;
407 end;
410 function AABB2D.contains (var aabb: AABB2D): Boolean; overload;
411 begin
412 result :=
413 (aabb.minX >= minX) and (aabb.minY >= minY) and
414 (aabb.maxX <= maxX) and (aabb.maxY <= maxY);
415 end;
418 function AABB2D.contains (ax, ay: Float): Boolean; overload;
419 begin
420 result := (ax >= minX) and (ay >= minY) and (ax <= maxX) and (ay <= maxY);
421 end;
424 function AABB2D.overlaps (var aabb: AABB2D): Boolean; overload;
425 begin
426 result := false;
427 // exit with no intersection if found separated along any axis
428 if (maxX < aabb.minX) or (minX > aabb.maxX) then exit;
429 if (maxY < aabb.minY) or (minY > aabb.maxY) then exit;
430 result := true;
431 end;
434 // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box
435 // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
436 function AABB2D.intersects (var ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload;
437 var
438 dinv, t1, t2, tmp: Float;
439 tmin, tmax: Float;
440 begin
441 // ok with coplanars
442 tmin := -1.0e100;
443 tmax := 1.0e100;
444 // do X
445 if (ray.dirX <> 0.0) then
446 begin
447 dinv := Float(1.0)/ray.dirX;
448 t1 := (minX-ray.origX)*dinv;
449 t2 := (maxX-ray.origX)*dinv;
450 if (t1 < t2) then tmin := t1 else tmin := t2;
451 if (t1 > t2) then tmax := t1 else tmax := t2;
452 end;
453 // do Y
454 if (ray.dirY <> 0.0) then
455 begin
456 dinv := Float(1.0)/ray.dirY;
457 t1 := (minY-ray.origY)*dinv;
458 t2 := (maxY-ray.origY)*dinv;
459 // tmin
460 if (t1 < t2) then tmp := t1 else tmp := t2; // min(t1, t2)
461 if (tmax < tmp) then tmp := tmax; // min(tmax, tmp)
462 if (tmin > tmp) then tmin := tmp; // max(tmin, tmp)
463 // tmax
464 if (t1 > t2) then tmp := t1 else tmp := t2; // max(t1, t2)
465 if (tmin > tmp) then tmp := tmin; // max(tmin, tmp)
466 if (tmax < tmp) then tmax := tmp; // min(tmax, tmp)
467 end;
468 if (tmin > 0) then tmp := tmin else tmp := 0;
469 if (tmax > tmp) then
470 begin
471 if (tmino <> nil) then tmino^ := tmin;
472 if (tmaxo <> nil) then tmaxo^ := tmax;
473 result := true;
474 end
475 else
476 begin
477 result := false;
478 end;
479 end;
481 function AABB2D.intersects (ax, ay, bx, by: Float): Boolean; overload;
482 var
483 tmin: Float;
484 ray: Ray2D;
485 begin
486 result := true;
487 // it may be faster to first check if start or end point is inside AABB (this is sometimes enough for dyntree)
488 if (ax >= minX) and (ay >= minY) and (ax <= maxX) and (ay <= maxY) then exit; // a
489 if (bx >= minX) and (by >= minY) and (bx <= maxX) and (by <= maxY) then exit; // b
490 // nope, do it hard way
491 ray.setX0Y0X1Y1(ax, ay, bx, by);
492 if not intersects(ray, @tmin) then begin result := false; exit; end;
493 if (tmin < 0) then exit; // inside, just in case
494 bx := bx-ax;
495 by := by-ay;
496 result := (tmin*tmin <= bx*bx+by*by);
497 end;
500 // ////////////////////////////////////////////////////////////////////////// //
501 procedure TDynAABBTree.TSegmentQueryResult.reset (); begin dist := -1; flesh := nil; end;
502 function TDynAABBTree.TSegmentQueryResult.valid (): Boolean; begin result := (dist >= 0) and (flesh <> nil); end;
505 // ////////////////////////////////////////////////////////////////////////// //
506 function TDynAABBTree.TTreeNode.leaf (): Boolean; begin result := (height = 0); end;
507 function TDynAABBTree.TTreeNode.free (): Boolean; begin result := (height = -1); end;
509 procedure TDynAABBTree.TTreeNode.clear ();
510 begin
511 parentId := 0;
512 children[0] := 0;
513 children[1] := 0;
514 flesh := nil;
515 height := 0;
516 aabb.setX0Y0X1Y1(0, 0, 0, 0);
517 end;
520 // ////////////////////////////////////////////////////////////////////////// //
521 // allocate and return a node to use in the tree
522 function TDynAABBTree.allocateNode (): Integer;
523 var
524 i, newsz, freeNodeId: Integer;
525 begin
526 // if there is no more allocated node to use
527 if (mFreeNodeId = TTreeNode.NullTreeNode) then
528 begin
529 {$IFDEF aabbtree_many_asserts}assert(mNodeCount = mAllocCount);{$ENDIF}
530 // allocate more nodes in the tree
531 if (mAllocCount < 8192) then newsz := mAllocCount*2 else newsz := mAllocCount+8192;
532 SetLength(mNodes, newsz);
533 mAllocCount := newsz;
534 // initialize the allocated nodes
535 for i := mNodeCount to mAllocCount-2 do
536 begin
537 mNodes[i].nextNodeId := i+1;
538 mNodes[i].height := -1;
539 end;
540 mNodes[mAllocCount-1].nextNodeId := TTreeNode.NullTreeNode;
541 mNodes[mAllocCount-1].height := -1;
542 mFreeNodeId := mNodeCount;
543 end;
544 // get the next free node
545 freeNodeId := mFreeNodeId;
546 {$IFDEF aabbtree_many_asserts}assert((freeNodeId >= mNodeCount) and (freeNodeId < mAllocCount));{$ENDIF}
547 mFreeNodeId := mNodes[freeNodeId].nextNodeId;
548 mNodes[freeNodeId].parentId := TTreeNode.NullTreeNode;
549 mNodes[freeNodeId].height := 0;
550 Inc(mNodeCount);
551 result := freeNodeId;
552 end;
555 // release a node
556 procedure TDynAABBTree.releaseNode (nodeId: Integer);
557 begin
558 {$IFDEF aabbtree_many_asserts}assert(mNodeCount > 0);{$ENDIF}
559 {$IFDEF aabbtree_many_asserts}assert((nodeId >= 0) and (nodeId < mAllocCount));{$ENDIF}
560 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].height >= 0);{$ENDIF}
561 mNodes[nodeId].nextNodeId := mFreeNodeId;
562 mNodes[nodeId].height := -1;
563 mFreeNodeId := nodeId;
564 Dec(mNodeCount);
565 end;
568 // insert a leaf node in the tree
569 // the process of inserting a new leaf node in the dynamic tree is described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
570 procedure TDynAABBTree.insertLeafNode (nodeId: Integer);
571 var
572 newNodeAABB, mergedAABBs, currentAndLeftAABB, currentAndRightAABB: AABB2D;
573 currentNodeId: Integer;
574 leftChild, rightChild, siblingNode: Integer;
575 oldParentNode, newParentNode: Integer;
576 volumeAABB, mergedVolume: Float;
577 costS, costI, costLeft, costRight: Float;
578 begin
579 // if the tree is empty
580 if (mRootNodeId = TTreeNode.NullTreeNode) then
581 begin
582 mRootNodeId := nodeId;
583 mNodes[mRootNodeId].parentId := TTreeNode.NullTreeNode;
584 exit;
585 end;
587 {$IFDEF aabbtree_many_asserts}assert(mRootNodeId <> TTreeNode.NullTreeNode);{$ENDIF}
589 // find the best sibling node for the new node
590 newNodeAABB := mNodes[nodeId].aabb;
591 currentNodeId := mRootNodeId;
592 while not mNodes[currentNodeId].leaf do
593 begin
594 leftChild := mNodes[currentNodeId].children[TTreeNode.Left];
595 rightChild := mNodes[currentNodeId].children[TTreeNode.Right];
597 // compute the merged AABB
598 volumeAABB := mNodes[currentNodeId].aabb.volume;
599 mergedAABBs.setMergeTwo(mNodes[currentNodeId].aabb, newNodeAABB);
600 mergedVolume := mergedAABBs.volume;
602 // compute the cost of making the current node the sibling of the new node
603 costS := Float(2.0)*mergedVolume;
605 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
606 costI := Float(2.0)*(mergedVolume-volumeAABB);
608 // compute the cost of descending into the left child
609 currentAndLeftAABB.setMergeTwo(newNodeAABB, mNodes[leftChild].aabb);
610 if (mNodes[leftChild].leaf) then
611 begin
612 costLeft := currentAndLeftAABB.volume+costI;
613 end
614 else
615 begin
616 costLeft := costI+currentAndLeftAABB.volume-mNodes[leftChild].aabb.volume;
617 end;
619 // compute the cost of descending into the right child
620 currentAndRightAABB.setMergeTwo(newNodeAABB, mNodes[rightChild].aabb);
621 if (mNodes[rightChild].leaf) then
622 begin
623 costRight := currentAndRightAABB.volume+costI;
624 end
625 else
626 begin
627 costRight := costI+currentAndRightAABB.volume-mNodes[rightChild].aabb.volume;
628 end;
630 // if the cost of making the current node a sibling of the new node is smaller than the cost of going down into the left or right child
631 if (costS < costLeft) and (costS < costRight) then break;
633 // it is cheaper to go down into a child of the current node, choose the best child
634 //currentNodeId = (costLeft < costRight ? leftChild : rightChild);
635 if (costLeft < costRight) then currentNodeId := leftChild else currentNodeId := rightChild;
636 end;
638 siblingNode := currentNodeId;
640 // create a new parent for the new node and the sibling node
641 oldParentNode := mNodes[siblingNode].parentId;
642 newParentNode := allocateNode();
643 mNodes[newParentNode].parentId := oldParentNode;
644 mNodes[newParentNode].aabb.setMergeTwo(mNodes[siblingNode].aabb, newNodeAABB);
645 mNodes[newParentNode].height := mNodes[siblingNode].height+1;
646 {$IFDEF aabbtree_many_asserts}assert(mNodes[newParentNode].height > 0);{$ENDIF}
648 // if the sibling node was not the root node
649 if (oldParentNode <> TTreeNode.NullTreeNode) then
650 begin
651 {$IFDEF aabbtree_many_asserts}assert(not mNodes[oldParentNode].leaf);{$ENDIF}
652 if (mNodes[oldParentNode].children[TTreeNode.Left] = siblingNode) then
653 begin
654 mNodes[oldParentNode].children[TTreeNode.Left] := newParentNode;
655 end
656 else
657 begin
658 mNodes[oldParentNode].children[TTreeNode.Right] := newParentNode;
659 end;
660 mNodes[newParentNode].children[TTreeNode.Left] := siblingNode;
661 mNodes[newParentNode].children[TTreeNode.Right] := nodeId;
662 mNodes[siblingNode].parentId := newParentNode;
663 mNodes[nodeId].parentId := newParentNode;
664 end
665 else
666 begin
667 // if the sibling node was the root node
668 mNodes[newParentNode].children[TTreeNode.Left] := siblingNode;
669 mNodes[newParentNode].children[TTreeNode.Right] := nodeId;
670 mNodes[siblingNode].parentId := newParentNode;
671 mNodes[nodeId].parentId := newParentNode;
672 mRootNodeId := newParentNode;
673 end;
675 // move up in the tree to change the AABBs that have changed
676 currentNodeId := mNodes[nodeId].parentId;
677 {$IFDEF aabbtree_many_asserts}assert(not mNodes[currentNodeId].leaf);{$ENDIF}
678 while (currentNodeId <> TTreeNode.NullTreeNode) do
679 begin
680 // balance the sub-tree of the current node if it is not balanced
681 currentNodeId := balanceSubTreeAtNode(currentNodeId);
682 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
684 {$IFDEF aabbtree_many_asserts}assert(not mNodes[currentNodeId].leaf);{$ENDIF}
685 leftChild := mNodes[currentNodeId].children[TTreeNode.Left];
686 rightChild := mNodes[currentNodeId].children[TTreeNode.Right];
687 {$IFDEF aabbtree_many_asserts}assert(leftChild <> TTreeNode.NullTreeNode);{$ENDIF}
688 {$IFDEF aabbtree_many_asserts}assert(rightChild <> TTreeNode.NullTreeNode);{$ENDIF}
690 // recompute the height of the node in the tree
691 mNodes[currentNodeId].height := maxI(mNodes[leftChild].height, mNodes[rightChild].height)+1;
692 {$IFDEF aabbtree_many_asserts}assert(mNodes[currentNodeId].height > 0);{$ENDIF}
694 // recompute the AABB of the node
695 mNodes[currentNodeId].aabb.setMergeTwo(mNodes[leftChild].aabb, mNodes[rightChild].aabb);
697 currentNodeId := mNodes[currentNodeId].parentId;
698 end;
700 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
701 end;
704 // remove a leaf node from the tree
705 procedure TDynAABBTree.removeLeafNode (nodeId: Integer);
706 var
707 currentNodeId, parentNodeId, grandParentNodeId, siblingNodeId: Integer;
708 leftChildId, rightChildId: Integer;
709 begin
710 {$IFDEF aabbtree_many_asserts}assert((nodeId >= 0) and (nodeId < mAllocCount));{$ENDIF}
711 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
713 // if we are removing the root node (root node is a leaf in this case)
714 if (mRootNodeId = nodeId) then begin mRootNodeId := TTreeNode.NullTreeNode; exit; end;
716 parentNodeId := mNodes[nodeId].parentId;
717 grandParentNodeId := mNodes[parentNodeId].parentId;
719 if (mNodes[parentNodeId].children[TTreeNode.Left] = nodeId) then
720 begin
721 siblingNodeId := mNodes[parentNodeId].children[TTreeNode.Right];
722 end
723 else
724 begin
725 siblingNodeId := mNodes[parentNodeId].children[TTreeNode.Left];
726 end;
728 // if the parent of the node to remove is not the root node
729 if (grandParentNodeId <> TTreeNode.NullTreeNode) then
730 begin
731 // destroy the parent node
732 if (mNodes[grandParentNodeId].children[TTreeNode.Left] = parentNodeId) then
733 begin
734 mNodes[grandParentNodeId].children[TTreeNode.Left] := siblingNodeId;
735 end
736 else
737 begin
738 {$IFDEF aabbtree_many_asserts}assert(mNodes[grandParentNodeId].children[TTreeNode.Right] = parentNodeId);{$ENDIF}
739 mNodes[grandParentNodeId].children[TTreeNode.Right] := siblingNodeId;
740 end;
741 mNodes[siblingNodeId].parentId := grandParentNodeId;
742 releaseNode(parentNodeId);
744 // now, we need to recompute the AABBs of the node on the path back to the root and make sure that the tree is still balanced
745 currentNodeId := grandParentNodeId;
746 while (currentNodeId <> TTreeNode.NullTreeNode) do
747 begin
748 // balance the current sub-tree if necessary
749 currentNodeId := balanceSubTreeAtNode(currentNodeId);
751 {$IFDEF aabbtree_many_asserts}assert(not mNodes[currentNodeId].leaf);{$ENDIF}
753 // get the two children of the current node
754 leftChildId := mNodes[currentNodeId].children[TTreeNode.Left];
755 rightChildId := mNodes[currentNodeId].children[TTreeNode.Right];
757 // recompute the AABB and the height of the current node
758 mNodes[currentNodeId].aabb.setMergeTwo(mNodes[leftChildId].aabb, mNodes[rightChildId].aabb);
759 mNodes[currentNodeId].height := maxI(mNodes[leftChildId].height, mNodes[rightChildId].height)+1;
760 {$IFDEF aabbtree_many_asserts}assert(mNodes[currentNodeId].height > 0);{$ENDIF}
762 currentNodeId := mNodes[currentNodeId].parentId;
763 end;
764 end
765 else
766 begin
767 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
768 mRootNodeId := siblingNodeId;
769 mNodes[siblingNodeId].parentId := TTreeNode.NullTreeNode;
770 releaseNode(parentNodeId);
771 end;
772 end;
775 // balance the sub-tree of a given node using left or right rotations
776 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
777 // this method returns the new root node id
778 function TDynAABBTree.balanceSubTreeAtNode (nodeId: Integer): Integer;
779 var
780 nodeA, nodeB, nodeC, nodeF, nodeG: PTreeNode;
781 nodeBId, nodeCId, nodeFId, nodeGId: Integer;
782 balanceFactor: Integer;
783 begin
784 {$IFDEF aabbtree_many_asserts}assert(nodeId <> TTreeNode.NullTreeNode);{$ENDIF}
786 nodeA := @mNodes[nodeId];
788 // if the node is a leaf or the height of A's sub-tree is less than 2
789 if (nodeA.leaf) or (nodeA.height < 2) then begin result := nodeId; exit; end; // do not perform any rotation
791 // get the two children nodes
792 nodeBId := nodeA.children[TTreeNode.Left];
793 nodeCId := nodeA.children[TTreeNode.Right];
794 {$IFDEF aabbtree_many_asserts}assert((nodeBId >= 0) and (nodeBId < mAllocCount));{$ENDIF}
795 {$IFDEF aabbtree_many_asserts}assert((nodeCId >= 0) and (nodeCId < mAllocCount));{$ENDIF}
796 nodeB := @mNodes[nodeBId];
797 nodeC := @mNodes[nodeCId];
799 // compute the factor of the left and right sub-trees
800 balanceFactor := nodeC.height-nodeB.height;
802 // if the right node C is 2 higher than left node B
803 if (balanceFactor > 1) then
804 begin
805 {$IFDEF aabbtree_many_asserts}assert(not nodeC.leaf);{$ENDIF}
807 nodeFId := nodeC.children[TTreeNode.Left];
808 nodeGId := nodeC.children[TTreeNode.Right];
809 {$IFDEF aabbtree_many_asserts}assert((nodeFId >= 0) and (nodeFId < mAllocCount));{$ENDIF}
810 {$IFDEF aabbtree_many_asserts}assert((nodeGId >= 0) and (nodeGId < mAllocCount));{$ENDIF}
811 nodeF := @mNodes[nodeFId];
812 nodeG := @mNodes[nodeGId];
814 nodeC.children[TTreeNode.Left] := nodeId;
815 nodeC.parentId := nodeA.parentId;
816 nodeA.parentId := nodeCId;
818 if (nodeC.parentId <> TTreeNode.NullTreeNode) then
819 begin
820 if (mNodes[nodeC.parentId].children[TTreeNode.Left] = nodeId) then
821 begin
822 mNodes[nodeC.parentId].children[TTreeNode.Left] := nodeCId;
823 end
824 else
825 begin
826 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeC.parentId].children[TTreeNode.Right] = nodeId);{$ENDIF}
827 mNodes[nodeC.parentId].children[TTreeNode.Right] := nodeCId;
828 end;
829 end
830 else
831 begin
832 mRootNodeId := nodeCId;
833 end;
835 {$IFDEF aabbtree_many_asserts}assert(not nodeC.leaf);{$ENDIF}
836 {$IFDEF aabbtree_many_asserts}assert(not nodeA.leaf);{$ENDIF}
838 // if the right node C was higher than left node B because of the F node
839 if (nodeF.height > nodeG.height) then
840 begin
841 nodeC.children[TTreeNode.Right] := nodeFId;
842 nodeA.children[TTreeNode.Right] := nodeGId;
843 nodeG.parentId := nodeId;
845 // recompute the AABB of node A and C
846 nodeA.aabb.setMergeTwo(nodeB.aabb, nodeG.aabb);
847 nodeC.aabb.setMergeTwo(nodeA.aabb, nodeF.aabb);
849 // recompute the height of node A and C
850 nodeA.height := maxI(nodeB.height, nodeG.height)+1;
851 nodeC.height := maxI(nodeA.height, nodeF.height)+1;
852 {$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
853 {$IFDEF aabbtree_many_asserts}assert(nodeC.height > 0);{$ENDIF}
854 end
855 else
856 begin
857 // if the right node C was higher than left node B because of node G
858 nodeC.children[TTreeNode.Right] := nodeGId;
859 nodeA.children[TTreeNode.Right] := nodeFId;
860 nodeF.parentId := nodeId;
862 // recompute the AABB of node A and C
863 nodeA.aabb.setMergeTwo(nodeB.aabb, nodeF.aabb);
864 nodeC.aabb.setMergeTwo(nodeA.aabb, nodeG.aabb);
866 // recompute the height of node A and C
867 nodeA.height := maxI(nodeB.height, nodeF.height)+1;
868 nodeC.height := maxI(nodeA.height, nodeG.height)+1;
869 {$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
870 {$IFDEF aabbtree_many_asserts}assert(nodeC.height > 0);{$ENDIF}
871 end;
873 // return the new root of the sub-tree
874 result := nodeCId;
875 exit;
876 end;
878 // if the left node B is 2 higher than right node C
879 if (balanceFactor < -1) then
880 begin
881 {$IFDEF aabbtree_many_asserts}assert(not nodeB.leaf);{$ENDIF}
883 nodeFId := nodeB.children[TTreeNode.Left];
884 nodeGId := nodeB.children[TTreeNode.Right];
885 {$IFDEF aabbtree_many_asserts}assert((nodeFId >= 0) and (nodeFId < mAllocCount));{$ENDIF}
886 {$IFDEF aabbtree_many_asserts}assert((nodeGId >= 0) and (nodeGId < mAllocCount));{$ENDIF}
887 nodeF := @mNodes[nodeFId];
888 nodeG := @mNodes[nodeGId];
890 nodeB.children[TTreeNode.Left] := nodeId;
891 nodeB.parentId := nodeA.parentId;
892 nodeA.parentId := nodeBId;
894 if (nodeB.parentId <> TTreeNode.NullTreeNode) then
895 begin
896 if (mNodes[nodeB.parentId].children[TTreeNode.Left] = nodeId) then
897 begin
898 mNodes[nodeB.parentId].children[TTreeNode.Left] := nodeBId;
899 end
900 else
901 begin
902 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeB.parentId].children[TTreeNode.Right] = nodeId);{$ENDIF}
903 mNodes[nodeB.parentId].children[TTreeNode.Right] := nodeBId;
904 end;
905 end
906 else
907 begin
908 mRootNodeId := nodeBId;
909 end;
911 {$IFDEF aabbtree_many_asserts}assert(not nodeB.leaf);{$ENDIF}
912 {$IFDEF aabbtree_many_asserts}assert(not nodeA.leaf);{$ENDIF}
914 // if the left node B was higher than right node C because of the F node
915 if (nodeF.height > nodeG.height) then
916 begin
917 nodeB.children[TTreeNode.Right] := nodeFId;
918 nodeA.children[TTreeNode.Left] := nodeGId;
919 nodeG.parentId := nodeId;
921 // recompute the AABB of node A and B
922 nodeA.aabb.setMergeTwo(nodeC.aabb, nodeG.aabb);
923 nodeB.aabb.setMergeTwo(nodeA.aabb, nodeF.aabb);
925 // recompute the height of node A and B
926 nodeA.height := maxI(nodeC.height, nodeG.height)+1;
927 nodeB.height := maxI(nodeA.height, nodeF.height)+1;
928 {$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
929 {$IFDEF aabbtree_many_asserts}assert(nodeB.height > 0);{$ENDIF}
930 end
931 else
932 begin
933 // if the left node B was higher than right node C because of node G
934 nodeB.children[TTreeNode.Right] := nodeGId;
935 nodeA.children[TTreeNode.Left] := nodeFId;
936 nodeF.parentId := nodeId;
938 // recompute the AABB of node A and B
939 nodeA.aabb.setMergeTwo(nodeC.aabb, nodeF.aabb);
940 nodeB.aabb.setMergeTwo(nodeA.aabb, nodeG.aabb);
942 // recompute the height of node A and B
943 nodeA.height := maxI(nodeC.height, nodeF.height)+1;
944 nodeB.height := maxI(nodeA.height, nodeG.height)+1;
945 {$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
946 {$IFDEF aabbtree_many_asserts}assert(nodeB.height > 0);{$ENDIF}
947 end;
949 // return the new root of the sub-tree
950 result := nodeBId;
951 exit;
952 end;
954 // if the sub-tree is balanced, return the current root node
955 result := nodeId;
956 end;
959 // compute the height of a given node in the tree
960 function TDynAABBTree.computeHeight (nodeId: Integer): Integer;
961 var
962 node: PTreeNode;
963 leftHeight, rightHeight: Integer;
964 begin
965 {$IFDEF aabbtree_many_asserts}assert((nodeId >= 0) and (nodeId < mAllocCount));{$ENDIF}
966 node := @mNodes[nodeId];
968 // if the node is a leaf, its height is zero
969 if (node.leaf) then begin result := 0; exit; end;
971 // compute the height of the left and right sub-tree
972 leftHeight := computeHeight(node.children[TTreeNode.Left]);
973 rightHeight := computeHeight(node.children[TTreeNode.Right]);
975 // return the height of the node
976 result := 1+maxI(leftHeight, rightHeight);
977 end;
980 // internally add an object into the tree
981 function TDynAABBTree.insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer;
982 var
983 nodeId: Integer;
984 begin
985 // get the next available node (or allocate new ones if necessary)
986 nodeId := allocateNode();
988 // create the fat aabb to use in the tree
989 mNodes[nodeId].aabb := aabb;
990 if (not staticObject) then
991 begin
992 mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX-mExtraGap;
993 mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY-mExtraGap;
994 mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+mExtraGap;
995 mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+mExtraGap;
996 end;
998 // set the height of the node in the tree
999 mNodes[nodeId].height := 0;
1001 // insert the new leaf node in the tree
1002 insertLeafNode(nodeId);
1003 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
1005 {$IFDEF aabbtree_many_asserts}assert(nodeId >= 0);{$ENDIF}
1007 // return the id of the node
1008 result := nodeId;
1009 end;
1012 // initialize the tree
1013 procedure TDynAABBTree.setup ();
1014 var
1015 i: Integer;
1016 begin
1017 mRootNodeId := TTreeNode.NullTreeNode;
1018 mNodeCount := 0;
1019 mAllocCount := 8192;
1021 SetLength(mNodes, mAllocCount);
1022 //memset(mNodes, 0, mAllocCount*TTreeNode.sizeof);
1023 for i := 0 to mAllocCount-1 do mNodes[i].clear();
1025 // initialize the allocated nodes
1026 for i := 0 to mAllocCount-2 do
1027 begin
1028 mNodes[i].nextNodeId := i+1;
1029 mNodes[i].height := -1;
1030 end;
1031 mNodes[mAllocCount-1].nextNodeId := TTreeNode.NullTreeNode;
1032 mNodes[mAllocCount-1].height := -1;
1033 mFreeNodeId := 0;
1034 end;
1037 // also, checks if the tree structure is valid (for debugging purpose)
1038 function TDynAABBTree.forEachLeaf (dg: TForEachLeafCB): Boolean;
1039 function forEachNode (nodeId: Integer): Boolean;
1040 var
1041 pNode: PTreeNode;
1042 leftChild, rightChild, height: Integer;
1043 aabb: AABB2D;
1044 begin
1045 result := false;
1046 if (nodeId = TTreeNode.NullTreeNode) then exit;
1047 // if it is the root
1048 if (nodeId = mRootNodeId) then assert(mNodes[nodeId].parentId = TTreeNode.NullTreeNode);
1049 // get the children nodes
1050 pNode := @mNodes[nodeId];
1051 assert(pNode.height >= 0);
1052 assert(pNode.aabb.volume > 0);
1053 // if the current node is a leaf
1054 if (pNode.leaf) then
1055 begin
1056 assert(pNode.height = 0);
1057 result := dg(pNode.flesh, pNode.aabb);
1058 end
1059 else
1060 begin
1061 leftChild := pNode.children[TTreeNode.Left];
1062 rightChild := pNode.children[TTreeNode.Right];
1063 // check that the children node Ids are valid
1064 assert((0 <= leftChild) and (leftChild < mAllocCount));
1065 assert((0 <= rightChild) and (rightChild < mAllocCount));
1066 // check that the children nodes have the correct parent node
1067 assert(mNodes[leftChild].parentId = nodeId);
1068 assert(mNodes[rightChild].parentId = nodeId);
1069 // check the height of node
1070 height := 1+maxI(mNodes[leftChild].height, mNodes[rightChild].height);
1071 assert(mNodes[nodeId].height = height);
1072 // check the AABB of the node
1073 aabb.setMergeTwo(mNodes[leftChild].aabb, mNodes[rightChild].aabb);
1074 assert(aabb.minX = mNodes[nodeId].aabb.minX);
1075 assert(aabb.minY = mNodes[nodeId].aabb.minY);
1076 assert(aabb.maxX = mNodes[nodeId].aabb.maxX);
1077 assert(aabb.maxY = mNodes[nodeId].aabb.maxY);
1078 // recursively check the children nodes
1079 result := forEachNode(leftChild);
1080 if not result then result := forEachNode(rightChild);
1081 end;
1082 end;
1084 begin
1085 result := false;
1086 if not assigned(dg) then exit;
1087 // recursively check each node
1088 result := forEachNode(mRootNodeId);
1089 end;
1092 // return `true` from visitor to stop immediately
1093 // checker should check if this node should be considered to further checking
1094 // returns tree node if visitor says stop or -1
1095 function TDynAABBTree.visit (checker: TVisitCheckerCB; visitor: TVisitVisitorCB): Integer;
1096 var
1097 stack: array [0..255] of Integer; // stack with the nodes to visit
1098 bigstack: array of Integer = nil;
1099 sp: Integer = 0;
1101 procedure spush (id: Integer);
1102 var
1103 xsp: Integer;
1104 begin
1105 if (sp < length(stack)) then
1106 begin
1107 // use "small stack"
1108 stack[sp] := id;
1109 Inc(sp);
1110 end
1111 else
1112 begin
1113 // use "big stack"
1114 xsp := sp-length(stack);
1115 if (xsp < length(bigstack)) then
1116 begin
1117 // reuse
1118 bigstack[xsp] := id;
1119 end
1120 else
1121 begin
1122 // grow
1123 SetLength(bigstack, length(bigstack)+1);
1124 bigstack[high(bigstack)] := id;
1125 end;
1126 Inc(sp);
1127 end;
1128 end;
1130 function spop (): Integer;
1131 begin
1132 assert(sp > 0);
1133 if (sp <= length(stack)) then
1134 begin
1135 // use "small stack"
1136 Dec(sp);
1137 result := stack[sp];
1138 end
1139 else
1140 begin
1141 // use "big stack"
1142 Dec(sp);
1143 result := bigstack[sp-length(stack)];
1144 end;
1145 end;
1147 var
1148 nodeId: Integer;
1149 node: PTreeNode;
1150 begin
1151 if not assigned(checker) then begin result := -1; exit; end;
1152 //if not assigned(visitor) then begin result := -1; exit; end;
1153 try
1154 {$IFDEF aabbtree_query_count}
1155 nodesVisited := 0;
1156 nodesDeepVisited := 0;
1157 {$ENDIF}
1159 // start from root node
1160 spush(mRootNodeId);
1162 // while there are still nodes to visit
1163 while (sp > 0) do
1164 begin
1165 // get the next node id to visit
1166 nodeId := spop();
1167 // skip it if it is a nil node
1168 if (nodeId = TTreeNode.NullTreeNode) then continue;
1169 {$IFDEF aabbtree_query_count}Inc(nodesVisited);{$ENDIF}
1170 // get the corresponding node
1171 node := @mNodes[nodeId];
1172 // should we investigate this node?
1173 if (checker(node)) then
1174 begin
1175 // if the node is a leaf
1176 if (node.leaf) then
1177 begin
1178 // call visitor on it
1179 {$IFDEF aabbtree_query_count}Inc(nodesDeepVisited);{$ENDIF}
1180 if assigned(visitor) then
1181 begin
1182 if (visitor(node.flesh)) then begin result := nodeId; exit; end;
1183 end;
1184 end
1185 else
1186 begin
1187 // if the node is not a leaf, we need to visit its children
1188 spush(node.children[TTreeNode.Left]);
1189 spush(node.children[TTreeNode.Right]);
1190 end;
1191 end;
1192 end;
1194 result := -1; // oops
1195 finally
1196 bigstack := nil;
1197 end;
1198 end;
1201 // add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds
1202 // extra AABB Gap used to allow the collision shape to move a little bit without triggering a large modification of the tree which can be costly
1203 constructor TDynAABBTree.Create (extraAABBGap: Float=0.0);
1204 begin
1205 mExtraGap := extraAABBGap;
1206 setup();
1207 end;
1210 destructor TDynAABBTree.Destroy ();
1211 begin
1212 mNodes := nil;
1213 inherited;
1214 end;
1217 // clear all the nodes and reset the tree
1218 procedure TDynAABBTree.reset ();
1219 begin
1220 mNodes := nil;
1221 setup();
1222 end;
1225 function TDynAABBTree.computeTreeHeight (): Integer; begin result := computeHeight(mRootNodeId); end;
1228 // return the root AABB of the tree
1229 procedure TDynAABBTree.getRootAABB (var aabb: AABB2D);
1230 begin
1231 {$IFDEF aabbtree_many_asserts}assert((mRootNodeId >= 0) and (mRootNodeId < mNodeCount));{$ENDIF}
1232 aabb := mNodes[mRootNodeId].aabb;
1233 end;
1236 // does the given id represents a valid object?
1237 // WARNING: ids of removed objects can be reused on later insertions!
1238 function TDynAABBTree.isValidId (id: Integer): Boolean;
1239 begin
1240 result := (id >= 0) and (id < mNodeCount) and (mNodes[id].leaf);
1241 end;
1244 // get object by nodeid; can return nil for invalid ids
1245 function TDynAABBTree.getNodeObjectId (nodeid: Integer): TTreeFlesh;
1246 begin
1247 if (nodeid >= 0) and (nodeid < mNodeCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := nil;
1248 end;
1250 // get fat object AABB by nodeid; returns random shit for invalid ids
1251 procedure TDynAABBTree.getNodeFatAABB (var aabb: AABB2D; nodeid: Integer);
1252 begin
1253 if (nodeid >= 0) and (nodeid < mNodeCount) and (not mNodes[nodeid].free) then aabb := mNodes[nodeid].aabb else aabb.setX0Y0X1Y1(0, 0, -1, -1);
1254 end;
1257 // insert an object into the tree
1258 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
1259 // AABB for static object will not be "fat" (simple optimization)
1260 // WARNING! inserting the same object several times *WILL* break everything!
1261 function TDynAABBTree.insertObject (flesh: TTreeFlesh; staticObject: Boolean=false): Integer;
1262 var
1263 aabb: AABB2D;
1264 nodeId: Integer;
1265 begin
1266 if not getFleshAABB(aabb, flesh) then begin result := -1; exit; end;
1267 nodeId := insertObjectInternal(aabb, staticObject);
1268 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
1269 mNodes[nodeId].flesh := flesh;
1270 result := nodeId;
1271 end;
1274 // remove an object from the tree
1275 // WARNING: ids of removed objects can be reused on later insertions!
1276 procedure TDynAABBTree.removeObject (nodeId: Integer);
1277 begin
1278 if (nodeId < 0) or (nodeId >= mNodeCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree');
1279 // remove the node from the tree
1280 removeLeafNode(nodeId);
1281 releaseNode(nodeId);
1282 end;
1285 function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: Float; forceReinsert: Boolean=false): Boolean;
1286 var
1287 newAABB: AABB2D;
1288 begin
1289 if (nodeId < 0) or (nodeId >= mNodeCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree');
1291 if not getFleshAABB(newAABB, mNodes[nodeId].flesh) then raise Exception.Create('invalid node id in TDynAABBTree');
1293 // if the new AABB is still inside the fat AABB of the node
1294 if (not forceReinsert) and (mNodes[nodeId].aabb.contains(newAABB)) then begin result := false; exit; end;
1296 // if the new AABB is outside the fat AABB, we remove the corresponding node
1297 removeLeafNode(nodeId);
1299 // compute the fat AABB by inflating the AABB with a constant gap
1300 mNodes[nodeId].aabb := newAABB;
1301 if not forceReinsert and ((dispX <> 0) or (dispY <> 0)) then
1302 begin
1303 mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX-mExtraGap;
1304 mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY-mExtraGap;
1305 mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+mExtraGap;
1306 mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+mExtraGap;
1307 end;
1309 // inflate the fat AABB in direction of the linear motion of the AABB
1310 if (dispX < 0.0) then
1311 begin
1312 mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX+LinearMotionGapMultiplier*dispX;
1313 end
1314 else
1315 begin
1316 mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+LinearMotionGapMultiplier*dispX;
1317 end;
1318 if (dispY < 0.0) then
1319 begin
1320 mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY+LinearMotionGapMultiplier*dispY;
1321 end
1322 else
1323 begin
1324 mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+LinearMotionGapMultiplier*dispY;
1325 end;
1327 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].aabb.contains(newAABB));{$ENDIF}
1329 // reinsert the node into the tree
1330 insertLeafNode(nodeId);
1332 result := true;
1333 end;
1336 // report all shapes overlapping with the AABB given in parameter
1337 procedure TDynAABBTree.aabbQuery (ax, ay, aw, ah: Float; cb: TQueryOverlapCB);
1338 var
1339 caabb: AABB2D;
1340 function checker (node: PTreeNode): Boolean;
1341 begin
1342 result := caabb.overlaps(node.aabb);
1343 end;
1344 begin
1345 if not assigned(cb) then exit;
1346 caabb.setXYWH(ax, ay, aw, ah);
1347 visit(checker, cb);
1348 end;
1351 // report body that contains the given point, or nil
1352 function TDynAABBTree.pointQuery (ax, ay: Float; cb: TQueryOverlapCB): TTreeFlesh;
1353 var
1354 nid: Integer;
1355 function checker (node: PTreeNode): Boolean;
1356 begin
1357 result := node.aabb.contains(ax, ay);
1358 end;
1359 begin
1360 nid := visit(checker, cb);
1361 {$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mNodeCount) and (mNodes[nid].leaf)));{$ENDIF}
1362 if (nid >= 0) then result := mNodes[nid].flesh else result := nil;
1363 end;
1366 // segment querying method
1367 function TDynAABBTree.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: Float; cb: TSegQueryCallback): Boolean;
1368 var
1369 maxFraction: Float = 1.0e100; // infinity
1370 curax, curay: Float;
1371 curbx, curby: Float;
1372 dirx, diry: Float;
1373 invlen: Float;
1375 function checker (node: PTreeNode): Boolean;
1376 begin
1377 result := node.aabb.intersects(curax, curay, curbx, curby);
1378 end;
1380 function visitor (flesh: TTreeFlesh): Boolean;
1381 var
1382 hitFraction: Float;
1383 begin
1384 hitFraction := cb(flesh, curax, curay, curbx, curby);
1385 // if the user returned a hitFraction of zero, it means that the raycasting should stop here
1386 if (hitFraction = 0.0) then
1387 begin
1388 qr.dist := 0;
1389 qr.flesh := flesh;
1390 result := true;
1391 exit;
1392 end;
1393 // if the user returned a positive fraction
1394 if (hitFraction > 0.0) then
1395 begin
1396 // we update the maxFraction value and the ray AABB using the new maximum fraction
1397 if (hitFraction < maxFraction) then
1398 begin
1399 maxFraction := hitFraction;
1400 qr.dist := hitFraction;
1401 qr.flesh := flesh;
1402 // fix curb here
1403 //curb := cura+dir*hitFraction;
1404 curbx := curax+dirx*hitFraction;
1405 curby := curay+diry*hitFraction;
1406 end;
1407 end;
1408 result := false; // continue
1409 end;
1411 begin
1412 qr.reset();
1414 if (ax >= bx) or (ay >= by) then begin result := false; exit; end;
1416 curax := ax;
1417 curay := ay;
1418 curbx := bx;
1419 curby := by;
1421 dirx := (curbx-curax);
1422 diry := (curby-curay);
1423 // normalize
1424 invlen := Float(1.0)/sqrt(dirx*dirx+diry*diry);
1425 dirx *= invlen;
1426 diry *= invlen;
1428 visit(checker, visitor);
1430 result := qr.valid;
1431 end;
1434 end.