c16719ac9e0cf143039d94a0dedefd2738ea4661
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {.$DEFINE aabbtree_many_asserts}
18 {$DEFINE aabbtree_query_count}
19 {.$DEFINE aabbtree_use_floats}
22 interface
24 uses
28 // ////////////////////////////////////////////////////////////////////////// //
29 type
33 // ////////////////////////////////////////////////////////////////////////// //
34 type
36 public
40 public
55 // ////////////////////////////////////////////////////////////////////////// //
56 type
58 public
61 private
68 public
84 // return true if the current AABB contains the AABB given in parameter
88 // return true if the current AABB is overlapping with the AABB in parameter
89 // two AABBs overlap if they overlap in the two axes at the same time
92 // ray direction must be normalized
93 function intersects (constref ray: Ray2D; tmino: PSingle=nil; tmaxo: PSingle=nil): Boolean; overload;
104 // ////////////////////////////////////////////////////////////////////////// //
105 (* Dynamic AABB tree (bounding volume hierarchy)
106 * based on the code from ReactPhysics3D physics library, http://www.reactphysics3d.com
107 * Copyright (c) 2010-2016 Daniel Chappuis
108 *
109 * This software is provided 'as-is', without any express or implied warranty.
110 * In no event will the authors be held liable for any damages arising from the
111 * use of this software.
112 *
113 * Permission is granted to anyone to use this software for any purpose,
114 * including commercial applications, and to alter it and redistribute it
115 * freely, subject to the following restrictions:
116 *
117 * 1. The origin of this software must not be misrepresented; you must not claim
118 * that you wrote the original software. If you use this software in a
119 * product, an acknowledgment in the product documentation would be
120 * appreciated but is not required.
121 *
122 * 2. Altered source versions must be plainly marked as such, and must not be
123 * misrepresented as being the original software.
124 *
125 * 3. This notice may not be removed or altered from any source distribution.
126 *)
127 // ////////////////////////////////////////////////////////////////////////// //
128 (*
129 * This class implements a dynamic AABB tree that is used for broad-phase
130 * collision detection. This data structure is inspired by Nathanael Presson's
131 * dynamic tree implementation in BulletPhysics. The following implementation is
132 * based on the one from Erin Catto in Box2D as described in the book
133 * "Introduction to Game Physics with Box2D" by Ian Parberry.
134 *)
135 // ////////////////////////////////////////////////////////////////////////// //
136 // Dynamic AABB Tree: can be used to speed up broad phase in various engines
137 type
139 public
142 private
143 type
146 public
150 public
151 // a node is either in the tree (has a parent) or in the free nodes list (has a next node)
153 //nextNodeId: Integer;
154 // a node is either a leaf (has data) or is an internal node (has children)
155 children: array [0..1] of Integer; // left and right child of the node (children[0] = left child)
156 // height of the node in the tree (-1 for free nodes)
158 // fat axis aligned bounding box (AABB) corresponding to the node
160 //TODO: `flesh` can be united with `children`
164 public
165 // return true if the node is a leaf of the tree
170 //property flesh: Integer read children[0] write children[0];
176 //TVisitVisitorCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested;
182 public
183 // return `true` to stop
184 type TForEachLeafCB = function (abody: TTreeFlesh; constref aabb: AABB2D): Boolean is nested; // WARNING! don't modify AABB here!
186 public
187 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
188 // also inflated in direction of the linear motion of the body by mutliplying the
189 // followin constant with the linear velocity and the elapsed time between two frames
190 {$IFDEF aabbtree_use_floats}
192 {$ELSE}
194 {$ENDIF}
196 public
197 // called when a overlapping node has been found during the call to forEachAABBOverlap()
198 // return `true` to stop
200 type TSegQueryCallback = function (abody: TTreeFlesh; ax, ay, bx, by: Single): Single is nested; // return dist from (ax,ay) to abody
212 private
215 mFreeNodeId: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
219 // extra AABB Gap used to allow the collision shape to move a little bit
220 // without triggering a large modification of the tree which can be costly
225 // for segment query
241 private
250 function visit (constref caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; visdg: TQueryOverlapDg; tagmask: Integer): Integer;
254 public
255 {$IFDEF aabbtree_query_count}
257 {$ENDIF}
259 public
263 // clear all the nodes and reset the tree
273 // returns `false` if nodeid is not leaf
276 // return `false` for invalid flesh
277 function getFleshAABB (out aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; virtual; abstract;
279 // insert an object into the tree
280 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
281 // AABB for static object will not be "fat" (simple optimization)
282 // WARNING! inserting the same object several times *WILL* break everything!
283 function insertObject (flesh: TTreeFlesh; tag: Integer=-1; staticObject: Boolean=false): Integer;
285 // remove an object from the tree
286 // WARNING: ids of removed objects can be reused on later insertions!
289 (** update the dynamic tree after an object has moved.
290 *
291 * if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
292 * otherwise, the corresponding node is removed and reinserted into the tree.
293 * the method returns true if the object has been reinserted into the tree.
294 * the `dispX` and `dispY` parameters are the linear velocity of the AABB multiplied by the elapsed time between two frames.
295 * if the `forceReinsert` parameter is `true`, we force a removal and reinsertion of the node
296 * (this can be useful if the shape AABB has become much smaller than the previous one for instance).
297 *
298 * note that you should call this method if body's AABB was modified, even if the body wasn't moved.
299 *
300 * if `forceReinsert` = `true` and both `dispX` and `dispY` are zeroes, convert object to "static" (don't extrude AABB).
301 *
302 * return `true` if the tree was modified.
303 *)
304 function updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
307 function aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
309 function segmentQuery (out qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback; tagmask: Integer=-1): Boolean;
316 {$IFDEF aabbtree_query_count}
319 {$ELSE}
322 {$ENDIF}
333 implementation
335 uses
336 SysUtils;
339 // ////////////////////////////////////////////////////////////////////////// //
340 function dtMinI (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end;
341 function dtMaxI (a, b: Integer): Integer; inline; begin if (a > b) then result := a else result := b; end;
343 function dtMinF (a, b: TreeNumber): TreeNumber; inline; begin if (a < b) then result := a else result := b; end;
344 function dtMaxF (a, b: TreeNumber): TreeNumber; inline; begin if (a > b) then result := a else result := b; end;
347 // ////////////////////////////////////////////////////////////////////////// //
348 constructor Ray2D.Create (ax, ay: Single; aangle: Single); begin setXYAngle(ax, ay, aangle); end;
349 constructor Ray2D.Create (ax0, ay0, ax1, ay1: Single); begin setX0Y0X1Y1(ax0, ay0, ax1, ay1); end;
354 begin
362 var
364 begin
371 begin
379 begin
389 begin
395 // ////////////////////////////////////////////////////////////////////////// //
397 begin
402 begin
407 begin
412 begin
419 function AABB2D.getvalid (): Boolean; inline; begin result := (minX <= maxX) and (minY <= maxY); end;
421 {$IFDEF aabbtree_use_floats}
424 {$ELSE}
427 {$ENDIF}
432 begin
437 {$IF DEFINED(D2F_DEBUG)}
439 {$ENDIF}
444 begin
449 {$IF DEFINED(D2F_DEBUG)}
451 {$ENDIF}
456 begin
457 {$IF DEFINED(D2F_DEBUG)}
460 {$ENDIF}
465 {$IF DEFINED(D2F_DEBUG)}
467 {$ENDIF}
472 begin
478 begin
479 {$IF DEFINED(D2F_DEBUG)}
481 {$ENDIF}
486 {$IF DEFINED(D2F_DEBUG)}
488 {$ENDIF}
493 begin
494 result :=
501 begin
507 begin
509 // exit with no intersection if found separated along any axis
516 // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box
517 // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
518 function AABB2D.intersects (constref ray: Ray2D; tmino: PSingle=nil; tmaxo: PSingle=nil): Boolean; overload;
519 var
522 begin
523 // ok with coplanars
526 // do X
528 begin
535 // do Y
537 begin
541 // tmin
545 // tmax
552 begin
556 end
557 else
558 begin
564 var
567 begin
569 // it may be faster to first check if start or end point is inside AABB (this is sometimes enough for dyntree)
572 // nope, do it hard way
582 // ////////////////////////////////////////////////////////////////////////// //
583 constructor TDynAABBTreeBase.TSegmentQueryResult.Create (fuckyoufpc: Boolean); begin dist := -1; flesh := Default(ITP); end;
584 procedure TDynAABBTreeBase.TSegmentQueryResult.reset (); inline; begin dist := -1; flesh := Default(ITP); end;
585 function TDynAABBTreeBase.TSegmentQueryResult.valid (): Boolean; inline; begin result := (dist >= 0) and (flesh <> Default(ITP)); end;
588 // ////////////////////////////////////////////////////////////////////////// //
589 function TDynAABBTreeBase.TTreeNode.leaf (): Boolean; inline; begin result := (height = 0); end;
590 function TDynAABBTreeBase.TTreeNode.isfree (): Boolean; inline; begin result := (height = -1); end;
593 begin
607 begin
608 e_WriteLog(Format('NODE: parentId=%d; children=[%d,%d]; height=%d; tag=%d; fleshX=%d; fleshY=%d; aabb=(%d,%d)-(%d,%d)',
609 [parentId, children[0], children[1], Integer(height), tag, fleshX, fleshY, aabb.minX, aabb.minY, aabb.maxX, aabb.maxY]),
610 MSG_NOTIFY);
614 // ////////////////////////////////////////////////////////////////////////// //
615 // allocate and return a node to use in the tree
617 var
620 begin
621 // if there is no more allocated node to use
623 begin
625 // allocate more nodes in the tree
629 // initialize the allocated nodes
631 begin
638 // get the next free node
649 //e_WriteLog(Format('tree: allocated node #%d', [result]), MSG_NOTIFY);
653 // release a node
655 begin
665 //e_WriteLog(Format('tree: released node #%d', [nodeId]), MSG_NOTIFY);
669 // insert a leaf node in the tree
670 // 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
672 var
679 begin
680 // if the tree is empty
682 begin
685 exit;
690 // find the best sibling node for the new node
694 begin
698 // compute the merged AABB
703 // compute the cost of making the current node the sibling of the new node
706 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
709 // compute the cost of descending into the left child
714 // compute the cost of descending into the right child
719 // 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
722 // it is cheaper to go down into a child of the current node, choose the best child
723 //currentNodeId = (costLeft < costRight ? leftChild : rightChild);
729 // create a new parent for the new node and the sibling node
737 // if the sibling node was not the root node
739 begin
742 begin
744 end
745 else
746 begin
753 end
754 else
755 begin
756 // if the sibling node was the root node
764 // move up in the tree to change the AABBs that have changed
768 begin
769 // balance the sub-tree of the current node if it is not balanced
779 // recompute the height of the node in the tree
783 // recompute the AABB of the node
793 // remove a leaf node from the tree
795 var
798 begin
802 // if we are removing the root node (root node is a leaf in this case)
809 begin
811 end
812 else
813 begin
817 // if the parent of the node to remove is not the root node
819 begin
820 // destroy the parent node
822 begin
824 end
825 else
826 begin
827 {$IFDEF aabbtree_many_asserts}assert(mNodes[grandParentNodeId].children[TTreeNode.Right] = parentNodeId);{$ENDIF}
833 // 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
836 begin
837 // balance the current sub-tree if necessary
842 // get the two children of the current node
846 // recompute the AABB and the height of the current node
848 mNodes[currentNodeId].height := dtMaxI(mNodes[leftChildId].height, mNodes[rightChildId].height)+1;
853 end
854 else
855 begin
856 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
864 // balance the sub-tree of a given node using left or right rotations
865 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
866 // this method returns the new root node id
868 var
872 begin
877 // if the node is a leaf or the height of A's sub-tree is less than 2
878 if (nodeA.leaf) or (nodeA.height < 2) then begin result := nodeId; exit; end; // do not perform any rotation
880 // get the two children nodes
888 // compute the factor of the left and right sub-trees
891 // if the right node C is 2 higher than left node B
893 begin
908 begin
910 begin
912 end
913 else
914 begin
915 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeC.parentId].children[TTreeNode.Right] = nodeId);{$ENDIF}
918 end
919 else
920 begin
927 // if the right node C was higher than left node B because of the F node
929 begin
934 // recompute the AABB of node A and C
938 // recompute the height of node A and C
943 end
944 else
945 begin
946 // if the right node C was higher than left node B because of node G
951 // recompute the AABB of node A and C
955 // recompute the height of node A and C
962 // return the new root of the sub-tree
964 exit;
967 // if the left node B is 2 higher than right node C
969 begin
984 begin
986 begin
988 end
989 else
990 begin
991 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeB.parentId].children[TTreeNode.Right] = nodeId);{$ENDIF}
994 end
995 else
996 begin
1003 // if the left node B was higher than right node C because of the F node
1005 begin
1010 // recompute the AABB of node A and B
1014 // recompute the height of node A and B
1019 end
1020 else
1021 begin
1022 // if the left node B was higher than right node C because of node G
1027 // recompute the AABB of node A and B
1031 // recompute the height of node A and B
1038 // return the new root of the sub-tree
1040 exit;
1043 // if the sub-tree is balanced, return the current root node
1048 // compute the height of a given node in the tree
1050 var
1053 begin
1057 // if the node is a leaf, its height is zero
1060 // compute the height of the left and right sub-tree
1064 // return the height of the node
1069 // internally add an object into the tree
1070 function TDynAABBTreeBase.insertObjectInternal (constref aabb: AABB2D; staticObject: Boolean): Integer;
1071 var
1074 begin
1075 // get the next available node (or allocate new ones if necessary)
1080 // create the fat aabb to use in the tree
1083 begin
1090 // set the height of the node in the tree
1093 // insert the new leaf node in the tree
1099 // return the id of the node
1104 // initialize the tree
1106 var
1108 begin
1115 //memset(mNodes, 0, mAllocCount*TTreeNode.sizeof);
1118 // initialize the allocated nodes
1120 begin
1129 // also, checks if the tree structure is valid (for debugging purpose)
1131 var
1135 begin
1138 // if it is the root
1140 // get the children nodes
1144 begin
1145 {$IFDEF aabbtree_use_floats}
1146 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);
1147 {$ELSE}
1148 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);
1149 {$ENDIF}
1151 begin
1153 {$IFDEF aabbtree_use_floats}
1154 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);
1155 {$ELSE}
1156 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);
1157 {$ENDIF}
1162 // if the current node is a leaf
1164 begin
1167 end
1168 else
1169 begin
1172 // check that the children node Ids are valid
1175 // check that the children nodes have the correct parent node
1178 // check the height of node
1181 // check the AABB of the node
1187 // recursively check the children nodes
1194 // also, checks if the tree structure is valid (for debugging purpose)
1196 begin
1197 // recursively check each node
1202 // return `true` from visitor to stop immediately
1203 // checker should check if this node should be considered to further checking
1204 // returns tree node if visitor says stop or -1
1205 function TDynAABBTreeBase.visit (constref caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; visdg: TQueryOverlapDg; tagmask: Integer): Integer;
1206 const
1208 var
1215 begin
1217 //if not assigned(visitor) and not assigned(visdg) then raise Exception.Create('dyntree: empty visitors aren''t supported');
1223 {$IFDEF aabbtree_query_count}
1226 {$ENDIF}
1228 // start from root node
1229 // we can't have nested functions in generics, sorry
1230 {$IF FALSE}
1232 {$ELSE}
1236 {$ENDIF}
1238 // while there are still nodes to visit
1240 begin
1241 // get the next node id to visit
1242 // we can't have nested functions in generics, sorry
1243 {$IF FALSE}
1245 {$ELSE}
1248 {$ENDIF}
1249 // skip it if it is a nil node
1252 // get the corresponding node
1254 // should we investigate this node?
1257 ModeAABB:
1258 begin
1259 //doNode := caabb.overlaps(node.aabb);
1260 // this gives small speedup (or not...)
1261 // exit with no intersection if found separated along any axis
1266 ModePoint:
1267 begin
1268 //doNode := node.aabb.contains(caabb.minX, caabb.minY);
1269 // this gives small speedup
1270 doNode := (caabb.minX >= node.aabb.minX) and (caabb.minY >= node.aabb.minY) and (caabb.minX <= node.aabb.maxX) and (caabb.minY <= node.aabb.maxY);
1274 begin
1275 // if the node is a leaf
1277 begin
1278 // call visitor on it
1281 begin
1283 // update object vars from cache, so recursive calls to `visit()` will work
1286 // call callbacks
1289 // do some sanity checks
1291 // should we exit?
1293 begin
1297 exit;
1300 end
1301 else
1302 begin
1303 // if the node is not a leaf, we need to visit its children
1304 // we can't have nested functions in generics, sorry
1305 {$IF FALSE}
1308 {$ELSE}
1314 {$ENDIF}
1325 // add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds
1326 // 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
1328 begin
1338 begin
1345 // clear all the nodes and reset the tree
1347 begin
1353 function TDynAABBTreeBase.computeTreeHeight (): Integer; begin result := computeHeight(mRootNodeId); end;
1356 // return the root AABB of the tree
1358 begin
1359 {$IFDEF aabbtree_many_asserts}assert((mRootNodeId >= 0) and (mRootNodeId < mAllocCount));{$ENDIF}
1364 // does the given id represents a valid object?
1365 // WARNING: ids of removed objects can be reused on later insertions!
1367 begin
1372 // get object by nodeid; can return nil for invalid ids
1374 begin
1375 if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := Default(ITP);
1378 // get fat object AABB by nodeid; returns random shit for invalid ids
1380 begin
1381 if (nodeid >= 0) and (nodeid < mAllocCount) and (not mNodes[nodeid].isfree) then aabb := AABB2D.Create(mNodes[nodeid].aabb) else aabb := AABB2D.Create(0, 0, 0, 0);
1385 begin
1387 begin
1389 {$IFDEF aabbtree_use_floats}
1392 {$ELSE}
1395 {$ENDIF}
1396 end
1397 else
1398 begin
1402 //if (nodeid >= 0) and (nodeid < mAllocCount) then mNodes[nodeid].dumpToLog();
1407 // insert an object into the tree
1408 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
1409 // AABB for static object will not be "fat" (simple optimization)
1410 // WARNING! inserting the same object several times *WILL* break everything!
1411 function TDynAABBTreeBase.insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer;
1412 var
1415 begin
1417 begin
1418 {$IFDEF aabbtree_use_floats}
1419 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);
1420 {$ELSE}
1421 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);
1422 {$ENDIF}
1423 //raise Exception.Create('trying to insert invalid flesh in dyntree');
1425 exit;
1428 begin
1429 {$IFDEF aabbtree_use_floats}
1430 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);
1431 {$ELSE}
1432 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);
1433 {$ENDIF}
1436 exit;
1438 //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);
1451 // remove an object from the tree
1452 // WARNING: ids of removed objects can be reused on later insertions!
1454 begin
1455 if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTreeBase');
1456 // remove the node from the tree
1462 function TDynAABBTreeBase.updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
1463 var
1466 begin
1467 if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTreeBase.updateObject');
1469 if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTreeBase.updateObject');
1470 if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTreeBase.updateObject');
1481 function TDynAABBTreeBase.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
1482 var
1486 begin
1487 if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTreeBase.updateObject');
1489 if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTreeBase.updateObject');
1490 if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTreeBase.updateObject');
1495 // if the new AABB is still inside the fat AABB of the node
1497 begin
1502 exit;
1505 // if the new AABB is outside the fat AABB, we remove the corresponding node
1510 // compute the fat AABB by inflating the AABB with a constant gap
1516 begin
1523 // inflate the fat AABB in direction of the linear motion of the AABB
1525 begin
1526 node.aabb.minX += LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1527 end
1528 else
1529 begin
1530 node.aabb.maxX += LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1534 begin
1535 node.aabb.minY += LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1536 end
1537 else
1538 begin
1539 node.aabb.maxY += LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1544 // reinsert the node into the tree
1552 begin
1557 // report all shapes overlapping with the AABB given in parameter
1558 function TDynAABBTreeBase.aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
1559 var
1562 begin
1566 //chkAABB := AABB2D.Create(ax, ay, ax+aw, ay+ah);
1579 begin
1584 // report body that contains the given point, or nil
1585 function TDynAABBTreeBase.pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
1586 var
1589 begin
1593 {$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mAllocCount) and (mNodes[nid].leaf)));{$ENDIF}
1600 begin
1605 var
1607 begin
1609 // if the user returned a hitFraction of zero, it means that the raycasting should stop here
1611 begin
1615 exit;
1617 // if the user returned a positive fraction
1619 begin
1620 // we update the maxFraction value and the ray AABB using the new maximum fraction
1622 begin
1626 // fix curb here
1627 //curb := cura+dir*hitFraction;
1636 // segment querying method
1637 function TDynAABBTreeBase.segmentQuery (out qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback; tagmask: Integer=-1): Boolean;
1638 var
1646 begin
1667 // normalize
1672 //chkAABB := AABB2D.Create(0, 0, 1, 1);