DEADSOFTWARE

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