1 (* Copyright (C) DooM 2D:Forever Developers
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.
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.
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/>.
16 {$INCLUDE ../shared/a_modes.inc}
17 {.$DEFINE aabbtree_many_asserts}
18 {.$DEFINE aabbtree_query_count}
19 {.$DEFINE aabbtree_use_floats}
27 // ////////////////////////////////////////////////////////////////////////// //
29 {$IFDEF aabbtree_use_floats}Float
= Single;{$ELSE}Float
= Integer;{$ENDIF}
35 // ////////////////////////////////////////////////////////////////////////// //
43 constructor Create (ax
, ay
: Single; aangle
: Single); overload
;
44 constructor Create (ax0
, ay0
, ax1
, ay1
: Single); overload
;
45 constructor Create (const aray
: Ray2D
); overload
;
47 procedure copyFrom (const aray
: Ray2D
); inline;
49 procedure normalizeDir (); inline;
51 procedure setXYAngle (ax
, ay
: Single; aangle
: Single); inline;
52 procedure setX0Y0X1Y1 (ax0
, ay0
, ax1
, ay1
: Single); inline;
55 // ////////////////////////////////////////////////////////////////////////// //
59 minX
, minY
, maxX
, maxY
: Float
;
62 function getvalid (): Boolean; inline;
63 function getcenterX (): Float
; inline;
64 function getcenterY (): Float
; inline;
65 function getextentX (): Float
; inline;
66 function getextentY (): Float
; inline;
69 constructor Create (x0
, y0
, x1
, y1
: Float
); overload
;
70 constructor Create (const aabb
: AABB2D
); overload
;
71 constructor Create (const aabb0
, aabb1
: AABB2D
); overload
;
73 procedure copyFrom (const aabb
: AABB2D
); inline;
74 procedure setDims (x0
, y0
, x1
, y1
: Float
); inline;
76 procedure setMergeTwo (const aabb0
, aabb1
: AABB2D
); inline;
78 function volume (): Float
; inline;
80 procedure merge (const aabb
: AABB2D
); inline;
82 // return true if the current AABB contains the AABB given in parameter
83 function contains (const aabb
: AABB2D
): Boolean; inline; overload
;
84 function contains (ax
, ay
: Float
): Boolean; inline; overload
;
86 // return true if the current AABB is overlapping with the AABB in parameter
87 // two AABBs overlap if they overlap in the two axes at the same time
88 function overlaps (const aabb
: AABB2D
): Boolean; inline; overload
;
90 // ray direction must be normalized
91 function intersects (const ray
: Ray2D
; tmino
: PSingle=nil; tmaxo
: PSingle=nil): Boolean; overload
;
92 function intersects (ax
, ay
, bx
, by
: Single): Boolean; inline; overload
;
94 property valid
: Boolean read getvalid
;
95 property centerX
: Float read getcenterX
;
96 property centerY
: Float read getcenterY
;
97 property extentX
: Float read getextentX
;
98 property extentY
: Float read getextentY
;
102 // ////////////////////////////////////////////////////////////////////////// //
103 (* Dynamic AABB tree (bounding volume hierarchy)
104 * based on the code from ReactPhysics3D physics library, http://www.reactphysics3d.com
105 * Copyright (c) 2010-2016 Daniel Chappuis
107 * This software is provided 'as-is', without any express or implied warranty.
108 * In no event will the authors be held liable for any damages arising from the
109 * use of this software.
111 * Permission is granted to anyone to use this software for any purpose,
112 * including commercial applications, and to alter it and redistribute it
113 * freely, subject to the following restrictions:
115 * 1. The origin of this software must not be misrepresented; you must not claim
116 * that you wrote the original software. If you use this software in a
117 * product, an acknowledgment in the product documentation would be
118 * appreciated but is not required.
120 * 2. Altered source versions must be plainly marked as such, and must not be
121 * misrepresented as being the original software.
123 * 3. This notice may not be removed or altered from any source distribution.
125 // ////////////////////////////////////////////////////////////////////////// //
127 * This class implements a dynamic AABB tree that is used for broad-phase
128 * collision detection. This data structure is inspired by Nathanael Presson's
129 * dynamic tree implementation in BulletPhysics. The following implementation is
130 * based on the one from Erin Catto in Box2D as described in the book
131 * "Introduction to Game Physics with Box2D" by Ian Parberry.
133 // ////////////////////////////////////////////////////////////////////////// //
134 // Dynamic AABB Tree: can be used to speed up broad phase in various engines
136 TDynAABBTree
= class(TObject
)
139 PTreeNode
= ^TTreeNode
;
142 const NullTreeNode
= -1;
146 // a node is either in the tree (has a parent) or in the free nodes list (has a next node)
148 //nextNodeId: Integer;
149 // a node is either a leaf (has data) or is an internal node (has children)
150 children
: array [0..1] of Integer; // left and right child of the node (children[0] = left child)
151 //TODO: `flesh` can be united with `children`
153 // height of the node in the tree (-1 for free nodes)
155 // fat axis aligned bounding box (AABB) corresponding to the node
157 tag
: Integer; // just a user-defined tag
159 // return true if the node is a leaf of the tree
160 procedure clear (); inline;
161 function leaf (): Boolean; inline;
162 function isfree (): Boolean; inline;
163 property nextNodeId
: Integer read parentId write parentId
;
164 //property flesh: Integer read children[0] write children[0];
167 TVisitCheckerCB
= function (node
: PTreeNode
): Boolean is nested
;
168 //TVisitVisitorCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested;
171 // return `true` to stop
172 type TForEachLeafCB
= function (abody
: TTreeFlesh
; const aabb
: AABB2D
): Boolean is nested
; // WARNING! don't modify AABB here!
175 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
176 // also inflated in direction of the linear motion of the body by mutliplying the
177 // followin constant with the linear velocity and the elapsed time between two frames
178 {$IFDEF aabbtree_use_floats}
179 const LinearMotionGapMultiplier
= 1.7;
181 const LinearMotionGapMultiplier
= 17; // *10
185 mNodes
: array of TTreeNode
; // nodes of the tree
186 mRootNodeId
: Integer; // id of the root node of the tree
187 mFreeNodeId
: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
188 mAllocCount
: Integer; // number of allocated nodes in the tree
189 mNodeCount
: Integer; // number of nodes in the tree
191 // extra AABB Gap used to allow the collision shape to move a little bit
192 // without triggering a large modification of the tree which can be costly
196 // called when a overlapping node has been found during the call to forEachAABBOverlap()
197 // return `true` to stop
198 type TQueryOverlapCB
= function (abody
: TTreeFlesh
; atag
: Integer): Boolean is nested
;
199 type TSegQueryCallback
= function (abody
: TTreeFlesh
; ax
, ay
, bx
, by
: Single): Single is nested
; // return dist from (ax,ay) to abody
201 TSegmentQueryResult
= record
202 dist
: Single; // <0: nothing was hit
205 procedure reset (); inline;
206 function valid (): Boolean; inline;
210 function allocateNode (): Integer;
211 procedure releaseNode (nodeId
: Integer);
212 procedure insertLeafNode (nodeId
: Integer);
213 procedure removeLeafNode (nodeId
: Integer);
214 function balanceSubTreeAtNode (nodeId
: Integer): Integer;
215 function computeHeight (nodeId
: Integer): Integer;
216 function insertObjectInternal (var aabb
: AABB2D
; staticObject
: Boolean): Integer;
218 function visit (checker
: TVisitCheckerCB
; visitor
: TQueryOverlapCB
; tagmask
: Integer=-1): Integer;
221 {$IFDEF aabbtree_query_count}
222 mNodesVisited
, mNodesDeepVisited
: Integer;
226 constructor Create (extraAABBGap
: Float
=0);
227 destructor Destroy (); override;
229 // clear all the nodes and reset the tree
232 function forEachLeaf (dg
: TForEachLeafCB
): Boolean; // WARNING! don't modify AABB/tree here!
233 procedure getRootAABB (var aabb
: AABB2D
);
235 function isValidId (id
: Integer): Boolean; inline;
236 function getNodeObjectId (nodeid
: Integer): TTreeFlesh
; inline;
237 procedure getNodeFatAABB (var aabb
: AABB2D
; nodeid
: Integer); inline;
239 // return `false` for invalid flesh
240 function getFleshAABB (var aabb
: AABB2D
; flesh
: TTreeFlesh
): Boolean; virtual; abstract;
242 // insert an object into the tree
243 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
244 // AABB for static object will not be "fat" (simple optimization)
245 // WARNING! inserting the same object several times *WILL* break everything!
246 function insertObject (flesh
: TTreeFlesh
; tag
: Integer; staticObject
: Boolean=false): Integer;
248 // remove an object from the tree
249 // WARNING: ids of removed objects can be reused on later insertions!
250 procedure removeObject (nodeId
: Integer);
252 (** update the dynamic tree after an object has moved.
254 * if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
255 * otherwise, the corresponding node is removed and reinserted into the tree.
256 * the method returns true if the object has been reinserted into the tree.
257 * the `dispX` and `dispY` parameters are the linear velocity of the AABB multiplied by the elapsed time between two frames.
258 * if the `forceReinsert` parameter is `true`, we force a removal and reinsertion of the node
259 * (this can be useful if the shape AABB has become much smaller than the previous one for instance).
261 * note that you should call this method if body's AABB was modified, even if the body wasn't moved.
263 * if `forceReinsert` = `true` and both `dispX` and `dispY` are zeroes, convert object to "static" (don't extrude AABB).
265 * return `true` if the tree was modified.
267 function updateObject (nodeId
: Integer; dispX
, dispY
: Float
; forceReinsert
: Boolean=false): Boolean;
269 function aabbQuery (ax
, ay
, aw
, ah
: Float
; cb
: TQueryOverlapCB
; tagmask
: Integer=-1): TTreeFlesh
;
270 function pointQuery (ax
, ay
: Float
; cb
: TQueryOverlapCB
): TTreeFlesh
;
271 function segmentQuery (var qr
: TSegmentQueryResult
; ax
, ay
, bx
, by
: Float
; cb
: TSegQueryCallback
): Boolean;
273 function computeTreeHeight (): Integer; // compute the height of the tree
275 property extraGap
: Float read mExtraGap write mExtraGap
;
276 property nodeCount
: Integer read mNodeCount
;
277 property nodeAlloced
: Integer read mAllocCount
;
278 {$IFDEF aabbtree_query_count}
279 property nodesVisited
: Integer read mNodesVisited
;
280 property nodesDeepVisited
: Integer read mNodesDeepVisited
;
282 const nodesVisited
= 0;
283 const nodesDeepVisited
= 0;
294 // ////////////////////////////////////////////////////////////////////////// //
295 function minI (a
, b
: Integer): Integer; inline; begin if (a
< b
) then result
:= a
else result
:= b
; end;
296 function maxI (a
, b
: Integer): Integer; inline; begin if (a
> b
) then result
:= a
else result
:= b
; end;
298 function minF (a
, b
: Float
): Float
; inline; begin if (a
< b
) then result
:= a
else result
:= b
; end;
299 function maxF (a
, b
: Float
): Float
; inline; begin if (a
> b
) then result
:= a
else result
:= b
; end;
302 // ////////////////////////////////////////////////////////////////////////// //
303 constructor Ray2D
.Create (ax
, ay
: Single; aangle
: Single); begin setXYAngle(ax
, ay
, aangle
); end;
304 constructor Ray2D
.Create (ax0
, ay0
, ax1
, ay1
: Single); begin setX0Y0X1Y1(ax0
, ay0
, ax1
, ay1
); end;
305 constructor Ray2D
.Create (const aray
: Ray2D
); overload
; begin copyFrom(aray
); end;
308 procedure Ray2D
.copyFrom (const aray
: Ray2D
); inline;
316 procedure Ray2D
.normalizeDir (); inline;
320 invlen
:= 1.0/sqrt(dirX
*dirX
+dirY
*dirY
);
325 procedure Ray2D
.setXYAngle (ax
, ay
: Single; aangle
: Single); inline;
333 procedure Ray2D
.setX0Y0X1Y1 (ax0
, ay0
, ax1
, ay1
: Single); inline;
343 // ////////////////////////////////////////////////////////////////////////// //
344 constructor AABB2D
.Create (x0
, y0
, x1
, y1
: Float
); overload
;
346 setDims(x0
, y0
, x1
, y1
);
349 constructor AABB2D
.Create (const aabb
: AABB2D
); overload
;
354 constructor AABB2D
.Create (const aabb0
, aabb1
: AABB2D
); overload
;
356 setMergeTwo(aabb0
, aabb1
);
359 function AABB2D
.getvalid (): Boolean; inline; begin result
:= (minX
< maxX
) and (minY
< maxY
); end;
361 {$IFDEF aabbtree_use_floats}
362 function AABB2D
.getcenterX (): Float
; inline; begin result
:= (minX
+maxX
)/2.0; end;
363 function AABB2D
.getcenterY (): Float
; inline; begin result
:= (minY
+maxY
)/2.0; end;
365 function AABB2D
.getcenterX (): Float
; inline; begin result
:= (minX
+maxX
) div 2; end;
366 function AABB2D
.getcenterY (): Float
; inline; begin result
:= (minY
+maxY
) div 2; end;
368 function AABB2D
.getextentX (): Float
; inline; begin result
:= (maxX
-minX
); end;
369 function AABB2D
.getextentY (): Float
; inline; begin result
:= (maxY
-minY
); end;
371 procedure AABB2D
.copyFrom (const aabb
: AABB2D
); inline;
377 {$IF DEFINED(D2F_DEBUG)}
378 if not valid
then raise Exception
.Create('copyFrom: result is fucked');
383 procedure AABB2D
.setDims (x0
, y0
, x1
, y1
: Float
); inline;
385 minX
:= minF(x0
, x1
);
386 minY
:= minF(y0
, y1
);
387 maxX
:= maxF(x0
, x1
);
388 maxY
:= maxF(y0
, y1
);
389 {$IF DEFINED(D2F_DEBUG)}
390 if not valid
then raise Exception
.Create('setDims: result is fucked');
395 procedure AABB2D
.setMergeTwo (const aabb0
, aabb1
: AABB2D
); inline;
397 {$IF DEFINED(D2F_DEBUG)}
398 if not aabb0
.valid
then raise Exception
.Create('setMergeTwo: aabb0 is fucked');
399 if not aabb1
.valid
then raise Exception
.Create('setMergeTwo: aabb0 is fucked');
401 minX
:= minF(aabb0
.minX
, aabb1
.minX
);
402 minY
:= minF(aabb0
.minY
, aabb1
.minY
);
403 maxX
:= maxF(aabb0
.maxX
, aabb1
.maxX
);
404 maxY
:= maxF(aabb0
.maxY
, aabb1
.maxY
);
405 {$IF DEFINED(D2F_DEBUG)}
406 if not valid
then raise Exception
.Create('setMergeTwo: result is fucked');
411 function AABB2D
.volume (): Float
; inline;
413 result
:= (maxX
-minX
)*(maxY
-minY
);
417 procedure AABB2D
.merge (const aabb
: AABB2D
); inline;
419 {$IF DEFINED(D2F_DEBUG)}
420 if not aabb
.valid
then raise Exception
.Create('merge: aabb is fucked');
422 minX
:= minF(minX
, aabb
.minX
);
423 minY
:= minF(minY
, aabb
.minY
);
424 maxX
:= maxF(maxX
, aabb
.maxX
);
425 maxY
:= maxF(maxY
, aabb
.maxY
);
426 {$IF DEFINED(D2F_DEBUG)}
427 if not valid
then raise Exception
.Create('setMergeTwo: result is fucked');
432 function AABB2D
.contains (const aabb
: AABB2D
): Boolean; inline; overload
;
435 (aabb
.minX
>= minX
) and (aabb
.minY
>= minY
) and
436 (aabb
.maxX
<= maxX
) and (aabb
.maxY
<= maxY
);
440 function AABB2D
.contains (ax
, ay
: Float
): Boolean; inline; overload
;
442 result
:= (ax
>= minX
) and (ay
>= minY
) and (ax
<= maxX
) and (ay
<= maxY
);
446 function AABB2D
.overlaps (const aabb
: AABB2D
): Boolean; inline; overload
;
449 // exit with no intersection if found separated along any axis
450 if (maxX
< aabb
.minX
) or (minX
> aabb
.maxX
) then exit
;
451 if (maxY
< aabb
.minY
) or (minY
> aabb
.maxY
) then exit
;
456 // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box
457 // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
458 function AABB2D
.intersects (const ray
: Ray2D
; tmino
: PSingle=nil; tmaxo
: PSingle=nil): Boolean; overload
;
460 dinv
, t1
, t2
, tmp
: Single;
467 if (ray
.dirX
<> 0.0) then
469 dinv
:= 1.0/ray
.dirX
;
470 t1
:= (minX
-ray
.origX
)*dinv
;
471 t2
:= (maxX
-ray
.origX
)*dinv
;
472 if (t1
< t2
) then tmin
:= t1
else tmin
:= t2
;
473 if (t1
> t2
) then tmax
:= t1
else tmax
:= t2
;
476 if (ray
.dirY
<> 0.0) then
478 dinv
:= 1.0/ray
.dirY
;
479 t1
:= (minY
-ray
.origY
)*dinv
;
480 t2
:= (maxY
-ray
.origY
)*dinv
;
482 if (t1
< t2
) then tmp
:= t1
else tmp
:= t2
; // min(t1, t2)
483 if (tmax
< tmp
) then tmp
:= tmax
; // min(tmax, tmp)
484 if (tmin
> tmp
) then tmin
:= tmp
; // max(tmin, tmp)
486 if (t1
> t2
) then tmp
:= t1
else tmp
:= t2
; // max(t1, t2)
487 if (tmin
> tmp
) then tmp
:= tmin
; // max(tmin, tmp)
488 if (tmax
< tmp
) then tmax
:= tmp
; // min(tmax, tmp)
490 if (tmin
> 0) then tmp
:= tmin
else tmp
:= 0;
493 if (tmino
<> nil) then tmino
^ := tmin
;
494 if (tmaxo
<> nil) then tmaxo
^ := tmax
;
503 function AABB2D
.intersects (ax
, ay
, bx
, by
: Single): Boolean; inline; overload
;
509 // it may be faster to first check if start or end point is inside AABB (this is sometimes enough for dyntree)
510 if (ax
>= minX
) and (ay
>= minY
) and (ax
<= maxX
) and (ay
<= maxY
) then exit
; // a
511 if (bx
>= minX
) and (by
>= minY
) and (bx
<= maxX
) and (by
<= maxY
) then exit
; // b
512 // nope, do it hard way
513 ray
:= Ray2D
.Create(ax
, ay
, bx
, by
);
514 if not intersects(ray
, @tmin
) then begin result
:= false; exit
; end;
515 if (tmin
< 0) then exit
; // inside, just in case
518 result
:= (tmin
*tmin
<= bx
*bx
+by
*by
);
522 // ////////////////////////////////////////////////////////////////////////// //
523 procedure TDynAABBTree
.TSegmentQueryResult
.reset (); inline; begin dist
:= -1; flesh
:= nil; end;
524 function TDynAABBTree
.TSegmentQueryResult
.valid (): Boolean; inline; begin result
:= (dist
>= 0) and (flesh
<> nil); end;
527 // ////////////////////////////////////////////////////////////////////////// //
528 function TDynAABBTree
.TTreeNode
.leaf (): Boolean; inline; begin result
:= (height
= 0); end;
529 function TDynAABBTree
.TTreeNode
.isfree (): Boolean; inline; begin result
:= (height
= -1); end;
531 procedure TDynAABBTree
.TTreeNode
.clear (); inline;
546 // ////////////////////////////////////////////////////////////////////////// //
547 // allocate and return a node to use in the tree
548 function TDynAABBTree
.allocateNode (): Integer;
550 i
, newsz
, freeNodeId
: Integer;
553 // if there is no more allocated node to use
554 if (mFreeNodeId
= TTreeNode
.NullTreeNode
) then
556 {$IFDEF aabbtree_many_asserts}assert(mNodeCount
= mAllocCount
);{$ENDIF}
557 // allocate more nodes in the tree
558 if (mAllocCount
< 32768) then newsz
:= mAllocCount
*2 else newsz
:= mAllocCount
+16384;
559 SetLength(mNodes
, newsz
);
560 mAllocCount
:= newsz
;
561 // initialize the allocated nodes
562 for i
:= mNodeCount
to mAllocCount
-1 do
564 mNodes
[i
].nextNodeId
:= i
+1;
565 mNodes
[i
].height
:= -1;
567 mNodes
[mAllocCount
-1].nextNodeId
:= TTreeNode
.NullTreeNode
;
568 mFreeNodeId
:= mNodeCount
;
570 // get the next free node
571 freeNodeId
:= mFreeNodeId
;
572 {$IFDEF aabbtree_many_asserts}assert((freeNodeId
>= mNodeCount
) and (freeNodeId
< mAllocCount
));{$ENDIF}
573 node
:= @mNodes
[freeNodeId
];
574 mFreeNodeId
:= node
.nextNodeId
;
576 node
.parentId
:= TTreeNode
.NullTreeNode
;
579 result
:= freeNodeId
;
584 procedure TDynAABBTree
.releaseNode (nodeId
: Integer);
586 {$IFDEF aabbtree_many_asserts}assert(mNodeCount
> 0);{$ENDIF}
587 {$IFDEF aabbtree_many_asserts}assert((nodeId
>= 0) and (nodeId
< mAllocCount
));{$ENDIF}
588 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].height
>= 0);{$ENDIF}
589 mNodes
[nodeId
].nextNodeId
:= mFreeNodeId
;
590 mNodes
[nodeId
].height
:= -1;
591 mNodes
[nodeId
].flesh
:= nil;
592 mFreeNodeId
:= nodeId
;
597 // insert a leaf node in the tree
598 // 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
599 procedure TDynAABBTree
.insertLeafNode (nodeId
: Integer);
601 newNodeAABB
, mergedAABBs
, currentAndLeftAABB
, currentAndRightAABB
: AABB2D
;
602 currentNodeId
: Integer;
603 leftChild
, rightChild
, siblingNode
: Integer;
604 oldParentNode
, newParentNode
: Integer;
605 volumeAABB
, mergedVolume
: Float
;
606 costS
, costI
, costLeft
, costRight
: Float
;
608 // if the tree is empty
609 if (mRootNodeId
= TTreeNode
.NullTreeNode
) then
611 mRootNodeId
:= nodeId
;
612 mNodes
[mRootNodeId
].parentId
:= TTreeNode
.NullTreeNode
;
616 {$IFDEF aabbtree_many_asserts}assert(mRootNodeId
<> TTreeNode
.NullTreeNode
);{$ENDIF}
618 // find the best sibling node for the new node
619 newNodeAABB
:= AABB2D
.Create(mNodes
[nodeId
].aabb
);
620 currentNodeId
:= mRootNodeId
;
621 while not mNodes
[currentNodeId
].leaf
do
623 leftChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Left
];
624 rightChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Right
];
626 // compute the merged AABB
627 volumeAABB
:= mNodes
[currentNodeId
].aabb
.volume
;
628 mergedAABBs
:= AABB2D
.Create(mNodes
[currentNodeId
].aabb
, newNodeAABB
);
629 mergedVolume
:= mergedAABBs
.volume
;
631 // compute the cost of making the current node the sibling of the new node
632 costS
:= 2*mergedVolume
;
634 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
635 costI
:= 2*(mergedVolume
-volumeAABB
);
637 // compute the cost of descending into the left child
638 currentAndLeftAABB
:= AABB2D
.Create(newNodeAABB
, mNodes
[leftChild
].aabb
);
639 costLeft
:= currentAndLeftAABB
.volume
+costI
;
640 if not mNodes
[leftChild
].leaf
then costLeft
-= mNodes
[leftChild
].aabb
.volume
;
642 // compute the cost of descending into the right child
643 currentAndRightAABB
:= AABB2D
.Create(newNodeAABB
, mNodes
[rightChild
].aabb
);
644 costRight
:= currentAndRightAABB
.volume
+costI
;
645 if not mNodes
[rightChild
].leaf
then costRight
-= mNodes
[rightChild
].aabb
.volume
;
647 // 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
648 if (costS
< costLeft
) and (costS
< costRight
) then break
;
650 // it is cheaper to go down into a child of the current node, choose the best child
651 //currentNodeId = (costLeft < costRight ? leftChild : rightChild);
652 if (costLeft
< costRight
) then currentNodeId
:= leftChild
else currentNodeId
:= rightChild
;
655 siblingNode
:= currentNodeId
;
657 // create a new parent for the new node and the sibling node
658 oldParentNode
:= mNodes
[siblingNode
].parentId
;
659 newParentNode
:= allocateNode();
660 mNodes
[newParentNode
].parentId
:= oldParentNode
;
661 mNodes
[newParentNode
].aabb
.setMergeTwo(mNodes
[siblingNode
].aabb
, newNodeAABB
);
662 mNodes
[newParentNode
].height
:= mNodes
[siblingNode
].height
+1;
663 {$IFDEF aabbtree_many_asserts}assert(mNodes
[newParentNode
].height
> 0);{$ENDIF}
665 // if the sibling node was not the root node
666 if (oldParentNode
<> TTreeNode
.NullTreeNode
) then
668 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[oldParentNode
].leaf
);{$ENDIF}
669 if (mNodes
[oldParentNode
].children
[TTreeNode
.Left
] = siblingNode
) then
671 mNodes
[oldParentNode
].children
[TTreeNode
.Left
] := newParentNode
;
675 mNodes
[oldParentNode
].children
[TTreeNode
.Right
] := newParentNode
;
677 mNodes
[newParentNode
].children
[TTreeNode
.Left
] := siblingNode
;
678 mNodes
[newParentNode
].children
[TTreeNode
.Right
] := nodeId
;
679 mNodes
[siblingNode
].parentId
:= newParentNode
;
680 mNodes
[nodeId
].parentId
:= newParentNode
;
684 // if the sibling node was the root node
685 mNodes
[newParentNode
].children
[TTreeNode
.Left
] := siblingNode
;
686 mNodes
[newParentNode
].children
[TTreeNode
.Right
] := nodeId
;
687 mNodes
[siblingNode
].parentId
:= newParentNode
;
688 mNodes
[nodeId
].parentId
:= newParentNode
;
689 mRootNodeId
:= newParentNode
;
692 // move up in the tree to change the AABBs that have changed
693 currentNodeId
:= mNodes
[nodeId
].parentId
;
694 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[currentNodeId
].leaf
);{$ENDIF}
695 while (currentNodeId
<> TTreeNode
.NullTreeNode
) do
697 // balance the sub-tree of the current node if it is not balanced
698 currentNodeId
:= balanceSubTreeAtNode(currentNodeId
);
699 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
701 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[currentNodeId
].leaf
);{$ENDIF}
702 leftChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Left
];
703 rightChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Right
];
704 {$IFDEF aabbtree_many_asserts}assert(leftChild
<> TTreeNode
.NullTreeNode
);{$ENDIF}
705 {$IFDEF aabbtree_many_asserts}assert(rightChild
<> TTreeNode
.NullTreeNode
);{$ENDIF}
707 // recompute the height of the node in the tree
708 mNodes
[currentNodeId
].height
:= maxI(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
)+1;
709 {$IFDEF aabbtree_many_asserts}assert(mNodes
[currentNodeId
].height
> 0);{$ENDIF}
711 // recompute the AABB of the node
712 mNodes
[currentNodeId
].aabb
.setMergeTwo(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
714 currentNodeId
:= mNodes
[currentNodeId
].parentId
;
717 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
721 // remove a leaf node from the tree
722 procedure TDynAABBTree
.removeLeafNode (nodeId
: Integer);
724 currentNodeId
, parentNodeId
, grandParentNodeId
, siblingNodeId
: Integer;
725 leftChildId
, rightChildId
: Integer;
727 {$IFDEF aabbtree_many_asserts}assert((nodeId
>= 0) and (nodeId
< mAllocCount
));{$ENDIF}
728 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
730 // if we are removing the root node (root node is a leaf in this case)
731 if (mRootNodeId
= nodeId
) then begin mRootNodeId
:= TTreeNode
.NullTreeNode
; exit
; end;
733 parentNodeId
:= mNodes
[nodeId
].parentId
;
734 grandParentNodeId
:= mNodes
[parentNodeId
].parentId
;
736 if (mNodes
[parentNodeId
].children
[TTreeNode
.Left
] = nodeId
) then
738 siblingNodeId
:= mNodes
[parentNodeId
].children
[TTreeNode
.Right
];
742 siblingNodeId
:= mNodes
[parentNodeId
].children
[TTreeNode
.Left
];
745 // if the parent of the node to remove is not the root node
746 if (grandParentNodeId
<> TTreeNode
.NullTreeNode
) then
748 // destroy the parent node
749 if (mNodes
[grandParentNodeId
].children
[TTreeNode
.Left
] = parentNodeId
) then
751 mNodes
[grandParentNodeId
].children
[TTreeNode
.Left
] := siblingNodeId
;
755 {$IFDEF aabbtree_many_asserts}assert(mNodes
[grandParentNodeId
].children
[TTreeNode
.Right
] = parentNodeId
);{$ENDIF}
756 mNodes
[grandParentNodeId
].children
[TTreeNode
.Right
] := siblingNodeId
;
758 mNodes
[siblingNodeId
].parentId
:= grandParentNodeId
;
759 releaseNode(parentNodeId
);
761 // 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
762 currentNodeId
:= grandParentNodeId
;
763 while (currentNodeId
<> TTreeNode
.NullTreeNode
) do
765 // balance the current sub-tree if necessary
766 currentNodeId
:= balanceSubTreeAtNode(currentNodeId
);
768 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[currentNodeId
].leaf
);{$ENDIF}
770 // get the two children of the current node
771 leftChildId
:= mNodes
[currentNodeId
].children
[TTreeNode
.Left
];
772 rightChildId
:= mNodes
[currentNodeId
].children
[TTreeNode
.Right
];
774 // recompute the AABB and the height of the current node
775 mNodes
[currentNodeId
].aabb
.setMergeTwo(mNodes
[leftChildId
].aabb
, mNodes
[rightChildId
].aabb
);
776 mNodes
[currentNodeId
].height
:= maxI(mNodes
[leftChildId
].height
, mNodes
[rightChildId
].height
)+1;
777 {$IFDEF aabbtree_many_asserts}assert(mNodes
[currentNodeId
].height
> 0);{$ENDIF}
779 currentNodeId
:= mNodes
[currentNodeId
].parentId
;
784 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
785 mRootNodeId
:= siblingNodeId
;
786 mNodes
[siblingNodeId
].parentId
:= TTreeNode
.NullTreeNode
;
787 releaseNode(parentNodeId
);
792 // balance the sub-tree of a given node using left or right rotations
793 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
794 // this method returns the new root node id
795 function TDynAABBTree
.balanceSubTreeAtNode (nodeId
: Integer): Integer;
797 nodeA
, nodeB
, nodeC
, nodeF
, nodeG
: PTreeNode
;
798 nodeBId
, nodeCId
, nodeFId
, nodeGId
: Integer;
799 balanceFactor
: Integer;
801 {$IFDEF aabbtree_many_asserts}assert(nodeId
<> TTreeNode
.NullTreeNode
);{$ENDIF}
803 nodeA
:= @mNodes
[nodeId
];
805 // if the node is a leaf or the height of A's sub-tree is less than 2
806 if (nodeA
.leaf
) or (nodeA
.height
< 2) then begin result
:= nodeId
; exit
; end; // do not perform any rotation
808 // get the two children nodes
809 nodeBId
:= nodeA
.children
[TTreeNode
.Left
];
810 nodeCId
:= nodeA
.children
[TTreeNode
.Right
];
811 {$IFDEF aabbtree_many_asserts}assert((nodeBId
>= 0) and (nodeBId
< mAllocCount
));{$ENDIF}
812 {$IFDEF aabbtree_many_asserts}assert((nodeCId
>= 0) and (nodeCId
< mAllocCount
));{$ENDIF}
813 nodeB
:= @mNodes
[nodeBId
];
814 nodeC
:= @mNodes
[nodeCId
];
816 // compute the factor of the left and right sub-trees
817 balanceFactor
:= nodeC
.height
-nodeB
.height
;
819 // if the right node C is 2 higher than left node B
820 if (balanceFactor
> 1) then
822 {$IFDEF aabbtree_many_asserts}assert(not nodeC
.leaf
);{$ENDIF}
824 nodeFId
:= nodeC
.children
[TTreeNode
.Left
];
825 nodeGId
:= nodeC
.children
[TTreeNode
.Right
];
826 {$IFDEF aabbtree_many_asserts}assert((nodeFId
>= 0) and (nodeFId
< mAllocCount
));{$ENDIF}
827 {$IFDEF aabbtree_many_asserts}assert((nodeGId
>= 0) and (nodeGId
< mAllocCount
));{$ENDIF}
828 nodeF
:= @mNodes
[nodeFId
];
829 nodeG
:= @mNodes
[nodeGId
];
831 nodeC
.children
[TTreeNode
.Left
] := nodeId
;
832 nodeC
.parentId
:= nodeA
.parentId
;
833 nodeA
.parentId
:= nodeCId
;
835 if (nodeC
.parentId
<> TTreeNode
.NullTreeNode
) then
837 if (mNodes
[nodeC
.parentId
].children
[TTreeNode
.Left
] = nodeId
) then
839 mNodes
[nodeC
.parentId
].children
[TTreeNode
.Left
] := nodeCId
;
843 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeC
.parentId
].children
[TTreeNode
.Right
] = nodeId
);{$ENDIF}
844 mNodes
[nodeC
.parentId
].children
[TTreeNode
.Right
] := nodeCId
;
849 mRootNodeId
:= nodeCId
;
852 {$IFDEF aabbtree_many_asserts}assert(not nodeC
.leaf
);{$ENDIF}
853 {$IFDEF aabbtree_many_asserts}assert(not nodeA
.leaf
);{$ENDIF}
855 // if the right node C was higher than left node B because of the F node
856 if (nodeF
.height
> nodeG
.height
) then
858 nodeC
.children
[TTreeNode
.Right
] := nodeFId
;
859 nodeA
.children
[TTreeNode
.Right
] := nodeGId
;
860 nodeG
.parentId
:= nodeId
;
862 // recompute the AABB of node A and C
863 nodeA
.aabb
.setMergeTwo(nodeB
.aabb
, nodeG
.aabb
);
864 nodeC
.aabb
.setMergeTwo(nodeA
.aabb
, nodeF
.aabb
);
866 // recompute the height of node A and C
867 nodeA
.height
:= maxI(nodeB
.height
, nodeG
.height
)+1;
868 nodeC
.height
:= maxI(nodeA
.height
, nodeF
.height
)+1;
869 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
870 {$IFDEF aabbtree_many_asserts}assert(nodeC
.height
> 0);{$ENDIF}
874 // if the right node C was higher than left node B because of node G
875 nodeC
.children
[TTreeNode
.Right
] := nodeGId
;
876 nodeA
.children
[TTreeNode
.Right
] := nodeFId
;
877 nodeF
.parentId
:= nodeId
;
879 // recompute the AABB of node A and C
880 nodeA
.aabb
.setMergeTwo(nodeB
.aabb
, nodeF
.aabb
);
881 nodeC
.aabb
.setMergeTwo(nodeA
.aabb
, nodeG
.aabb
);
883 // recompute the height of node A and C
884 nodeA
.height
:= maxI(nodeB
.height
, nodeF
.height
)+1;
885 nodeC
.height
:= maxI(nodeA
.height
, nodeG
.height
)+1;
886 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
887 {$IFDEF aabbtree_many_asserts}assert(nodeC
.height
> 0);{$ENDIF}
890 // return the new root of the sub-tree
895 // if the left node B is 2 higher than right node C
896 if (balanceFactor
< -1) then
898 {$IFDEF aabbtree_many_asserts}assert(not nodeB
.leaf
);{$ENDIF}
900 nodeFId
:= nodeB
.children
[TTreeNode
.Left
];
901 nodeGId
:= nodeB
.children
[TTreeNode
.Right
];
902 {$IFDEF aabbtree_many_asserts}assert((nodeFId
>= 0) and (nodeFId
< mAllocCount
));{$ENDIF}
903 {$IFDEF aabbtree_many_asserts}assert((nodeGId
>= 0) and (nodeGId
< mAllocCount
));{$ENDIF}
904 nodeF
:= @mNodes
[nodeFId
];
905 nodeG
:= @mNodes
[nodeGId
];
907 nodeB
.children
[TTreeNode
.Left
] := nodeId
;
908 nodeB
.parentId
:= nodeA
.parentId
;
909 nodeA
.parentId
:= nodeBId
;
911 if (nodeB
.parentId
<> TTreeNode
.NullTreeNode
) then
913 if (mNodes
[nodeB
.parentId
].children
[TTreeNode
.Left
] = nodeId
) then
915 mNodes
[nodeB
.parentId
].children
[TTreeNode
.Left
] := nodeBId
;
919 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeB
.parentId
].children
[TTreeNode
.Right
] = nodeId
);{$ENDIF}
920 mNodes
[nodeB
.parentId
].children
[TTreeNode
.Right
] := nodeBId
;
925 mRootNodeId
:= nodeBId
;
928 {$IFDEF aabbtree_many_asserts}assert(not nodeB
.leaf
);{$ENDIF}
929 {$IFDEF aabbtree_many_asserts}assert(not nodeA
.leaf
);{$ENDIF}
931 // if the left node B was higher than right node C because of the F node
932 if (nodeF
.height
> nodeG
.height
) then
934 nodeB
.children
[TTreeNode
.Right
] := nodeFId
;
935 nodeA
.children
[TTreeNode
.Left
] := nodeGId
;
936 nodeG
.parentId
:= nodeId
;
938 // recompute the AABB of node A and B
939 nodeA
.aabb
.setMergeTwo(nodeC
.aabb
, nodeG
.aabb
);
940 nodeB
.aabb
.setMergeTwo(nodeA
.aabb
, nodeF
.aabb
);
942 // recompute the height of node A and B
943 nodeA
.height
:= maxI(nodeC
.height
, nodeG
.height
)+1;
944 nodeB
.height
:= maxI(nodeA
.height
, nodeF
.height
)+1;
945 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
946 {$IFDEF aabbtree_many_asserts}assert(nodeB
.height
> 0);{$ENDIF}
950 // if the left node B was higher than right node C because of node G
951 nodeB
.children
[TTreeNode
.Right
] := nodeGId
;
952 nodeA
.children
[TTreeNode
.Left
] := nodeFId
;
953 nodeF
.parentId
:= nodeId
;
955 // recompute the AABB of node A and B
956 nodeA
.aabb
.setMergeTwo(nodeC
.aabb
, nodeF
.aabb
);
957 nodeB
.aabb
.setMergeTwo(nodeA
.aabb
, nodeG
.aabb
);
959 // recompute the height of node A and B
960 nodeA
.height
:= maxI(nodeC
.height
, nodeF
.height
)+1;
961 nodeB
.height
:= maxI(nodeA
.height
, nodeG
.height
)+1;
962 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
963 {$IFDEF aabbtree_many_asserts}assert(nodeB
.height
> 0);{$ENDIF}
966 // return the new root of the sub-tree
971 // if the sub-tree is balanced, return the current root node
976 // compute the height of a given node in the tree
977 function TDynAABBTree
.computeHeight (nodeId
: Integer): Integer;
980 leftHeight
, rightHeight
: Integer;
982 {$IFDEF aabbtree_many_asserts}assert((nodeId
>= 0) and (nodeId
< mAllocCount
));{$ENDIF}
983 node
:= @mNodes
[nodeId
];
985 // if the node is a leaf, its height is zero
986 if (node
.leaf
) then begin result
:= 0; exit
; end;
988 // compute the height of the left and right sub-tree
989 leftHeight
:= computeHeight(node
.children
[TTreeNode
.Left
]);
990 rightHeight
:= computeHeight(node
.children
[TTreeNode
.Right
]);
992 // return the height of the node
993 result
:= 1+maxI(leftHeight
, rightHeight
);
997 // internally add an object into the tree
998 function TDynAABBTree
.insertObjectInternal (var aabb
: AABB2D
; staticObject
: Boolean): Integer;
1002 // get the next available node (or allocate new ones if necessary)
1003 nodeId
:= allocateNode();
1005 // create the fat aabb to use in the tree
1006 mNodes
[nodeId
].aabb
:= AABB2D
.Create(aabb
);
1007 if (not staticObject
) then
1009 mNodes
[nodeId
].aabb
.minX
-= mExtraGap
;
1010 mNodes
[nodeId
].aabb
.minY
-= mExtraGap
;
1011 mNodes
[nodeId
].aabb
.maxX
+= mExtraGap
;
1012 mNodes
[nodeId
].aabb
.maxY
+= mExtraGap
;
1015 // set the height of the node in the tree
1016 mNodes
[nodeId
].height
:= 0;
1018 // insert the new leaf node in the tree
1019 insertLeafNode(nodeId
);
1020 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
1022 {$IFDEF aabbtree_many_asserts}assert(nodeId
>= 0);{$ENDIF}
1024 // return the id of the node
1029 // initialize the tree
1030 procedure TDynAABBTree
.setup ();
1034 mRootNodeId
:= TTreeNode
.NullTreeNode
;
1036 mAllocCount
:= 8192;
1038 SetLength(mNodes
, mAllocCount
);
1039 //memset(mNodes, 0, mAllocCount*TTreeNode.sizeof);
1040 for i
:= 0 to mAllocCount
-1 do mNodes
[i
].clear();
1042 // initialize the allocated nodes
1043 for i
:= 0 to mAllocCount
-1 do
1045 mNodes
[i
].nextNodeId
:= i
+1;
1046 mNodes
[i
].height
:= -1;
1048 mNodes
[mAllocCount
-1].nextNodeId
:= TTreeNode
.NullTreeNode
;
1053 // also, checks if the tree structure is valid (for debugging purpose)
1054 function TDynAABBTree
.forEachLeaf (dg
: TForEachLeafCB
): Boolean;
1055 function forEachNode (nodeId
: Integer): Boolean;
1058 leftChild
, rightChild
, height
: Integer;
1062 if (nodeId
= TTreeNode
.NullTreeNode
) then exit
;
1063 // if it is the root
1064 if (nodeId
= mRootNodeId
) then assert(mNodes
[nodeId
].parentId
= TTreeNode
.NullTreeNode
);
1065 // get the children nodes
1066 pNode
:= @mNodes
[nodeId
];
1067 assert(pNode
.height
>= 0);
1068 if (not pNode
.aabb
.valid
) then
1070 {$IFDEF aabbtree_use_floats}
1071 e_WriteLog(Format('AABB:(%f,%f)-(%f,%f); volume=%f; valid=%d; height=%d; leaf=%d', [pNode
.aabb
.minX
, pNode
.aabb
.minY
, pNode
.aabb
.maxX
, pNode
.aabb
.maxY
, pNode
.aabb
.volume
, Integer(pNode
.aabb
.valid
), pNode
.height
, Integer(pNode
.leaf
)]), MSG_NOTIFY
);
1073 e_WriteLog(Format('AABB:(%d,%d)-(%d,%d); volume=%d; valid=%d; height=%d; leaf=%d', [pNode
.aabb
.minX
, pNode
.aabb
.minY
, pNode
.aabb
.maxX
, pNode
.aabb
.maxY
, pNode
.aabb
.volume
, Integer(pNode
.aabb
.valid
), pNode
.height
, Integer(pNode
.leaf
)]), MSG_NOTIFY
);
1077 getFleshAABB(aabb
, pNode
.flesh
);
1078 {$IFDEF aabbtree_use_floats}
1079 e_WriteLog(Format(' LEAF AABB:(%f,%f)-(%f,%f); valid=%d; volume=%f', [aabb
.minX
, aabb
.minY
, aabb
.maxX
, aabb
.maxY
, Integer(aabb
.valid
), aabb
.volume
]), MSG_NOTIFY
);
1081 e_WriteLog(Format(' LEAF AABB:(%d,%d)-(%d,%d); valid=%d; volume=%d', [aabb
.minX
, aabb
.minY
, aabb
.maxX
, aabb
.maxY
, Integer(aabb
.valid
), aabb
.volume
]), MSG_NOTIFY
);
1085 assert(pNode
.aabb
.valid
);
1086 assert(pNode
.aabb
.volume
> 0);
1087 // if the current node is a leaf
1088 if (pNode
.leaf
) then
1090 assert(pNode
.height
= 0);
1091 if assigned(dg
) then result
:= dg(pNode
.flesh
, pNode
.aabb
);
1095 leftChild
:= pNode
.children
[TTreeNode
.Left
];
1096 rightChild
:= pNode
.children
[TTreeNode
.Right
];
1097 // check that the children node Ids are valid
1098 assert((0 <= leftChild
) and (leftChild
< mAllocCount
));
1099 assert((0 <= rightChild
) and (rightChild
< mAllocCount
));
1100 // check that the children nodes have the correct parent node
1101 assert(mNodes
[leftChild
].parentId
= nodeId
);
1102 assert(mNodes
[rightChild
].parentId
= nodeId
);
1103 // check the height of node
1104 height
:= 1+maxI(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
);
1105 assert(mNodes
[nodeId
].height
= height
);
1106 // check the AABB of the node
1107 aabb
:= AABB2D
.Create(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
1108 assert(aabb
.minX
= mNodes
[nodeId
].aabb
.minX
);
1109 assert(aabb
.minY
= mNodes
[nodeId
].aabb
.minY
);
1110 assert(aabb
.maxX
= mNodes
[nodeId
].aabb
.maxX
);
1111 assert(aabb
.maxY
= mNodes
[nodeId
].aabb
.maxY
);
1112 // recursively check the children nodes
1113 result
:= forEachNode(leftChild
);
1114 if not result
then result
:= forEachNode(rightChild
);
1119 // recursively check each node
1120 result
:= forEachNode(mRootNodeId
);
1124 // return `true` from visitor to stop immediately
1125 // checker should check if this node should be considered to further checking
1126 // returns tree node if visitor says stop or -1
1127 function TDynAABBTree
.visit (checker
: TVisitCheckerCB
; visitor
: TQueryOverlapCB
; tagmask
: Integer=-1): Integer;
1129 stack
: array [0..255] of Integer; // stack with the nodes to visit
1130 bigstack
: array of Integer = nil;
1133 procedure spush (id
: Integer); inline;
1137 if (sp
< length(stack
)) then
1139 // use "small stack"
1146 xsp
:= sp
-length(stack
);
1147 if (xsp
< length(bigstack
)) then
1150 bigstack
[xsp
] := id
;
1155 SetLength(bigstack
, length(bigstack
)+1);
1156 bigstack
[high(bigstack
)] := id
;
1163 function spop (): Integer; inline;
1165 {$IFDEF aabbtree_many_asserts}assert(sp > 0);{$ENDIF}
1166 if (sp <= length(stack)) then
1168 // use "small stack"
1170 result := stack[sp];
1176 result := bigstack[sp-length(stack)];
1185 if not assigned(checker
) then begin result
:= -1; exit
; end;
1186 //if not assigned(visitor) then begin result := -1; exit; end;
1188 {$IFDEF aabbtree_query_count}
1190 mNodesDeepVisited
:= 0;
1193 // start from root node
1196 // while there are still nodes to visit
1199 // get the next node id to visit
1201 {$IFDEF aabbtree_many_asserts}assert(sp
> 0);{$ENDIF}
1202 if (sp
<= length(stack
)) then
1204 // use "small stack"
1206 nodeId
:= stack
[sp
];
1212 nodeId
:= bigstack
[sp
-length(stack
)];
1215 // skip it if it is a nil node
1216 if (nodeId
= TTreeNode
.NullTreeNode
) then continue
;
1217 {$IFDEF aabbtree_query_count}Inc(mNodesVisited
);{$ENDIF}
1218 // get the corresponding node
1219 node
:= @mNodes
[nodeId
];
1220 // should we investigate this node?
1221 if (checker(node
)) then
1223 // if the node is a leaf
1226 // call visitor on it
1227 {$IFDEF aabbtree_query_count}Inc(mNodesDeepVisited
);{$ENDIF}
1228 if ((node
.tag
and tagmask
) <> 0) and assigned(visitor
) then
1230 if (visitor(node
.flesh
, node
.tag
)) then begin result
:= nodeId
; exit
; end;
1235 // if the node is not a leaf, we need to visit its children
1236 spush(node
.children
[TTreeNode
.Left
]);
1237 spush(node
.children
[TTreeNode
.Right
]);
1242 result
:= -1; // oops
1249 // add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds
1250 // 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
1251 constructor TDynAABBTree
.Create (extraAABBGap
: Float
=0);
1253 mExtraGap
:= extraAABBGap
;
1258 destructor TDynAABBTree
.Destroy ();
1265 // clear all the nodes and reset the tree
1266 procedure TDynAABBTree
.reset ();
1273 function TDynAABBTree
.computeTreeHeight (): Integer; begin result
:= computeHeight(mRootNodeId
); end;
1276 // return the root AABB of the tree
1277 procedure TDynAABBTree
.getRootAABB (var aabb
: AABB2D
);
1279 {$IFDEF aabbtree_many_asserts}assert((mRootNodeId
>= 0) and (mRootNodeId
< mNodeCount
));{$ENDIF}
1280 aabb
:= mNodes
[mRootNodeId
].aabb
;
1284 // does the given id represents a valid object?
1285 // WARNING: ids of removed objects can be reused on later insertions!
1286 function TDynAABBTree
.isValidId (id
: Integer): Boolean;
1288 result
:= (id
>= 0) and (id
< mNodeCount
) and (mNodes
[id
].leaf
);
1292 // get object by nodeid; can return nil for invalid ids
1293 function TDynAABBTree
.getNodeObjectId (nodeid
: Integer): TTreeFlesh
;
1295 if (nodeid
>= 0) and (nodeid
< mNodeCount
) and (mNodes
[nodeid
].leaf
) then result
:= mNodes
[nodeid
].flesh
else result
:= nil;
1298 // get fat object AABB by nodeid; returns random shit for invalid ids
1299 procedure TDynAABBTree
.getNodeFatAABB (var aabb
: AABB2D
; nodeid
: Integer);
1301 if (nodeid
>= 0) and (nodeid
< mNodeCount
) and (not mNodes
[nodeid
].isfree
) then aabb
.copyFrom(mNodes
[nodeid
].aabb
) else aabb
.setDims(0, 0, 0, 0);
1305 // insert an object into the tree
1306 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
1307 // AABB for static object will not be "fat" (simple optimization)
1308 // WARNING! inserting the same object several times *WILL* break everything!
1309 function TDynAABBTree
.insertObject (flesh
: TTreeFlesh
; tag
: Integer; staticObject
: Boolean=false): Integer;
1314 if not getFleshAABB(aabb
, flesh
) then
1316 {$IFDEF aabbtree_use_floats}
1317 e_WriteLog(Format('trying to insert FUCKED FLESH:(%f,%f)-(%f,%f); volume=%f; valid=%d', [aabb
.minX
, aabb
.minY
, aabb
.maxX
, aabb
.maxY
, aabb
.volume
, Integer(aabb
.valid
)]), MSG_WARNING
);
1319 e_WriteLog(Format('trying to insert FUCKED FLESH:(%d,%d)-(%d,%d); volume=%d; valid=%d', [aabb
.minX
, aabb
.minY
, aabb
.maxX
, aabb
.maxY
, aabb
.volume
, Integer(aabb
.valid
)]), MSG_WARNING
);
1321 //raise Exception.Create('trying to insert invalid flesh in dyntree');
1325 if not aabb
.valid
then
1327 {$IFDEF aabbtree_use_floats}
1328 e_WriteLog(Format('trying to insert FUCKED AABB:(%f,%f)-(%f,%f); volume=%f; valid=%d', [aabb
.minX
, aabb
.minY
, aabb
.maxX
, aabb
.maxY
, aabb
.volume
, Integer(aabb
.valid
)]), MSG_WARNING
);
1330 e_WriteLog(Format('trying to insert FUCKED AABB:(%d,%d)-(%d,%d); volume=%d; valid=%d', [aabb
.minX
, aabb
.minY
, aabb
.maxX
, aabb
.maxY
, aabb
.volume
, Integer(aabb
.valid
)]), MSG_WARNING
);
1332 raise Exception
.Create('trying to insert invalid aabb in dyntree');
1336 //e_WriteLog(Format('inserting AABB:(%f,%f)-(%f,%f); volume=%f; valid=%d', [aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, aabb.volume, Integer(aabb.valid)]), MSG_NOTIFY);
1337 nodeId
:= insertObjectInternal(aabb
, staticObject
);
1338 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
1339 mNodes
[nodeId
].flesh
:= flesh
;
1340 mNodes
[nodeId
].tag
:= tag
;
1345 // remove an object from the tree
1346 // WARNING: ids of removed objects can be reused on later insertions!
1347 procedure TDynAABBTree
.removeObject (nodeId
: Integer);
1349 if (nodeId
< 0) or (nodeId
>= mNodeCount
) or (not mNodes
[nodeId
].leaf
) then raise Exception
.Create('invalid node id in TDynAABBTree');
1350 // remove the node from the tree
1351 removeLeafNode(nodeId
);
1352 releaseNode(nodeId
);
1356 function TDynAABBTree
.updateObject (nodeId
: Integer; dispX
, dispY
: Float
; forceReinsert
: Boolean=false): Boolean;
1360 if (nodeId
< 0) or (nodeId
>= mNodeCount
) or (not mNodes
[nodeId
].leaf
) then raise Exception
.Create('invalid node id in TDynAABBTree.updateObject');
1362 if not getFleshAABB(newAABB
, mNodes
[nodeId
].flesh
) then raise Exception
.Create('invalid node id in TDynAABBTree.updateObject');
1363 if not newAABB
.valid
then raise Exception
.Create('invalid flesh aabb in TDynAABBTree.updateObject');
1365 // if the new AABB is still inside the fat AABB of the node
1366 if (not forceReinsert
) and (mNodes
[nodeId
].aabb
.contains(newAABB
)) then begin result
:= false; exit
; end;
1368 // if the new AABB is outside the fat AABB, we remove the corresponding node
1369 removeLeafNode(nodeId
);
1371 // compute the fat AABB by inflating the AABB with a constant gap
1372 mNodes
[nodeId
].aabb
:= newAABB
;
1373 if (not forceReinsert
) and ((dispX
<> 0) or (dispY
<> 0)) then
1375 mNodes
[nodeId
].aabb
.minX
:= mNodes
[nodeId
].aabb
.minX
-mExtraGap
;
1376 mNodes
[nodeId
].aabb
.minY
:= mNodes
[nodeId
].aabb
.minY
-mExtraGap
;
1377 mNodes
[nodeId
].aabb
.maxX
:= mNodes
[nodeId
].aabb
.maxX
+mExtraGap
;
1378 mNodes
[nodeId
].aabb
.maxY
:= mNodes
[nodeId
].aabb
.maxY
+mExtraGap
;
1381 // inflate the fat AABB in direction of the linear motion of the AABB
1384 mNodes
[nodeId
].aabb
.minX
:= mNodes
[nodeId
].aabb
.minX
+LinearMotionGapMultiplier
*dispX
{$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1388 mNodes
[nodeId
].aabb
.maxX
:= mNodes
[nodeId
].aabb
.maxX
+LinearMotionGapMultiplier
*dispX
{$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1392 mNodes
[nodeId
].aabb
.minY
:= mNodes
[nodeId
].aabb
.minY
+LinearMotionGapMultiplier
*dispY
{$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1396 mNodes
[nodeId
].aabb
.maxY
:= mNodes
[nodeId
].aabb
.maxY
+LinearMotionGapMultiplier
*dispY
{$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1399 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].aabb
.contains(newAABB
));{$ENDIF}
1401 // reinsert the node into the tree
1402 insertLeafNode(nodeId
);
1408 // report all shapes overlapping with the AABB given in parameter
1409 function TDynAABBTree
.aabbQuery (ax
, ay
, aw
, ah
: Float
; cb
: TQueryOverlapCB
; tagmask
: Integer=-1): TTreeFlesh
;
1412 function checker (node
: PTreeNode
): Boolean;
1414 result
:= caabb
.overlaps(node
.aabb
);
1420 if not assigned(cb
) then exit
;
1421 if (aw
< 1) or (ah
< 1) then exit
;
1422 caabb
:= AABB2D
.Create(ax
, ay
, ax
+aw
, ay
+ah
);
1423 nid
:= visit(checker
, cb
, tagmask
);
1424 if (nid
>= 0) then result
:= mNodes
[nid
].flesh
else result
:= nil;
1428 // report body that contains the given point, or nil
1429 function TDynAABBTree
.pointQuery (ax
, ay
: Float
; cb
: TQueryOverlapCB
): TTreeFlesh
;
1430 function checker (node
: PTreeNode
): Boolean;
1432 result
:= node
.aabb
.contains(ax
, ay
);
1437 nid
:= visit(checker
, cb
);
1438 {$IFDEF aabbtree_many_asserts}assert((nid
< 0) or ((nid
>= 0) and (nid
< mNodeCount
) and (mNodes
[nid
].leaf
)));{$ENDIF}
1439 if (nid
>= 0) then result
:= mNodes
[nid
].flesh
else result
:= nil;
1443 // segment querying method
1444 function TDynAABBTree
.segmentQuery (var qr
: TSegmentQueryResult
; ax
, ay
, bx
, by
: Float
; cb
: TSegQueryCallback
): Boolean;
1446 maxFraction
: Single = 1.0e100
; // infinity
1447 curax
, curay
: Single;
1448 curbx
, curby
: Single;
1452 function checker (node
: PTreeNode
): Boolean;
1454 result
:= node
.aabb
.intersects(curax
, curay
, curbx
, curby
);
1457 function visitor (flesh
: TTreeFlesh
; tag
: Integer): Boolean;
1459 hitFraction
: Single;
1461 hitFraction
:= cb(flesh
, curax
, curay
, curbx
, curby
);
1462 // if the user returned a hitFraction of zero, it means that the raycasting should stop here
1463 if (hitFraction
= 0.0) then
1470 // if the user returned a positive fraction
1471 if (hitFraction
> 0.0) then
1473 // we update the maxFraction value and the ray AABB using the new maximum fraction
1474 if (hitFraction
< maxFraction
) then
1476 maxFraction
:= hitFraction
;
1477 qr
.dist
:= hitFraction
;
1480 //curb := cura+dir*hitFraction;
1481 curbx
:= curax
+dirx
*hitFraction
;
1482 curby
:= curay
+diry
*hitFraction
;
1485 result
:= false; // continue
1491 if (ax
>= bx
) or (ay
>= by
) then begin result
:= false; exit
; end;
1498 dirx
:= (curbx
-curax
);
1499 diry
:= (curby
-curay
);
1501 invlen
:= 1.0/sqrt(dirx
*dirx
+diry
*diry
);
1505 visit(checker
, visitor
);