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}
26 // ////////////////////////////////////////////////////////////////////////// //
34 // ////////////////////////////////////////////////////////////////////////// //
42 constructor Create (ax
, ay
: Float
; aangle
: Float
); overload
;
43 constructor Create (ax0
, ay0
, ax1
, ay1
: Float
); overload
;
44 constructor Create (const aray
: Ray2D
); overload
;
46 procedure copyFrom (const aray
: Ray2D
); inline;
48 procedure normalizeDir (); inline;
50 procedure setXYAngle (ax
, ay
: Float
; aangle
: Float
); inline;
51 procedure setX0Y0X1Y1 (ax0
, ay0
, ax1
, ay1
: Float
); inline;
54 // ////////////////////////////////////////////////////////////////////////// //
58 minX
, minY
, maxX
, maxY
: Float
;
61 function getvalid (): Boolean; inline;
62 function getcenterX (): Float
; inline;
63 function getcenterY (): Float
; inline;
64 function getextentX (): Float
; inline;
65 function getextentY (): Float
; inline;
68 constructor Create (x0
, y0
, x1
, y1
: Float
); overload
;
69 constructor Create (const aabb
: AABB2D
); overload
;
70 constructor Create (const aabb0
, aabb1
: AABB2D
); overload
;
72 procedure copyFrom (const aabb
: AABB2D
); inline;
73 procedure setDims (x0
, y0
, x1
, y1
: Float
); inline;
75 procedure setMergeTwo (const aabb0
, aabb1
: AABB2D
); inline;
77 function volume (): Float
; inline;
79 procedure merge (const aabb
: AABB2D
); inline;
81 // return true if the current AABB contains the AABB given in parameter
82 function contains (const aabb
: AABB2D
): Boolean; inline; overload
;
83 function contains (ax
, ay
: Float
): Boolean; inline; overload
;
85 // return true if the current AABB is overlapping with the AABB in parameter
86 // two AABBs overlap if they overlap in the two axes at the same time
87 function overlaps (const aabb
: AABB2D
): Boolean; inline; overload
;
89 // ray direction must be normalized
90 function intersects (const ray
: Ray2D
; tmino
: PFloat
=nil; tmaxo
: PFloat
=nil): Boolean; overload
;
91 function intersects (ax
, ay
, bx
, by
: Float
): Boolean; inline; overload
;
93 property valid
: Boolean read getvalid
;
94 property centerX
: Float read getcenterX
;
95 property centerY
: Float read getcenterY
;
96 property extentX
: Float read getextentX
;
97 property extentY
: Float read getextentY
;
101 // ////////////////////////////////////////////////////////////////////////// //
102 (* Dynamic AABB tree (bounding volume hierarchy)
103 * based on the code from ReactPhysics3D physics library, http://www.reactphysics3d.com
104 * Copyright (c) 2010-2016 Daniel Chappuis
106 * This software is provided 'as-is', without any express or implied warranty.
107 * In no event will the authors be held liable for any damages arising from the
108 * use of this software.
110 * Permission is granted to anyone to use this software for any purpose,
111 * including commercial applications, and to alter it and redistribute it
112 * freely, subject to the following restrictions:
114 * 1. The origin of this software must not be misrepresented; you must not claim
115 * that you wrote the original software. If you use this software in a
116 * product, an acknowledgment in the product documentation would be
117 * appreciated but is not required.
119 * 2. Altered source versions must be plainly marked as such, and must not be
120 * misrepresented as being the original software.
122 * 3. This notice may not be removed or altered from any source distribution.
124 // ////////////////////////////////////////////////////////////////////////// //
126 * This class implements a dynamic AABB tree that is used for broad-phase
127 * collision detection. This data structure is inspired by Nathanael Presson's
128 * dynamic tree implementation in BulletPhysics. The following implementation is
129 * based on the one from Erin Catto in Box2D as described in the book
130 * "Introduction to Game Physics with Box2D" by Ian Parberry.
132 // ////////////////////////////////////////////////////////////////////////// //
133 // Dynamic AABB Tree: can be used to speed up broad phase in various engines
135 TDynAABBTree
= class(TObject
)
138 PTreeNode
= ^TTreeNode
;
141 const NullTreeNode
= -1;
145 // a node is either in the tree (has a parent) or in the free nodes list (has a next node)
147 //nextNodeId: Integer;
148 // a node is either a leaf (has data) or is an internal node (has children)
149 children
: array [0..1] of Integer; // left and right child of the node (children[0] = left child)
150 //TODO: `flesh` can be united with `children`
152 // height of the node in the tree (-1 for free nodes)
154 // fat axis aligned bounding box (AABB) corresponding to the node
156 tag
: Integer; // just a user-defined tag
158 // return true if the node is a leaf of the tree
159 procedure clear (); inline;
160 function leaf (): Boolean; inline;
161 function isfree (): Boolean; inline;
162 property nextNodeId
: Integer read parentId write parentId
;
163 //property flesh: Integer read children[0] write children[0];
166 TVisitCheckerCB
= function (node
: PTreeNode
): Boolean is nested
;
167 //TVisitVisitorCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested;
170 // return `true` to stop
171 type TForEachLeafCB
= function (abody
: TTreeFlesh
; const aabb
: AABB2D
): Boolean is nested
; // WARNING! don't modify AABB here!
174 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
175 // also inflated in direction of the linear motion of the body by mutliplying the
176 // followin constant with the linear velocity and the elapsed time between two frames
177 const LinearMotionGapMultiplier
= 1.7;
180 mNodes
: array of TTreeNode
; // nodes of the tree
181 mRootNodeId
: Integer; // id of the root node of the tree
182 mFreeNodeId
: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
183 mAllocCount
: Integer; // number of allocated nodes in the tree
184 mNodeCount
: Integer; // number of nodes in the tree
186 // extra AABB Gap used to allow the collision shape to move a little bit
187 // without triggering a large modification of the tree which can be costly
191 // called when a overlapping node has been found during the call to forEachAABBOverlap()
192 // return `true` to stop
193 type TQueryOverlapCB
= function (abody
: TTreeFlesh
; atag
: Integer): Boolean is nested
;
194 type TSegQueryCallback
= function (abody
: TTreeFlesh
; ax
, ay
, bx
, by
: Float
): Float
is nested
; // return dist from (ax,ay) to abody
196 TSegmentQueryResult
= record
197 dist
: Float
; // <0: nothing was hit
200 procedure reset (); inline;
201 function valid (): Boolean; inline;
205 function allocateNode (): Integer;
206 procedure releaseNode (nodeId
: Integer);
207 procedure insertLeafNode (nodeId
: Integer);
208 procedure removeLeafNode (nodeId
: Integer);
209 function balanceSubTreeAtNode (nodeId
: Integer): Integer;
210 function computeHeight (nodeId
: Integer): Integer;
211 function insertObjectInternal (var aabb
: AABB2D
; staticObject
: Boolean): Integer;
213 function visit (checker
: TVisitCheckerCB
; visitor
: TQueryOverlapCB
; tagmask
: Integer=-1): Integer;
216 {$IFDEF aabbtree_query_count}
217 mNodesVisited
, mNodesDeepVisited
: Integer;
221 constructor Create (extraAABBGap
: Float
=0.0);
222 destructor Destroy (); override;
224 // clear all the nodes and reset the tree
227 function forEachLeaf (dg
: TForEachLeafCB
): Boolean; // WARNING! don't modify AABB/tree here!
228 procedure getRootAABB (var aabb
: AABB2D
);
230 function isValidId (id
: Integer): Boolean; inline;
231 function getNodeObjectId (nodeid
: Integer): TTreeFlesh
; inline;
232 procedure getNodeFatAABB (var aabb
: AABB2D
; nodeid
: Integer); inline;
234 // return `false` for invalid flesh
235 function getFleshAABB (var aabb
: AABB2D
; flesh
: TTreeFlesh
): Boolean; virtual; abstract;
237 // insert an object into the tree
238 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
239 // AABB for static object will not be "fat" (simple optimization)
240 // WARNING! inserting the same object several times *WILL* break everything!
241 function insertObject (flesh
: TTreeFlesh
; tag
: Integer; staticObject
: Boolean=false): Integer;
243 // remove an object from the tree
244 // WARNING: ids of removed objects can be reused on later insertions!
245 procedure removeObject (nodeId
: Integer);
247 (** update the dynamic tree after an object has moved.
249 * if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
250 * otherwise, the corresponding node is removed and reinserted into the tree.
251 * the method returns true if the object has been reinserted into the tree.
252 * the `dispX` and `dispY` parameters are the linear velocity of the AABB multiplied by the elapsed time between two frames.
253 * if the `forceReinsert` parameter is `true`, we force a removal and reinsertion of the node
254 * (this can be useful if the shape AABB has become much smaller than the previous one for instance).
256 * note that you should call this method if body's AABB was modified, even if the body wasn't moved.
258 * if `forceReinsert` = `true` and both `dispX` and `dispY` are zeroes, convert object to "static" (don't extrude AABB).
260 * return `true` if the tree was modified.
262 function updateObject (nodeId
: Integer; dispX
, dispY
: Float
; forceReinsert
: Boolean=false): Boolean;
264 function aabbQuery (ax
, ay
, aw
, ah
: Float
; cb
: TQueryOverlapCB
; tagmask
: Integer=-1): TTreeFlesh
;
265 function pointQuery (ax
, ay
: Float
; cb
: TQueryOverlapCB
): TTreeFlesh
;
266 function segmentQuery (var qr
: TSegmentQueryResult
; ax
, ay
, bx
, by
: Float
; cb
: TSegQueryCallback
): Boolean;
268 function computeTreeHeight (): Integer; // compute the height of the tree
270 property extraGap
: Float read mExtraGap write mExtraGap
;
271 property nodeCount
: Integer read mNodeCount
;
272 property nodeAlloced
: Integer read mAllocCount
;
273 {$IFDEF aabbtree_query_count}
274 property nodesVisited
: Integer read mNodesVisited
;
275 property nodesDeepVisited
: Integer read mNodesDeepVisited
;
277 const nodesVisited
= 0;
278 const nodesDeepVisited
= 0;
289 // ////////////////////////////////////////////////////////////////////////// //
290 function minI (a
, b
: Integer): Integer; inline; begin if (a
< b
) then result
:= a
else result
:= b
; end;
291 function maxI (a
, b
: Integer): Integer; inline; begin if (a
> b
) then result
:= a
else result
:= b
; end;
293 function minF (a
, b
: Float
): Float
; inline; begin if (a
< b
) then result
:= a
else result
:= b
; end;
294 function maxF (a
, b
: Float
): Float
; inline; begin if (a
> b
) then result
:= a
else result
:= b
; end;
297 // ////////////////////////////////////////////////////////////////////////// //
298 constructor Ray2D
.Create (ax
, ay
: Float
; aangle
: Float
); begin setXYAngle(ax
, ay
, aangle
); end;
299 constructor Ray2D
.Create (ax0
, ay0
, ax1
, ay1
: Float
); begin setX0Y0X1Y1(ax0
, ay0
, ax1
, ay1
); end;
300 constructor Ray2D
.Create (const aray
: Ray2D
); overload
; begin copyFrom(aray
); end;
303 procedure Ray2D
.copyFrom (const aray
: Ray2D
); inline;
311 procedure Ray2D
.normalizeDir (); inline;
315 invlen
:= 1.0/sqrt(dirX
*dirX
+dirY
*dirY
);
320 procedure Ray2D
.setXYAngle (ax
, ay
: Float
; aangle
: Float
); inline;
328 procedure Ray2D
.setX0Y0X1Y1 (ax0
, ay0
, ax1
, ay1
: Float
); inline;
338 // ////////////////////////////////////////////////////////////////////////// //
339 constructor AABB2D
.Create (x0
, y0
, x1
, y1
: Float
); overload
;
341 setDims(x0
, y0
, x1
, y1
);
344 constructor AABB2D
.Create (const aabb
: AABB2D
); overload
;
349 constructor AABB2D
.Create (const aabb0
, aabb1
: AABB2D
); overload
;
351 setMergeTwo(aabb0
, aabb1
);
354 function AABB2D
.getvalid (): Boolean; inline; begin result
:= (minX
< maxX
) and (minY
< maxY
); end;
356 function AABB2D
.getcenterX (): Float
; inline; begin result
:= (minX
+maxX
)/2.0; end;
357 function AABB2D
.getcenterY (): Float
; inline; begin result
:= (minY
+maxY
)/2.0; end;
358 function AABB2D
.getextentX (): Float
; inline; begin result
:= (maxX
-minX
)+1.0; end;
359 function AABB2D
.getextentY (): Float
; inline; begin result
:= (maxY
-minY
)+1.0; end;
362 procedure AABB2D
.copyFrom (const aabb
: AABB2D
); inline;
368 {$IF DEFINED(D2F_DEBUG)}
369 if not valid
then raise Exception
.Create('copyFrom: result is fucked');
374 procedure AABB2D
.setDims (x0
, y0
, x1
, y1
: Float
); inline;
376 minX
:= minF(x0
, x1
);
377 minY
:= minF(y0
, y1
);
378 maxX
:= maxF(x0
, x1
);
379 maxY
:= maxF(y0
, y1
);
380 {$IF DEFINED(D2F_DEBUG)}
381 if not valid
then raise Exception
.Create('setDims: result is fucked');
386 procedure AABB2D
.setMergeTwo (const aabb0
, aabb1
: AABB2D
); inline;
388 {$IF DEFINED(D2F_DEBUG)}
389 if not aabb0
.valid
then raise Exception
.Create('setMergeTwo: aabb0 is fucked');
390 if not aabb1
.valid
then raise Exception
.Create('setMergeTwo: aabb0 is fucked');
392 minX
:= minF(aabb0
.minX
, aabb1
.minX
);
393 minY
:= minF(aabb0
.minY
, aabb1
.minY
);
394 maxX
:= maxF(aabb0
.maxX
, aabb1
.maxX
);
395 maxY
:= maxF(aabb0
.maxY
, aabb1
.maxY
);
396 {$IF DEFINED(D2F_DEBUG)}
397 if not valid
then raise Exception
.Create('setMergeTwo: result is fucked');
402 function AABB2D
.volume (): Float
; inline;
404 result
:= (maxX
-minX
)*(maxY
-minY
);
408 procedure AABB2D
.merge (const aabb
: AABB2D
); inline;
410 {$IF DEFINED(D2F_DEBUG)}
411 if not aabb
.valid
then raise Exception
.Create('merge: aabb is fucked');
413 minX
:= minF(minX
, aabb
.minX
);
414 minY
:= minF(minY
, aabb
.minY
);
415 maxX
:= maxF(maxX
, aabb
.maxX
);
416 maxY
:= maxF(maxY
, aabb
.maxY
);
417 {$IF DEFINED(D2F_DEBUG)}
418 if not valid
then raise Exception
.Create('setMergeTwo: result is fucked');
423 function AABB2D
.contains (const aabb
: AABB2D
): Boolean; inline; overload
;
426 (aabb
.minX
>= minX
) and (aabb
.minY
>= minY
) and
427 (aabb
.maxX
<= maxX
) and (aabb
.maxY
<= maxY
);
431 function AABB2D
.contains (ax
, ay
: Float
): Boolean; inline; overload
;
433 result
:= (ax
>= minX
) and (ay
>= minY
) and (ax
<= maxX
) and (ay
<= maxY
);
437 function AABB2D
.overlaps (const aabb
: AABB2D
): Boolean; inline; overload
;
440 // exit with no intersection if found separated along any axis
441 if (maxX
< aabb
.minX
) or (minX
> aabb
.maxX
) then exit
;
442 if (maxY
< aabb
.minY
) or (minY
> aabb
.maxY
) then exit
;
447 // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box
448 // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
449 function AABB2D
.intersects (const ray
: Ray2D
; tmino
: PFloat
=nil; tmaxo
: PFloat
=nil): Boolean; overload
;
451 dinv
, t1
, t2
, tmp
: Float
;
458 if (ray
.dirX
<> 0.0) then
460 dinv
:= 1.0/ray
.dirX
;
461 t1
:= (minX
-ray
.origX
)*dinv
;
462 t2
:= (maxX
-ray
.origX
)*dinv
;
463 if (t1
< t2
) then tmin
:= t1
else tmin
:= t2
;
464 if (t1
> t2
) then tmax
:= t1
else tmax
:= t2
;
467 if (ray
.dirY
<> 0.0) then
469 dinv
:= 1.0/ray
.dirY
;
470 t1
:= (minY
-ray
.origY
)*dinv
;
471 t2
:= (maxY
-ray
.origY
)*dinv
;
473 if (t1
< t2
) then tmp
:= t1
else tmp
:= t2
; // min(t1, t2)
474 if (tmax
< tmp
) then tmp
:= tmax
; // min(tmax, tmp)
475 if (tmin
> tmp
) then tmin
:= tmp
; // max(tmin, tmp)
477 if (t1
> t2
) then tmp
:= t1
else tmp
:= t2
; // max(t1, t2)
478 if (tmin
> tmp
) then tmp
:= tmin
; // max(tmin, tmp)
479 if (tmax
< tmp
) then tmax
:= tmp
; // min(tmax, tmp)
481 if (tmin
> 0) then tmp
:= tmin
else tmp
:= 0;
484 if (tmino
<> nil) then tmino
^ := tmin
;
485 if (tmaxo
<> nil) then tmaxo
^ := tmax
;
494 function AABB2D
.intersects (ax
, ay
, bx
, by
: Float
): Boolean; inline; overload
;
500 // it may be faster to first check if start or end point is inside AABB (this is sometimes enough for dyntree)
501 if (ax
>= minX
) and (ay
>= minY
) and (ax
<= maxX
) and (ay
<= maxY
) then exit
; // a
502 if (bx
>= minX
) and (by
>= minY
) and (bx
<= maxX
) and (by
<= maxY
) then exit
; // b
503 // nope, do it hard way
504 ray
:= Ray2D
.Create(ax
, ay
, bx
, by
);
505 if not intersects(ray
, @tmin
) then begin result
:= false; exit
; end;
506 if (tmin
< 0) then exit
; // inside, just in case
509 result
:= (tmin
*tmin
<= bx
*bx
+by
*by
);
513 // ////////////////////////////////////////////////////////////////////////// //
514 procedure TDynAABBTree
.TSegmentQueryResult
.reset (); inline; begin dist
:= -1; flesh
:= nil; end;
515 function TDynAABBTree
.TSegmentQueryResult
.valid (): Boolean; inline; begin result
:= (dist
>= 0) and (flesh
<> nil); end;
518 // ////////////////////////////////////////////////////////////////////////// //
519 function TDynAABBTree
.TTreeNode
.leaf (): Boolean; inline; begin result
:= (height
= 0); end;
520 function TDynAABBTree
.TTreeNode
.isfree (): Boolean; inline; begin result
:= (height
= -1); end;
522 procedure TDynAABBTree
.TTreeNode
.clear (); inline;
537 // ////////////////////////////////////////////////////////////////////////// //
538 // allocate and return a node to use in the tree
539 function TDynAABBTree
.allocateNode (): Integer;
541 i
, newsz
, freeNodeId
: Integer;
544 // if there is no more allocated node to use
545 if (mFreeNodeId
= TTreeNode
.NullTreeNode
) then
547 {$IFDEF aabbtree_many_asserts}assert(mNodeCount
= mAllocCount
);{$ENDIF}
548 // allocate more nodes in the tree
549 if (mAllocCount
< 32768) then newsz
:= mAllocCount
*2 else newsz
:= mAllocCount
+16384;
550 SetLength(mNodes
, newsz
);
551 mAllocCount
:= newsz
;
552 // initialize the allocated nodes
553 for i
:= mNodeCount
to mAllocCount
-1 do
555 mNodes
[i
].nextNodeId
:= i
+1;
556 mNodes
[i
].height
:= -1;
558 mNodes
[mAllocCount
-1].nextNodeId
:= TTreeNode
.NullTreeNode
;
559 mFreeNodeId
:= mNodeCount
;
561 // get the next free node
562 freeNodeId
:= mFreeNodeId
;
563 {$IFDEF aabbtree_many_asserts}assert((freeNodeId
>= mNodeCount
) and (freeNodeId
< mAllocCount
));{$ENDIF}
564 node
:= @mNodes
[freeNodeId
];
565 mFreeNodeId
:= node
.nextNodeId
;
567 node
.parentId
:= TTreeNode
.NullTreeNode
;
570 result
:= freeNodeId
;
575 procedure TDynAABBTree
.releaseNode (nodeId
: Integer);
577 {$IFDEF aabbtree_many_asserts}assert(mNodeCount
> 0);{$ENDIF}
578 {$IFDEF aabbtree_many_asserts}assert((nodeId
>= 0) and (nodeId
< mAllocCount
));{$ENDIF}
579 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].height
>= 0);{$ENDIF}
580 mNodes
[nodeId
].nextNodeId
:= mFreeNodeId
;
581 mNodes
[nodeId
].height
:= -1;
582 mNodes
[nodeId
].flesh
:= nil;
583 mFreeNodeId
:= nodeId
;
588 // insert a leaf node in the tree
589 // 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
590 procedure TDynAABBTree
.insertLeafNode (nodeId
: Integer);
592 newNodeAABB
, mergedAABBs
, currentAndLeftAABB
, currentAndRightAABB
: AABB2D
;
593 currentNodeId
: Integer;
594 leftChild
, rightChild
, siblingNode
: Integer;
595 oldParentNode
, newParentNode
: Integer;
596 volumeAABB
, mergedVolume
: Float
;
597 costS
, costI
, costLeft
, costRight
: Float
;
599 // if the tree is empty
600 if (mRootNodeId
= TTreeNode
.NullTreeNode
) then
602 mRootNodeId
:= nodeId
;
603 mNodes
[mRootNodeId
].parentId
:= TTreeNode
.NullTreeNode
;
607 {$IFDEF aabbtree_many_asserts}assert(mRootNodeId
<> TTreeNode
.NullTreeNode
);{$ENDIF}
609 // find the best sibling node for the new node
610 newNodeAABB
:= AABB2D
.Create(mNodes
[nodeId
].aabb
);
611 currentNodeId
:= mRootNodeId
;
612 while not mNodes
[currentNodeId
].leaf
do
614 leftChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Left
];
615 rightChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Right
];
617 // compute the merged AABB
618 volumeAABB
:= mNodes
[currentNodeId
].aabb
.volume
;
619 mergedAABBs
:= AABB2D
.Create(mNodes
[currentNodeId
].aabb
, newNodeAABB
);
620 mergedVolume
:= mergedAABBs
.volume
;
622 // compute the cost of making the current node the sibling of the new node
623 costS
:= 2.0*mergedVolume
;
625 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
626 costI
:= 2.0*(mergedVolume
-volumeAABB
);
628 // compute the cost of descending into the left child
629 currentAndLeftAABB
:= AABB2D
.Create(newNodeAABB
, mNodes
[leftChild
].aabb
);
630 costLeft
:= currentAndLeftAABB
.volume
+costI
;
631 if not mNodes
[leftChild
].leaf
then costLeft
-= mNodes
[leftChild
].aabb
.volume
;
633 // compute the cost of descending into the right child
634 currentAndRightAABB
:= AABB2D
.Create(newNodeAABB
, mNodes
[rightChild
].aabb
);
635 costRight
:= currentAndRightAABB
.volume
+costI
;
636 if not mNodes
[rightChild
].leaf
then costRight
-= mNodes
[rightChild
].aabb
.volume
;
638 // 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
639 if (costS
< costLeft
) and (costS
< costRight
) then break
;
641 // it is cheaper to go down into a child of the current node, choose the best child
642 //currentNodeId = (costLeft < costRight ? leftChild : rightChild);
643 if (costLeft
< costRight
) then currentNodeId
:= leftChild
else currentNodeId
:= rightChild
;
646 siblingNode
:= currentNodeId
;
648 // create a new parent for the new node and the sibling node
649 oldParentNode
:= mNodes
[siblingNode
].parentId
;
650 newParentNode
:= allocateNode();
651 mNodes
[newParentNode
].parentId
:= oldParentNode
;
652 mNodes
[newParentNode
].aabb
.setMergeTwo(mNodes
[siblingNode
].aabb
, newNodeAABB
);
653 mNodes
[newParentNode
].height
:= mNodes
[siblingNode
].height
+1;
654 {$IFDEF aabbtree_many_asserts}assert(mNodes
[newParentNode
].height
> 0);{$ENDIF}
656 // if the sibling node was not the root node
657 if (oldParentNode
<> TTreeNode
.NullTreeNode
) then
659 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[oldParentNode
].leaf
);{$ENDIF}
660 if (mNodes
[oldParentNode
].children
[TTreeNode
.Left
] = siblingNode
) then
662 mNodes
[oldParentNode
].children
[TTreeNode
.Left
] := newParentNode
;
666 mNodes
[oldParentNode
].children
[TTreeNode
.Right
] := newParentNode
;
668 mNodes
[newParentNode
].children
[TTreeNode
.Left
] := siblingNode
;
669 mNodes
[newParentNode
].children
[TTreeNode
.Right
] := nodeId
;
670 mNodes
[siblingNode
].parentId
:= newParentNode
;
671 mNodes
[nodeId
].parentId
:= newParentNode
;
675 // if the sibling node was the root node
676 mNodes
[newParentNode
].children
[TTreeNode
.Left
] := siblingNode
;
677 mNodes
[newParentNode
].children
[TTreeNode
.Right
] := nodeId
;
678 mNodes
[siblingNode
].parentId
:= newParentNode
;
679 mNodes
[nodeId
].parentId
:= newParentNode
;
680 mRootNodeId
:= newParentNode
;
683 // move up in the tree to change the AABBs that have changed
684 currentNodeId
:= mNodes
[nodeId
].parentId
;
685 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[currentNodeId
].leaf
);{$ENDIF}
686 while (currentNodeId
<> TTreeNode
.NullTreeNode
) do
688 // balance the sub-tree of the current node if it is not balanced
689 currentNodeId
:= balanceSubTreeAtNode(currentNodeId
);
690 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
692 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[currentNodeId
].leaf
);{$ENDIF}
693 leftChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Left
];
694 rightChild
:= mNodes
[currentNodeId
].children
[TTreeNode
.Right
];
695 {$IFDEF aabbtree_many_asserts}assert(leftChild
<> TTreeNode
.NullTreeNode
);{$ENDIF}
696 {$IFDEF aabbtree_many_asserts}assert(rightChild
<> TTreeNode
.NullTreeNode
);{$ENDIF}
698 // recompute the height of the node in the tree
699 mNodes
[currentNodeId
].height
:= maxI(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
)+1;
700 {$IFDEF aabbtree_many_asserts}assert(mNodes
[currentNodeId
].height
> 0);{$ENDIF}
702 // recompute the AABB of the node
703 mNodes
[currentNodeId
].aabb
.setMergeTwo(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
705 currentNodeId
:= mNodes
[currentNodeId
].parentId
;
708 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
712 // remove a leaf node from the tree
713 procedure TDynAABBTree
.removeLeafNode (nodeId
: Integer);
715 currentNodeId
, parentNodeId
, grandParentNodeId
, siblingNodeId
: Integer;
716 leftChildId
, rightChildId
: Integer;
718 {$IFDEF aabbtree_many_asserts}assert((nodeId
>= 0) and (nodeId
< mAllocCount
));{$ENDIF}
719 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
721 // if we are removing the root node (root node is a leaf in this case)
722 if (mRootNodeId
= nodeId
) then begin mRootNodeId
:= TTreeNode
.NullTreeNode
; exit
; end;
724 parentNodeId
:= mNodes
[nodeId
].parentId
;
725 grandParentNodeId
:= mNodes
[parentNodeId
].parentId
;
727 if (mNodes
[parentNodeId
].children
[TTreeNode
.Left
] = nodeId
) then
729 siblingNodeId
:= mNodes
[parentNodeId
].children
[TTreeNode
.Right
];
733 siblingNodeId
:= mNodes
[parentNodeId
].children
[TTreeNode
.Left
];
736 // if the parent of the node to remove is not the root node
737 if (grandParentNodeId
<> TTreeNode
.NullTreeNode
) then
739 // destroy the parent node
740 if (mNodes
[grandParentNodeId
].children
[TTreeNode
.Left
] = parentNodeId
) then
742 mNodes
[grandParentNodeId
].children
[TTreeNode
.Left
] := siblingNodeId
;
746 {$IFDEF aabbtree_many_asserts}assert(mNodes
[grandParentNodeId
].children
[TTreeNode
.Right
] = parentNodeId
);{$ENDIF}
747 mNodes
[grandParentNodeId
].children
[TTreeNode
.Right
] := siblingNodeId
;
749 mNodes
[siblingNodeId
].parentId
:= grandParentNodeId
;
750 releaseNode(parentNodeId
);
752 // 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
753 currentNodeId
:= grandParentNodeId
;
754 while (currentNodeId
<> TTreeNode
.NullTreeNode
) do
756 // balance the current sub-tree if necessary
757 currentNodeId
:= balanceSubTreeAtNode(currentNodeId
);
759 {$IFDEF aabbtree_many_asserts}assert(not mNodes
[currentNodeId
].leaf
);{$ENDIF}
761 // get the two children of the current node
762 leftChildId
:= mNodes
[currentNodeId
].children
[TTreeNode
.Left
];
763 rightChildId
:= mNodes
[currentNodeId
].children
[TTreeNode
.Right
];
765 // recompute the AABB and the height of the current node
766 mNodes
[currentNodeId
].aabb
.setMergeTwo(mNodes
[leftChildId
].aabb
, mNodes
[rightChildId
].aabb
);
767 mNodes
[currentNodeId
].height
:= maxI(mNodes
[leftChildId
].height
, mNodes
[rightChildId
].height
)+1;
768 {$IFDEF aabbtree_many_asserts}assert(mNodes
[currentNodeId
].height
> 0);{$ENDIF}
770 currentNodeId
:= mNodes
[currentNodeId
].parentId
;
775 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
776 mRootNodeId
:= siblingNodeId
;
777 mNodes
[siblingNodeId
].parentId
:= TTreeNode
.NullTreeNode
;
778 releaseNode(parentNodeId
);
783 // balance the sub-tree of a given node using left or right rotations
784 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
785 // this method returns the new root node id
786 function TDynAABBTree
.balanceSubTreeAtNode (nodeId
: Integer): Integer;
788 nodeA
, nodeB
, nodeC
, nodeF
, nodeG
: PTreeNode
;
789 nodeBId
, nodeCId
, nodeFId
, nodeGId
: Integer;
790 balanceFactor
: Integer;
792 {$IFDEF aabbtree_many_asserts}assert(nodeId
<> TTreeNode
.NullTreeNode
);{$ENDIF}
794 nodeA
:= @mNodes
[nodeId
];
796 // if the node is a leaf or the height of A's sub-tree is less than 2
797 if (nodeA
.leaf
) or (nodeA
.height
< 2) then begin result
:= nodeId
; exit
; end; // do not perform any rotation
799 // get the two children nodes
800 nodeBId
:= nodeA
.children
[TTreeNode
.Left
];
801 nodeCId
:= nodeA
.children
[TTreeNode
.Right
];
802 {$IFDEF aabbtree_many_asserts}assert((nodeBId
>= 0) and (nodeBId
< mAllocCount
));{$ENDIF}
803 {$IFDEF aabbtree_many_asserts}assert((nodeCId
>= 0) and (nodeCId
< mAllocCount
));{$ENDIF}
804 nodeB
:= @mNodes
[nodeBId
];
805 nodeC
:= @mNodes
[nodeCId
];
807 // compute the factor of the left and right sub-trees
808 balanceFactor
:= nodeC
.height
-nodeB
.height
;
810 // if the right node C is 2 higher than left node B
811 if (balanceFactor
> 1.0) then
813 {$IFDEF aabbtree_many_asserts}assert(not nodeC
.leaf
);{$ENDIF}
815 nodeFId
:= nodeC
.children
[TTreeNode
.Left
];
816 nodeGId
:= nodeC
.children
[TTreeNode
.Right
];
817 {$IFDEF aabbtree_many_asserts}assert((nodeFId
>= 0) and (nodeFId
< mAllocCount
));{$ENDIF}
818 {$IFDEF aabbtree_many_asserts}assert((nodeGId
>= 0) and (nodeGId
< mAllocCount
));{$ENDIF}
819 nodeF
:= @mNodes
[nodeFId
];
820 nodeG
:= @mNodes
[nodeGId
];
822 nodeC
.children
[TTreeNode
.Left
] := nodeId
;
823 nodeC
.parentId
:= nodeA
.parentId
;
824 nodeA
.parentId
:= nodeCId
;
826 if (nodeC
.parentId
<> TTreeNode
.NullTreeNode
) then
828 if (mNodes
[nodeC
.parentId
].children
[TTreeNode
.Left
] = nodeId
) then
830 mNodes
[nodeC
.parentId
].children
[TTreeNode
.Left
] := nodeCId
;
834 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeC
.parentId
].children
[TTreeNode
.Right
] = nodeId
);{$ENDIF}
835 mNodes
[nodeC
.parentId
].children
[TTreeNode
.Right
] := nodeCId
;
840 mRootNodeId
:= nodeCId
;
843 {$IFDEF aabbtree_many_asserts}assert(not nodeC
.leaf
);{$ENDIF}
844 {$IFDEF aabbtree_many_asserts}assert(not nodeA
.leaf
);{$ENDIF}
846 // if the right node C was higher than left node B because of the F node
847 if (nodeF
.height
> nodeG
.height
) then
849 nodeC
.children
[TTreeNode
.Right
] := nodeFId
;
850 nodeA
.children
[TTreeNode
.Right
] := nodeGId
;
851 nodeG
.parentId
:= nodeId
;
853 // recompute the AABB of node A and C
854 nodeA
.aabb
.setMergeTwo(nodeB
.aabb
, nodeG
.aabb
);
855 nodeC
.aabb
.setMergeTwo(nodeA
.aabb
, nodeF
.aabb
);
857 // recompute the height of node A and C
858 nodeA
.height
:= maxI(nodeB
.height
, nodeG
.height
)+1;
859 nodeC
.height
:= maxI(nodeA
.height
, nodeF
.height
)+1;
860 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
861 {$IFDEF aabbtree_many_asserts}assert(nodeC
.height
> 0);{$ENDIF}
865 // if the right node C was higher than left node B because of node G
866 nodeC
.children
[TTreeNode
.Right
] := nodeGId
;
867 nodeA
.children
[TTreeNode
.Right
] := nodeFId
;
868 nodeF
.parentId
:= nodeId
;
870 // recompute the AABB of node A and C
871 nodeA
.aabb
.setMergeTwo(nodeB
.aabb
, nodeF
.aabb
);
872 nodeC
.aabb
.setMergeTwo(nodeA
.aabb
, nodeG
.aabb
);
874 // recompute the height of node A and C
875 nodeA
.height
:= maxI(nodeB
.height
, nodeF
.height
)+1;
876 nodeC
.height
:= maxI(nodeA
.height
, nodeG
.height
)+1;
877 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
878 {$IFDEF aabbtree_many_asserts}assert(nodeC
.height
> 0);{$ENDIF}
881 // return the new root of the sub-tree
886 // if the left node B is 2 higher than right node C
887 if (balanceFactor
< -1) then
889 {$IFDEF aabbtree_many_asserts}assert(not nodeB
.leaf
);{$ENDIF}
891 nodeFId
:= nodeB
.children
[TTreeNode
.Left
];
892 nodeGId
:= nodeB
.children
[TTreeNode
.Right
];
893 {$IFDEF aabbtree_many_asserts}assert((nodeFId
>= 0) and (nodeFId
< mAllocCount
));{$ENDIF}
894 {$IFDEF aabbtree_many_asserts}assert((nodeGId
>= 0) and (nodeGId
< mAllocCount
));{$ENDIF}
895 nodeF
:= @mNodes
[nodeFId
];
896 nodeG
:= @mNodes
[nodeGId
];
898 nodeB
.children
[TTreeNode
.Left
] := nodeId
;
899 nodeB
.parentId
:= nodeA
.parentId
;
900 nodeA
.parentId
:= nodeBId
;
902 if (nodeB
.parentId
<> TTreeNode
.NullTreeNode
) then
904 if (mNodes
[nodeB
.parentId
].children
[TTreeNode
.Left
] = nodeId
) then
906 mNodes
[nodeB
.parentId
].children
[TTreeNode
.Left
] := nodeBId
;
910 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeB
.parentId
].children
[TTreeNode
.Right
] = nodeId
);{$ENDIF}
911 mNodes
[nodeB
.parentId
].children
[TTreeNode
.Right
] := nodeBId
;
916 mRootNodeId
:= nodeBId
;
919 {$IFDEF aabbtree_many_asserts}assert(not nodeB
.leaf
);{$ENDIF}
920 {$IFDEF aabbtree_many_asserts}assert(not nodeA
.leaf
);{$ENDIF}
922 // if the left node B was higher than right node C because of the F node
923 if (nodeF
.height
> nodeG
.height
) then
925 nodeB
.children
[TTreeNode
.Right
] := nodeFId
;
926 nodeA
.children
[TTreeNode
.Left
] := nodeGId
;
927 nodeG
.parentId
:= nodeId
;
929 // recompute the AABB of node A and B
930 nodeA
.aabb
.setMergeTwo(nodeC
.aabb
, nodeG
.aabb
);
931 nodeB
.aabb
.setMergeTwo(nodeA
.aabb
, nodeF
.aabb
);
933 // recompute the height of node A and B
934 nodeA
.height
:= maxI(nodeC
.height
, nodeG
.height
)+1;
935 nodeB
.height
:= maxI(nodeA
.height
, nodeF
.height
)+1;
936 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
937 {$IFDEF aabbtree_many_asserts}assert(nodeB
.height
> 0);{$ENDIF}
941 // if the left node B was higher than right node C because of node G
942 nodeB
.children
[TTreeNode
.Right
] := nodeGId
;
943 nodeA
.children
[TTreeNode
.Left
] := nodeFId
;
944 nodeF
.parentId
:= nodeId
;
946 // recompute the AABB of node A and B
947 nodeA
.aabb
.setMergeTwo(nodeC
.aabb
, nodeF
.aabb
);
948 nodeB
.aabb
.setMergeTwo(nodeA
.aabb
, nodeG
.aabb
);
950 // recompute the height of node A and B
951 nodeA
.height
:= maxI(nodeC
.height
, nodeF
.height
)+1;
952 nodeB
.height
:= maxI(nodeA
.height
, nodeG
.height
)+1;
953 {$IFDEF aabbtree_many_asserts}assert(nodeA
.height
> 0);{$ENDIF}
954 {$IFDEF aabbtree_many_asserts}assert(nodeB
.height
> 0);{$ENDIF}
957 // return the new root of the sub-tree
962 // if the sub-tree is balanced, return the current root node
967 // compute the height of a given node in the tree
968 function TDynAABBTree
.computeHeight (nodeId
: Integer): Integer;
971 leftHeight
, rightHeight
: Integer;
973 {$IFDEF aabbtree_many_asserts}assert((nodeId
>= 0) and (nodeId
< mAllocCount
));{$ENDIF}
974 node
:= @mNodes
[nodeId
];
976 // if the node is a leaf, its height is zero
977 if (node
.leaf
) then begin result
:= 0; exit
; end;
979 // compute the height of the left and right sub-tree
980 leftHeight
:= computeHeight(node
.children
[TTreeNode
.Left
]);
981 rightHeight
:= computeHeight(node
.children
[TTreeNode
.Right
]);
983 // return the height of the node
984 result
:= 1+maxI(leftHeight
, rightHeight
);
988 // internally add an object into the tree
989 function TDynAABBTree
.insertObjectInternal (var aabb
: AABB2D
; staticObject
: Boolean): Integer;
993 // get the next available node (or allocate new ones if necessary)
994 nodeId
:= allocateNode();
996 // create the fat aabb to use in the tree
997 mNodes
[nodeId
].aabb
:= AABB2D
.Create(aabb
);
998 if (not staticObject
) then
1000 mNodes
[nodeId
].aabb
.minX
-= mExtraGap
;
1001 mNodes
[nodeId
].aabb
.minY
-= mExtraGap
;
1002 mNodes
[nodeId
].aabb
.maxX
+= mExtraGap
;
1003 mNodes
[nodeId
].aabb
.maxY
+= mExtraGap
;
1006 // set the height of the node in the tree
1007 mNodes
[nodeId
].height
:= 0;
1009 // insert the new leaf node in the tree
1010 insertLeafNode(nodeId
);
1011 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
1013 {$IFDEF aabbtree_many_asserts}assert(nodeId
>= 0);{$ENDIF}
1015 // return the id of the node
1020 // initialize the tree
1021 procedure TDynAABBTree
.setup ();
1025 mRootNodeId
:= TTreeNode
.NullTreeNode
;
1027 mAllocCount
:= 8192;
1029 SetLength(mNodes
, mAllocCount
);
1030 //memset(mNodes, 0, mAllocCount*TTreeNode.sizeof);
1031 for i
:= 0 to mAllocCount
-1 do mNodes
[i
].clear();
1033 // initialize the allocated nodes
1034 for i
:= 0 to mAllocCount
-1 do
1036 mNodes
[i
].nextNodeId
:= i
+1;
1037 mNodes
[i
].height
:= -1;
1039 mNodes
[mAllocCount
-1].nextNodeId
:= TTreeNode
.NullTreeNode
;
1044 // also, checks if the tree structure is valid (for debugging purpose)
1045 function TDynAABBTree
.forEachLeaf (dg
: TForEachLeafCB
): Boolean;
1046 function forEachNode (nodeId
: Integer): Boolean;
1049 leftChild
, rightChild
, height
: Integer;
1053 if (nodeId
= TTreeNode
.NullTreeNode
) then exit
;
1054 // if it is the root
1055 if (nodeId
= mRootNodeId
) then assert(mNodes
[nodeId
].parentId
= TTreeNode
.NullTreeNode
);
1056 // get the children nodes
1057 pNode
:= @mNodes
[nodeId
];
1058 assert(pNode
.height
>= 0);
1059 if (not pNode
.aabb
.valid
) then
1061 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
);
1064 getFleshAABB(aabb
, pNode
.flesh
);
1065 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
);
1068 assert(pNode
.aabb
.valid
);
1069 assert(pNode
.aabb
.volume
> 0);
1070 // if the current node is a leaf
1071 if (pNode
.leaf
) then
1073 assert(pNode
.height
= 0);
1074 if assigned(dg
) then result
:= dg(pNode
.flesh
, pNode
.aabb
);
1078 leftChild
:= pNode
.children
[TTreeNode
.Left
];
1079 rightChild
:= pNode
.children
[TTreeNode
.Right
];
1080 // check that the children node Ids are valid
1081 assert((0 <= leftChild
) and (leftChild
< mAllocCount
));
1082 assert((0 <= rightChild
) and (rightChild
< mAllocCount
));
1083 // check that the children nodes have the correct parent node
1084 assert(mNodes
[leftChild
].parentId
= nodeId
);
1085 assert(mNodes
[rightChild
].parentId
= nodeId
);
1086 // check the height of node
1087 height
:= 1+maxI(mNodes
[leftChild
].height
, mNodes
[rightChild
].height
);
1088 assert(mNodes
[nodeId
].height
= height
);
1089 // check the AABB of the node
1090 aabb
:= AABB2D
.Create(mNodes
[leftChild
].aabb
, mNodes
[rightChild
].aabb
);
1091 assert(aabb
.minX
= mNodes
[nodeId
].aabb
.minX
);
1092 assert(aabb
.minY
= mNodes
[nodeId
].aabb
.minY
);
1093 assert(aabb
.maxX
= mNodes
[nodeId
].aabb
.maxX
);
1094 assert(aabb
.maxY
= mNodes
[nodeId
].aabb
.maxY
);
1095 // recursively check the children nodes
1096 result
:= forEachNode(leftChild
);
1097 if not result
then result
:= forEachNode(rightChild
);
1102 // recursively check each node
1103 result
:= forEachNode(mRootNodeId
);
1107 // return `true` from visitor to stop immediately
1108 // checker should check if this node should be considered to further checking
1109 // returns tree node if visitor says stop or -1
1110 function TDynAABBTree
.visit (checker
: TVisitCheckerCB
; visitor
: TQueryOverlapCB
; tagmask
: Integer=-1): Integer;
1112 stack
: array [0..255] of Integer; // stack with the nodes to visit
1113 bigstack
: array of Integer = nil;
1116 procedure spush (id
: Integer); inline;
1120 if (sp
< length(stack
)) then
1122 // use "small stack"
1129 xsp
:= sp
-length(stack
);
1130 if (xsp
< length(bigstack
)) then
1133 bigstack
[xsp
] := id
;
1138 SetLength(bigstack
, length(bigstack
)+1);
1139 bigstack
[high(bigstack
)] := id
;
1146 function spop (): Integer; inline;
1148 {$IFDEF aabbtree_many_asserts}assert(sp > 0);{$ENDIF}
1149 if (sp <= length(stack)) then
1151 // use "small stack"
1153 result := stack[sp];
1159 result := bigstack[sp-length(stack)];
1168 if not assigned(checker
) then begin result
:= -1; exit
; end;
1169 //if not assigned(visitor) then begin result := -1; exit; end;
1171 {$IFDEF aabbtree_query_count}
1173 mNodesDeepVisited
:= 0;
1176 // start from root node
1179 // while there are still nodes to visit
1182 // get the next node id to visit
1184 {$IFDEF aabbtree_many_asserts}assert(sp
> 0);{$ENDIF}
1185 if (sp
<= length(stack
)) then
1187 // use "small stack"
1189 nodeId
:= stack
[sp
];
1195 nodeId
:= bigstack
[sp
-length(stack
)];
1198 // skip it if it is a nil node
1199 if (nodeId
= TTreeNode
.NullTreeNode
) then continue
;
1200 {$IFDEF aabbtree_query_count}Inc(mNodesVisited
);{$ENDIF}
1201 // get the corresponding node
1202 node
:= @mNodes
[nodeId
];
1203 // should we investigate this node?
1204 if (checker(node
)) then
1206 // if the node is a leaf
1209 // call visitor on it
1210 {$IFDEF aabbtree_query_count}Inc(mNodesDeepVisited
);{$ENDIF}
1211 if ((node
.tag
and tagmask
) <> 0) and assigned(visitor
) then
1213 if (visitor(node
.flesh
, node
.tag
)) then begin result
:= nodeId
; exit
; end;
1218 // if the node is not a leaf, we need to visit its children
1219 spush(node
.children
[TTreeNode
.Left
]);
1220 spush(node
.children
[TTreeNode
.Right
]);
1225 result
:= -1; // oops
1232 // add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds
1233 // 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
1234 constructor TDynAABBTree
.Create (extraAABBGap
: Float
=0.0);
1236 mExtraGap
:= extraAABBGap
;
1241 destructor TDynAABBTree
.Destroy ();
1248 // clear all the nodes and reset the tree
1249 procedure TDynAABBTree
.reset ();
1256 function TDynAABBTree
.computeTreeHeight (): Integer; begin result
:= computeHeight(mRootNodeId
); end;
1259 // return the root AABB of the tree
1260 procedure TDynAABBTree
.getRootAABB (var aabb
: AABB2D
);
1262 {$IFDEF aabbtree_many_asserts}assert((mRootNodeId
>= 0) and (mRootNodeId
< mNodeCount
));{$ENDIF}
1263 aabb
:= mNodes
[mRootNodeId
].aabb
;
1267 // does the given id represents a valid object?
1268 // WARNING: ids of removed objects can be reused on later insertions!
1269 function TDynAABBTree
.isValidId (id
: Integer): Boolean;
1271 result
:= (id
>= 0) and (id
< mNodeCount
) and (mNodes
[id
].leaf
);
1275 // get object by nodeid; can return nil for invalid ids
1276 function TDynAABBTree
.getNodeObjectId (nodeid
: Integer): TTreeFlesh
;
1278 if (nodeid
>= 0) and (nodeid
< mNodeCount
) and (mNodes
[nodeid
].leaf
) then result
:= mNodes
[nodeid
].flesh
else result
:= nil;
1281 // get fat object AABB by nodeid; returns random shit for invalid ids
1282 procedure TDynAABBTree
.getNodeFatAABB (var aabb
: AABB2D
; nodeid
: Integer);
1284 if (nodeid
>= 0) and (nodeid
< mNodeCount
) and (not mNodes
[nodeid
].isfree
) then aabb
.copyFrom(mNodes
[nodeid
].aabb
) else aabb
.setDims(0, 0, 0, 0);
1288 // insert an object into the tree
1289 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
1290 // AABB for static object will not be "fat" (simple optimization)
1291 // WARNING! inserting the same object several times *WILL* break everything!
1292 function TDynAABBTree
.insertObject (flesh
: TTreeFlesh
; tag
: Integer; staticObject
: Boolean=false): Integer;
1297 if not getFleshAABB(aabb
, flesh
) then
1299 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
);
1300 //raise Exception.Create('trying to insert invalid flesh in dyntree');
1304 if not aabb
.valid
then
1306 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
);
1307 raise Exception
.Create('trying to insert invalid aabb in dyntree');
1311 //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);
1312 nodeId
:= insertObjectInternal(aabb
, staticObject
);
1313 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].leaf
);{$ENDIF}
1314 mNodes
[nodeId
].flesh
:= flesh
;
1315 mNodes
[nodeId
].tag
:= tag
;
1320 // remove an object from the tree
1321 // WARNING: ids of removed objects can be reused on later insertions!
1322 procedure TDynAABBTree
.removeObject (nodeId
: Integer);
1324 if (nodeId
< 0) or (nodeId
>= mNodeCount
) or (not mNodes
[nodeId
].leaf
) then raise Exception
.Create('invalid node id in TDynAABBTree');
1325 // remove the node from the tree
1326 removeLeafNode(nodeId
);
1327 releaseNode(nodeId
);
1331 function TDynAABBTree
.updateObject (nodeId
: Integer; dispX
, dispY
: Float
; forceReinsert
: Boolean=false): Boolean;
1335 if (nodeId
< 0) or (nodeId
>= mNodeCount
) or (not mNodes
[nodeId
].leaf
) then raise Exception
.Create('invalid node id in TDynAABBTree.updateObject');
1337 if not getFleshAABB(newAABB
, mNodes
[nodeId
].flesh
) then raise Exception
.Create('invalid node id in TDynAABBTree.updateObject');
1338 if not newAABB
.valid
then raise Exception
.Create('invalid flesh aabb in TDynAABBTree.updateObject');
1340 // if the new AABB is still inside the fat AABB of the node
1341 if (not forceReinsert
) and (mNodes
[nodeId
].aabb
.contains(newAABB
)) then begin result
:= false; exit
; end;
1343 // if the new AABB is outside the fat AABB, we remove the corresponding node
1344 removeLeafNode(nodeId
);
1346 // compute the fat AABB by inflating the AABB with a constant gap
1347 mNodes
[nodeId
].aabb
:= newAABB
;
1348 if (not forceReinsert
) and ((dispX
<> 0) or (dispY
<> 0)) then
1350 mNodes
[nodeId
].aabb
.minX
:= mNodes
[nodeId
].aabb
.minX
-mExtraGap
;
1351 mNodes
[nodeId
].aabb
.minY
:= mNodes
[nodeId
].aabb
.minY
-mExtraGap
;
1352 mNodes
[nodeId
].aabb
.maxX
:= mNodes
[nodeId
].aabb
.maxX
+mExtraGap
;
1353 mNodes
[nodeId
].aabb
.maxY
:= mNodes
[nodeId
].aabb
.maxY
+mExtraGap
;
1356 // inflate the fat AABB in direction of the linear motion of the AABB
1357 if (dispX
< 0.0) then
1359 mNodes
[nodeId
].aabb
.minX
:= mNodes
[nodeId
].aabb
.minX
+LinearMotionGapMultiplier
*dispX
;
1363 mNodes
[nodeId
].aabb
.maxX
:= mNodes
[nodeId
].aabb
.maxX
+LinearMotionGapMultiplier
*dispX
;
1365 if (dispY
< 0.0) then
1367 mNodes
[nodeId
].aabb
.minY
:= mNodes
[nodeId
].aabb
.minY
+LinearMotionGapMultiplier
*dispY
;
1371 mNodes
[nodeId
].aabb
.maxY
:= mNodes
[nodeId
].aabb
.maxY
+LinearMotionGapMultiplier
*dispY
;
1374 {$IFDEF aabbtree_many_asserts}assert(mNodes
[nodeId
].aabb
.contains(newAABB
));{$ENDIF}
1376 // reinsert the node into the tree
1377 insertLeafNode(nodeId
);
1383 // report all shapes overlapping with the AABB given in parameter
1384 function TDynAABBTree
.aabbQuery (ax
, ay
, aw
, ah
: Float
; cb
: TQueryOverlapCB
; tagmask
: Integer=-1): TTreeFlesh
;
1387 function checker (node
: PTreeNode
): Boolean;
1389 result
:= caabb
.overlaps(node
.aabb
);
1395 if not assigned(cb
) then exit
;
1396 if (aw
< 1) or (ah
< 1) then exit
;
1397 caabb
:= AABB2D
.Create(ax
, ay
, ax
+aw
, ay
+ah
);
1398 nid
:= visit(checker
, cb
, tagmask
);
1399 if (nid
>= 0) then result
:= mNodes
[nid
].flesh
else result
:= nil;
1403 // report body that contains the given point, or nil
1404 function TDynAABBTree
.pointQuery (ax
, ay
: Float
; cb
: TQueryOverlapCB
): TTreeFlesh
;
1405 function checker (node
: PTreeNode
): Boolean;
1407 result
:= node
.aabb
.contains(ax
, ay
);
1412 nid
:= visit(checker
, cb
);
1413 {$IFDEF aabbtree_many_asserts}assert((nid
< 0) or ((nid
>= 0) and (nid
< mNodeCount
) and (mNodes
[nid
].leaf
)));{$ENDIF}
1414 if (nid
>= 0) then result
:= mNodes
[nid
].flesh
else result
:= nil;
1418 // segment querying method
1419 function TDynAABBTree
.segmentQuery (var qr
: TSegmentQueryResult
; ax
, ay
, bx
, by
: Float
; cb
: TSegQueryCallback
): Boolean;
1421 maxFraction
: Float
= 1.0e100
; // infinity
1422 curax
, curay
: Float
;
1423 curbx
, curby
: Float
;
1427 function checker (node
: PTreeNode
): Boolean;
1429 result
:= node
.aabb
.intersects(curax
, curay
, curbx
, curby
);
1432 function visitor (flesh
: TTreeFlesh
; tag
: Integer): Boolean;
1436 hitFraction
:= cb(flesh
, curax
, curay
, curbx
, curby
);
1437 // if the user returned a hitFraction of zero, it means that the raycasting should stop here
1438 if (hitFraction
= 0.0) then
1445 // if the user returned a positive fraction
1446 if (hitFraction
> 0.0) then
1448 // we update the maxFraction value and the ray AABB using the new maximum fraction
1449 if (hitFraction
< maxFraction
) then
1451 maxFraction
:= hitFraction
;
1452 qr
.dist
:= hitFraction
;
1455 //curb := cura+dir*hitFraction;
1456 curbx
:= curax
+dirx
*hitFraction
;
1457 curby
:= curay
+diry
*hitFraction
;
1460 result
:= false; // continue
1466 if (ax
>= bx
) or (ay
>= by
) then begin result
:= false; exit
; end;
1473 dirx
:= (curbx
-curax
);
1474 diry
:= (curby
-curay
);
1476 invlen
:= 1.0/sqrt(dirx
*dirx
+diry
*diry
);
1480 visit(checker
, visitor
);