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
35 // ////////////////////////////////////////////////////////////////////////// //
36 type
38 public
42 public
55 // ////////////////////////////////////////////////////////////////////////// //
56 type
58 public
61 private
68 public
82 // return true if the current AABB contains the AABB given in parameter
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
90 // ray direction must be normalized
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
106 *
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.
110 *
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:
114 *
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.
119 *
120 * 2. Altered source versions must be plainly marked as such, and must not be
121 * misrepresented as being the original software.
122 *
123 * 3. This notice may not be removed or altered from any source distribution.
124 *)
125 // ////////////////////////////////////////////////////////////////////////// //
126 (*
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.
132 *)
133 // ////////////////////////////////////////////////////////////////////////// //
134 // Dynamic AABB Tree: can be used to speed up broad phase in various engines
135 type
137 private
138 type
141 public
145 public
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 // height of the node in the tree (-1 for free nodes)
153 // fat axis aligned bounding box (AABB) corresponding to the node
155 //TODO: `flesh` can be united with `children`
159 public
160 // return true if the node is a leaf of the tree
165 //property flesh: Integer read children[0] write children[0];
171 //TVisitVisitorCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested;
173 public
174 // return `true` to stop
175 type TForEachLeafCB = function (abody: TTreeFlesh; var aabb: AABB2D): Boolean is nested; // WARNING! don't modify AABB here!
177 public
178 // in the broad-phase collision detection (dynamic AABB tree), the AABBs are
179 // also inflated in direction of the linear motion of the body by mutliplying the
180 // followin constant with the linear velocity and the elapsed time between two frames
181 {$IFDEF aabbtree_use_floats}
183 {$ELSE}
185 {$ENDIF}
187 private
190 mFreeNodeId: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
194 // extra AABB Gap used to allow the collision shape to move a little bit
195 // without triggering a large modification of the tree which can be costly
198 public
199 // called when a overlapping node has been found during the call to forEachAABBOverlap()
200 // return `true` to stop
202 type TSegQueryCallback = function (abody: TTreeFlesh; ax, ay, bx, by: Single): Single is nested; // return dist from (ax,ay) to abody
212 private
221 function visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; tagmask: Integer=-1): Integer;
223 public
224 {$IFDEF aabbtree_query_count}
226 {$ENDIF}
228 public
232 // clear all the nodes and reset the tree
242 // returns `false` if nodeid is not leaf
245 // return `false` for invalid flesh
246 function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; virtual; abstract;
248 // insert an object into the tree
249 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
250 // AABB for static object will not be "fat" (simple optimization)
251 // WARNING! inserting the same object several times *WILL* break everything!
254 // remove an object from the tree
255 // WARNING: ids of removed objects can be reused on later insertions!
258 (** update the dynamic tree after an object has moved.
259 *
260 * if the new AABB of the object that has moved is still inside its fat AABB, then nothing is done.
261 * otherwise, the corresponding node is removed and reinserted into the tree.
262 * the method returns true if the object has been reinserted into the tree.
263 * the `dispX` and `dispY` parameters are the linear velocity of the AABB multiplied by the elapsed time between two frames.
264 * if the `forceReinsert` parameter is `true`, we force a removal and reinsertion of the node
265 * (this can be useful if the shape AABB has become much smaller than the previous one for instance).
266 *
267 * note that you should call this method if body's AABB was modified, even if the body wasn't moved.
268 *
269 * if `forceReinsert` = `true` and both `dispX` and `dispY` are zeroes, convert object to "static" (don't extrude AABB).
270 *
271 * return `true` if the tree was modified.
272 *)
273 function updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
276 function aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
278 function segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback): Boolean;
285 {$IFDEF aabbtree_query_count}
288 {$ELSE}
291 {$ENDIF}
295 implementation
297 uses
298 SysUtils;
301 // ////////////////////////////////////////////////////////////////////////// //
302 function minI (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end;
303 function maxI (a, b: Integer): Integer; inline; begin if (a > b) then result := a else result := b; end;
305 function minF (a, b: TreeNumber): TreeNumber; inline; begin if (a < b) then result := a else result := b; end;
306 function maxF (a, b: TreeNumber): TreeNumber; inline; begin if (a > b) then result := a else result := b; end;
309 // ////////////////////////////////////////////////////////////////////////// //
310 constructor Ray2D.Create (ax, ay: Single; aangle: Single); begin setXYAngle(ax, ay, aangle); end;
311 constructor Ray2D.Create (ax0, ay0, ax1, ay1: Single); begin setX0Y0X1Y1(ax0, ay0, ax1, ay1); end;
316 begin
324 var
326 begin
333 begin
341 begin
350 // ////////////////////////////////////////////////////////////////////////// //
352 begin
357 begin
362 begin
366 function AABB2D.getvalid (): Boolean; inline; begin result := (minX < maxX) and (minY < maxY); end;
368 {$IFDEF aabbtree_use_floats}
371 {$ELSE}
374 {$ENDIF}
379 begin
384 {$IF DEFINED(D2F_DEBUG)}
386 {$ENDIF}
391 begin
396 {$IF DEFINED(D2F_DEBUG)}
398 {$ENDIF}
403 begin
404 {$IF DEFINED(D2F_DEBUG)}
407 {$ENDIF}
412 {$IF DEFINED(D2F_DEBUG)}
414 {$ENDIF}
419 begin
425 begin
426 {$IF DEFINED(D2F_DEBUG)}
428 {$ENDIF}
433 {$IF DEFINED(D2F_DEBUG)}
435 {$ENDIF}
440 begin
441 result :=
448 begin
454 begin
456 // exit with no intersection if found separated along any axis
463 // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box
464 // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
465 function AABB2D.intersects (var ray: Ray2D; tmino: PSingle=nil; tmaxo: PSingle=nil): Boolean; overload;
466 var
469 begin
470 // ok with coplanars
473 // do X
475 begin
482 // do Y
484 begin
488 // tmin
492 // tmax
499 begin
503 end
504 else
505 begin
511 var
514 begin
516 // it may be faster to first check if start or end point is inside AABB (this is sometimes enough for dyntree)
519 // nope, do it hard way
529 // ////////////////////////////////////////////////////////////////////////// //
530 procedure TDynAABBTree.TSegmentQueryResult.reset (); inline; begin dist := -1; flesh := nil; end;
531 function TDynAABBTree.TSegmentQueryResult.valid (): Boolean; inline; begin result := (dist >= 0) and (flesh <> nil); end;
534 // ////////////////////////////////////////////////////////////////////////// //
539 begin
553 begin
554 e_WriteLog(Format('NODE: parentId=%d; children=[%d,%d]; height=%d; tag=%d; fleshX=%d; fleshY=%d; aabb=(%d,%d)-(%d,%d)',
555 [parentId, children[0], children[1], Integer(height), tag, fleshX, fleshY, aabb.minX, aabb.minY, aabb.maxX, aabb.maxY]),
556 MSG_NOTIFY);
560 // ////////////////////////////////////////////////////////////////////////// //
561 // allocate and return a node to use in the tree
563 var
566 begin
567 // if there is no more allocated node to use
569 begin
571 // allocate more nodes in the tree
575 // initialize the allocated nodes
577 begin
584 // get the next free node
595 //e_WriteLog(Format('tree: allocated node #%d', [result]), MSG_NOTIFY);
599 // release a node
601 begin
611 //e_WriteLog(Format('tree: released node #%d', [nodeId]), MSG_NOTIFY);
615 // insert a leaf node in the tree
616 // 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
618 var
625 begin
626 // if the tree is empty
628 begin
631 exit;
636 // find the best sibling node for the new node
640 begin
644 // compute the merged AABB
649 // compute the cost of making the current node the sibling of the new node
652 // compute the minimum cost of pushing the new node further down the tree (inheritance cost)
655 // compute the cost of descending into the left child
660 // compute the cost of descending into the right child
665 // 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
668 // it is cheaper to go down into a child of the current node, choose the best child
669 //currentNodeId = (costLeft < costRight ? leftChild : rightChild);
675 // create a new parent for the new node and the sibling node
683 // if the sibling node was not the root node
685 begin
688 begin
690 end
691 else
692 begin
699 end
700 else
701 begin
702 // if the sibling node was the root node
710 // move up in the tree to change the AABBs that have changed
714 begin
715 // balance the sub-tree of the current node if it is not balanced
725 // recompute the height of the node in the tree
729 // recompute the AABB of the node
739 // remove a leaf node from the tree
741 var
744 begin
748 // if we are removing the root node (root node is a leaf in this case)
755 begin
757 end
758 else
759 begin
763 // if the parent of the node to remove is not the root node
765 begin
766 // destroy the parent node
768 begin
770 end
771 else
772 begin
773 {$IFDEF aabbtree_many_asserts}assert(mNodes[grandParentNodeId].children[TTreeNode.Right] = parentNodeId);{$ENDIF}
779 // 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
782 begin
783 // balance the current sub-tree if necessary
788 // get the two children of the current node
792 // recompute the AABB and the height of the current node
794 mNodes[currentNodeId].height := maxI(mNodes[leftChildId].height, mNodes[rightChildId].height)+1;
799 end
800 else
801 begin
802 // if the parent of the node to remove is the root node, the sibling node becomes the new root node
810 // balance the sub-tree of a given node using left or right rotations
811 // the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
812 // this method returns the new root node id
814 var
818 begin
823 // if the node is a leaf or the height of A's sub-tree is less than 2
824 if (nodeA.leaf) or (nodeA.height < 2) then begin result := nodeId; exit; end; // do not perform any rotation
826 // get the two children nodes
834 // compute the factor of the left and right sub-trees
837 // if the right node C is 2 higher than left node B
839 begin
854 begin
856 begin
858 end
859 else
860 begin
861 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeC.parentId].children[TTreeNode.Right] = nodeId);{$ENDIF}
864 end
865 else
866 begin
873 // if the right node C was higher than left node B because of the F node
875 begin
880 // recompute the AABB of node A and C
884 // recompute the height of node A and C
889 end
890 else
891 begin
892 // if the right node C was higher than left node B because of node G
897 // recompute the AABB of node A and C
901 // recompute the height of node A and C
908 // return the new root of the sub-tree
910 exit;
913 // if the left node B is 2 higher than right node C
915 begin
930 begin
932 begin
934 end
935 else
936 begin
937 {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeB.parentId].children[TTreeNode.Right] = nodeId);{$ENDIF}
940 end
941 else
942 begin
949 // if the left node B was higher than right node C because of the F node
951 begin
956 // recompute the AABB of node A and B
960 // recompute the height of node A and B
965 end
966 else
967 begin
968 // if the left node B was higher than right node C because of node G
973 // recompute the AABB of node A and B
977 // recompute the height of node A and B
984 // return the new root of the sub-tree
986 exit;
989 // if the sub-tree is balanced, return the current root node
994 // compute the height of a given node in the tree
996 var
999 begin
1003 // if the node is a leaf, its height is zero
1006 // compute the height of the left and right sub-tree
1010 // return the height of the node
1015 // internally add an object into the tree
1017 var
1020 begin
1021 // get the next available node (or allocate new ones if necessary)
1026 // create the fat aabb to use in the tree
1029 begin
1036 // set the height of the node in the tree
1039 // insert the new leaf node in the tree
1045 // return the id of the node
1050 // initialize the tree
1052 var
1054 begin
1060 //memset(mNodes, 0, mAllocCount*TTreeNode.sizeof);
1063 // initialize the allocated nodes
1065 begin
1074 // also, checks if the tree structure is valid (for debugging purpose)
1077 var
1081 begin
1084 // if it is the root
1086 // get the children nodes
1090 begin
1091 {$IFDEF aabbtree_use_floats}
1092 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);
1093 {$ELSE}
1094 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);
1095 {$ENDIF}
1097 begin
1099 {$IFDEF aabbtree_use_floats}
1100 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);
1101 {$ELSE}
1102 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);
1103 {$ENDIF}
1108 // if the current node is a leaf
1110 begin
1113 end
1114 else
1115 begin
1118 // check that the children node Ids are valid
1121 // check that the children nodes have the correct parent node
1124 // check the height of node
1127 // check the AABB of the node
1133 // recursively check the children nodes
1139 begin
1140 // recursively check each node
1145 // return `true` from visitor to stop immediately
1146 // checker should check if this node should be considered to further checking
1147 // returns tree node if visitor says stop or -1
1151 function TDynAABBTree.visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; tagmask: Integer=-1): Integer;
1152 var
1158 var
1160 begin
1162 begin
1163 // use "small stack"
1166 end
1167 else
1168 begin
1169 // use "big stack"
1172 begin
1173 // reuse
1175 end
1176 else
1177 begin
1178 // grow
1187 begin
1188 //{$IFDEF aabbtree_many_asserts}assert(sp > 0);{$ENDIF}
1190 begin
1191 // use "small stack"
1194 end
1195 else
1196 begin
1197 // use "big stack"
1203 var
1207 begin
1209 if not assigned(visitor) then raise Exception.Create('dyntree: empty visitors aren''t supported');
1210 //if not assigned(visitor) then begin result := -1; exit; end;
1211 //try
1212 {$IFDEF aabbtree_query_count}
1215 {$ENDIF}
1217 // start from root node
1220 // while there are still nodes to visit
1222 begin
1223 // get the next node id to visit
1224 {$IF TRUE}
1226 {$ELSE}
1228 begin
1229 // use "small stack"
1232 end
1233 else
1234 begin
1235 // use "big stack"
1239 {$ENDIF}
1240 // skip it if it is a nil node
1243 // get the corresponding node
1245 // should we investigate this node?
1248 ModeAABB:
1249 begin
1250 //doNode := caabb.overlaps(node.aabb);
1251 // this gives small speedup (or not...)
1252 // exit with no intersection if found separated along any axis
1257 ModePoint:
1258 begin
1259 //doNode := node.aabb.contains(caabb.minX, caabb.minY);
1260 // this gives small speedup
1261 doNode := (caabb.minX >= node.aabb.minX) and (caabb.minY >= node.aabb.minY) and (caabb.minX <= node.aabb.maxX) and (caabb.minY <= node.aabb.maxY);
1265 begin
1266 // if the node is a leaf
1268 begin
1269 // call visitor on it
1272 begin
1275 end
1276 else
1277 begin
1278 // if the node is not a leaf, we need to visit its children
1287 //finally
1288 // bigstack := nil;
1289 //end;
1293 // add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds
1294 // 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
1296 begin
1303 begin
1309 // clear all the nodes and reset the tree
1311 begin
1317 function TDynAABBTree.computeTreeHeight (): Integer; begin result := computeHeight(mRootNodeId); end;
1320 // return the root AABB of the tree
1322 begin
1323 {$IFDEF aabbtree_many_asserts}assert((mRootNodeId >= 0) and (mRootNodeId < mAllocCount));{$ENDIF}
1328 // does the given id represents a valid object?
1329 // WARNING: ids of removed objects can be reused on later insertions!
1331 begin
1336 // get object by nodeid; can return nil for invalid ids
1338 begin
1339 if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := nil;
1342 // get fat object AABB by nodeid; returns random shit for invalid ids
1344 begin
1345 if (nodeid >= 0) and (nodeid < mAllocCount) and (not mNodes[nodeid].isfree) then aabb.copyFrom(mNodes[nodeid].aabb) else aabb.setDims(0, 0, 0, 0);
1349 begin
1351 begin
1353 {$IFDEF aabbtree_use_floats}
1356 {$ELSE}
1359 {$ENDIF}
1360 end
1361 else
1362 begin
1366 //if (nodeid >= 0) and (nodeid < mAllocCount) then mNodes[nodeid].dumpToLog();
1371 // insert an object into the tree
1372 // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
1373 // AABB for static object will not be "fat" (simple optimization)
1374 // WARNING! inserting the same object several times *WILL* break everything!
1375 function TDynAABBTree.insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer;
1376 var
1379 begin
1381 begin
1382 {$IFDEF aabbtree_use_floats}
1383 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);
1384 {$ELSE}
1385 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);
1386 {$ENDIF}
1387 //raise Exception.Create('trying to insert invalid flesh in dyntree');
1389 exit;
1392 begin
1393 {$IFDEF aabbtree_use_floats}
1394 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);
1395 {$ELSE}
1396 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);
1397 {$ENDIF}
1400 exit;
1402 //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);
1415 // remove an object from the tree
1416 // WARNING: ids of removed objects can be reused on later insertions!
1418 begin
1419 if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree');
1420 // remove the node from the tree
1426 function TDynAABBTree.updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
1427 var
1430 begin
1431 if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
1433 if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTree.updateObject');
1434 if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTree.updateObject');
1445 function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
1446 var
1450 begin
1451 if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
1453 if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTree.updateObject');
1454 if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTree.updateObject');
1459 // if the new AABB is still inside the fat AABB of the node
1461 begin
1466 exit;
1469 // if the new AABB is outside the fat AABB, we remove the corresponding node
1474 // compute the fat AABB by inflating the AABB with a constant gap
1480 begin
1487 // inflate the fat AABB in direction of the linear motion of the AABB
1489 begin
1490 node.aabb.minX += LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1491 end
1492 else
1493 begin
1494 node.aabb.maxX += LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1498 begin
1499 node.aabb.minY += LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1500 end
1501 else
1502 begin
1503 node.aabb.maxY += LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF};
1508 // reinsert the node into the tree
1515 // report all shapes overlapping with the AABB given in parameter
1516 function TDynAABBTree.aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
1517 var
1520 begin
1523 var
1525 begin
1529 //caabb := AABB2D.Create(ax, ay, ax+aw, ay+ah);
1539 // report body that contains the given point, or nil
1542 begin
1546 var
1549 begin
1553 {$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mAllocCount) and (mNodes[nid].leaf)));{$ENDIF}
1558 // segment querying method
1559 function TDynAABBTree.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback): Boolean;
1560 var
1569 begin
1574 var
1576 begin
1578 // if the user returned a hitFraction of zero, it means that the raycasting should stop here
1580 begin
1584 exit;
1586 // if the user returned a positive fraction
1588 begin
1589 // we update the maxFraction value and the ray AABB using the new maximum fraction
1591 begin
1595 // fix curb here
1596 //curb := cura+dir*hitFraction;
1604 begin
1616 // normalize