X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fz_aabbtree.pas;h=fcb8f6801d20ea037a5e906b40cc2dcc9cb28c07;hb=1ac9fc74d98d56a450774702aae19fa1546f2849;hp=4f3e9938c0fe9c5f5acb617621a6242e890a5983;hpb=983a3707e9af7ec7aadd245ed3fda5876c6b04c7;p=d2df-sdl.git diff --git a/src/game/z_aabbtree.pas b/src/game/z_aabbtree.pas index 4f3e993..fcb8f68 100644 --- a/src/game/z_aabbtree.pas +++ b/src/game/z_aabbtree.pas @@ -15,7 +15,8 @@ *) {$INCLUDE ../shared/a_modes.inc} {.$DEFINE aabbtree_many_asserts} -{.$DEFINE aabbtree_query_count} +{$DEFINE aabbtree_query_count} +{.$DEFINE aabbtree_use_floats} unit z_aabbtree; interface @@ -25,8 +26,7 @@ uses e_log; // ////////////////////////////////////////////////////////////////////////// // type - Float = Single; - PFloat = ^Float; + {$IFDEF aabbtree_use_floats}TreeNumber = Single;{$ELSE}TreeNumber = Integer;{$ENDIF} TTreeFlesh = TObject; @@ -35,66 +35,66 @@ type type Ray2D = record public - origX, origY: Float; - dirX, dirY: Float; + origX, origY: Single; + dirX, dirY: Single; public - constructor Create (ax, ay: Float; aangle: Float); overload; - constructor Create (ax0, ay0, ax1, ay1: Float); overload; + constructor Create (ax, ay: Single; aangle: Single); overload; + constructor Create (ax0, ay0, ax1, ay1: Single); overload; constructor Create (const aray: Ray2D); overload; procedure copyFrom (const aray: Ray2D); inline; procedure normalizeDir (); inline; - procedure setXYAngle (ax, ay: Float; aangle: Float); inline; - procedure setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Float); inline; + procedure setXYAngle (ax, ay: Single; aangle: Single); inline; + procedure setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Single); inline; end; // ////////////////////////////////////////////////////////////////////////// // type AABB2D = record public - minX, minY, maxX, maxY: Float; + minX, minY, maxX, maxY: TreeNumber; private function getvalid (): Boolean; inline; - function getcenterX (): Float; inline; - function getcenterY (): Float; inline; - function getextentX (): Float; inline; - function getextentY (): Float; inline; + function getcenterX (): TreeNumber; inline; + function getcenterY (): TreeNumber; inline; + function getextentX (): TreeNumber; inline; + function getextentY (): TreeNumber; inline; public - constructor Create (x0, y0, x1, y1: Float); overload; + constructor Create (x0, y0, x1, y1: TreeNumber); overload; constructor Create (const aabb: AABB2D); overload; constructor Create (const aabb0, aabb1: AABB2D); overload; procedure copyFrom (const aabb: AABB2D); inline; - procedure setDims (x0, y0, x1, y1: Float); inline; + procedure setDims (x0, y0, x1, y1: TreeNumber); inline; procedure setMergeTwo (const aabb0, aabb1: AABB2D); inline; - function volume (): Float; inline; + function volume (): TreeNumber; inline; procedure merge (const aabb: AABB2D); inline; // return true if the current AABB contains the AABB given in parameter function contains (const aabb: AABB2D): Boolean; inline; overload; - function contains (ax, ay: Float): Boolean; inline; overload; + function contains (ax, ay: TreeNumber): Boolean; inline; overload; // return true if the current AABB is overlapping with the AABB in parameter // two AABBs overlap if they overlap in the two axes at the same time function overlaps (const aabb: AABB2D): Boolean; inline; overload; // ray direction must be normalized - function intersects (const ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload; - function intersects (ax, ay, bx, by: Float): Boolean; inline; overload; + function intersects (const ray: Ray2D; tmino: PSingle=nil; tmaxo: PSingle=nil): Boolean; overload; + function intersects (ax, ay, bx, by: Single): Boolean; inline; overload; property valid: Boolean read getvalid; - property centerX: Float read getcenterX; - property centerY: Float read getcenterY; - property extentX: Float read getextentX; - property extentY: Float read getextentY; + property centerX: TreeNumber read getcenterX; + property centerY: TreeNumber read getcenterY; + property extentX: TreeNumber read getextentX; + property extentY: TreeNumber read getextentY; end; @@ -153,6 +153,7 @@ type height: SmallInt; // fat axis aligned bounding box (AABB) corresponding to the node aabb: AABB2D; + tag: Integer; // just a user-defined tag public // return true if the node is a leaf of the tree procedure clear (); inline; @@ -163,7 +164,7 @@ type end; TVisitCheckerCB = function (node: PTreeNode): Boolean is nested; - TVisitVisitorCB = function (abody: TTreeFlesh): Boolean is nested; + //TVisitVisitorCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested; public // return `true` to stop @@ -173,7 +174,11 @@ type // in the broad-phase collision detection (dynamic AABB tree), the AABBs are // also inflated in direction of the linear motion of the body by mutliplying the // followin constant with the linear velocity and the elapsed time between two frames + {$IFDEF aabbtree_use_floats} const LinearMotionGapMultiplier = 1.7; + {$ELSE} + const LinearMotionGapMultiplier = 17; // *10 + {$ENDIF} private mNodes: array of TTreeNode; // nodes of the tree @@ -184,7 +189,21 @@ type // 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 - mExtraGap: Float; + mExtraGap: TreeNumber; + + public + // called when a overlapping node has been found during the call to forEachAABBOverlap() + // return `true` to stop + type TQueryOverlapCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested; + type TSegQueryCallback = function (abody: TTreeFlesh; ax, ay, bx, by: Single): Single is nested; // return dist from (ax,ay) to abody + + TSegmentQueryResult = record + dist: Single; // <0: nothing was hit + flesh: TTreeFlesh; + + procedure reset (); inline; + function valid (): Boolean; inline; + end; private function allocateNode (): Integer; @@ -195,7 +214,7 @@ type function computeHeight (nodeId: Integer): Integer; function insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer; procedure setup (); - function visit (checker: TVisitCheckerCB; visitor: TVisitVisitorCB): Integer; + function visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; tagmask: Integer=-1): Integer; public {$IFDEF aabbtree_query_count} @@ -203,21 +222,7 @@ type {$ENDIF} public - // called when a overlapping node has been found during the call to forEachAABBOverlap() - // return `true` to stop - type TQueryOverlapCB = function (abody: TTreeFlesh): Boolean is nested; - type TSegQueryCallback = function (abody: TTreeFlesh; ax, ay, bx, by: Float): Float is nested; // return dist from (ax,ay) to abody - - TSegmentQueryResult = record - dist: Float; // <0: nothing was hit - flesh: TTreeFlesh; - - procedure reset (); inline; - function valid (): Boolean; inline; - end; - - public - constructor Create (extraAABBGap: Float=0.0); + constructor Create (extraAABBGap: TreeNumber=0); destructor Destroy (); override; // clear all the nodes and reset the tree @@ -237,7 +242,7 @@ type // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error // AABB for static object will not be "fat" (simple optimization) // WARNING! inserting the same object several times *WILL* break everything! - function insertObject (flesh: TTreeFlesh; staticObject: Boolean=false): Integer; + function insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer; // remove an object from the tree // WARNING: ids of removed objects can be reused on later insertions! @@ -258,15 +263,15 @@ type * * return `true` if the tree was modified. *) - function updateObject (nodeId: Integer; dispX, dispY: Float; forceReinsert: Boolean=false): Boolean; + function updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; - function aabbQuery (ax, ay, aw, ah: Float; cb: TQueryOverlapCB): Boolean; - function pointQuery (ax, ay: Float; cb: TQueryOverlapCB): TTreeFlesh; - function segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: Float; cb: TSegQueryCallback): Boolean; + function aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh; + function pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB): TTreeFlesh; + function segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback): Boolean; function computeTreeHeight (): Integer; // compute the height of the tree - property extraGap: Float read mExtraGap write mExtraGap; + property extraGap: TreeNumber read mExtraGap write mExtraGap; property nodeCount: Integer read mNodeCount; property nodeAlloced: Integer read mAllocCount; {$IFDEF aabbtree_query_count} @@ -289,13 +294,13 @@ uses function minI (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end; function maxI (a, b: Integer): Integer; inline; begin if (a > b) then result := a else result := b; end; -function minF (a, b: Float): Float; inline; begin if (a < b) then result := a else result := b; end; -function maxF (a, b: Float): Float; inline; begin if (a > b) then result := a else result := b; end; +function minF (a, b: TreeNumber): TreeNumber; inline; begin if (a < b) then result := a else result := b; end; +function maxF (a, b: TreeNumber): TreeNumber; inline; begin if (a > b) then result := a else result := b; end; // ////////////////////////////////////////////////////////////////////////// // -constructor Ray2D.Create (ax, ay: Float; aangle: Float); begin setXYAngle(ax, ay, aangle); end; -constructor Ray2D.Create (ax0, ay0, ax1, ay1: Float); begin setX0Y0X1Y1(ax0, ay0, ax1, ay1); end; +constructor Ray2D.Create (ax, ay: Single; aangle: Single); begin setXYAngle(ax, ay, aangle); end; +constructor Ray2D.Create (ax0, ay0, ax1, ay1: Single); begin setX0Y0X1Y1(ax0, ay0, ax1, ay1); end; constructor Ray2D.Create (const aray: Ray2D); overload; begin copyFrom(aray); end; @@ -309,14 +314,14 @@ end; procedure Ray2D.normalizeDir (); inline; var - invlen: Float; + invlen: Single; begin invlen := 1.0/sqrt(dirX*dirX+dirY*dirY); dirX *= invlen; dirY *= invlen; end; -procedure Ray2D.setXYAngle (ax, ay: Float; aangle: Float); inline; +procedure Ray2D.setXYAngle (ax, ay: Single; aangle: Single); inline; begin origX := ax; origY := ay; @@ -324,7 +329,7 @@ begin dirY := sin(aangle); end; -procedure Ray2D.setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Float); inline; +procedure Ray2D.setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Single); inline; begin origX := ax0; origY := ay0; @@ -335,7 +340,7 @@ end; // ////////////////////////////////////////////////////////////////////////// // -constructor AABB2D.Create (x0, y0, x1, y1: Float); overload; +constructor AABB2D.Create (x0, y0, x1, y1: TreeNumber); overload; begin setDims(x0, y0, x1, y1); end; @@ -352,11 +357,15 @@ end; function AABB2D.getvalid (): Boolean; inline; begin result := (minX < maxX) and (minY < maxY); end; -function AABB2D.getcenterX (): Float; inline; begin result := (minX+maxX)/2.0; end; -function AABB2D.getcenterY (): Float; inline; begin result := (minY+maxY)/2.0; end; -function AABB2D.getextentX (): Float; inline; begin result := (maxX-minX)+1.0; end; -function AABB2D.getextentY (): Float; inline; begin result := (maxY-minY)+1.0; end; - +{$IFDEF aabbtree_use_floats} +function AABB2D.getcenterX (): TreeNumber; inline; begin result := (minX+maxX)/2.0; end; +function AABB2D.getcenterY (): TreeNumber; inline; begin result := (minY+maxY)/2.0; end; +{$ELSE} +function AABB2D.getcenterX (): TreeNumber; inline; begin result := (minX+maxX) div 2; end; +function AABB2D.getcenterY (): TreeNumber; inline; begin result := (minY+maxY) div 2; end; +{$ENDIF} +function AABB2D.getextentX (): TreeNumber; inline; begin result := (maxX-minX); end; +function AABB2D.getextentY (): TreeNumber; inline; begin result := (maxY-minY); end; procedure AABB2D.copyFrom (const aabb: AABB2D); inline; begin @@ -370,7 +379,7 @@ begin end; -procedure AABB2D.setDims (x0, y0, x1, y1: Float); inline; +procedure AABB2D.setDims (x0, y0, x1, y1: TreeNumber); inline; begin minX := minF(x0, x1); minY := minF(y0, y1); @@ -398,7 +407,7 @@ begin end; -function AABB2D.volume (): Float; inline; +function AABB2D.volume (): TreeNumber; inline; begin result := (maxX-minX)*(maxY-minY); end; @@ -427,7 +436,7 @@ begin end; -function AABB2D.contains (ax, ay: Float): Boolean; inline; overload; +function AABB2D.contains (ax, ay: TreeNumber): Boolean; inline; overload; begin result := (ax >= minX) and (ay >= minY) and (ax <= maxX) and (ay <= maxY); end; @@ -445,10 +454,10 @@ end; // something to consider here is that 0 * inf =nan which occurs when the ray starts exactly on the edge of a box // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/ -function AABB2D.intersects (const ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload; +function AABB2D.intersects (const ray: Ray2D; tmino: PSingle=nil; tmaxo: PSingle=nil): Boolean; overload; var - dinv, t1, t2, tmp: Float; - tmin, tmax: Float; + dinv, t1, t2, tmp: Single; + tmin, tmax: Single; begin // ok with coplanars tmin := -1.0e100; @@ -490,9 +499,9 @@ begin end; end; -function AABB2D.intersects (ax, ay, bx, by: Float): Boolean; inline; overload; +function AABB2D.intersects (ax, ay, bx, by: Single): Boolean; inline; overload; var - tmin: Float; + tmin: Single; ray: Ray2D; begin result := true; @@ -524,6 +533,7 @@ begin children[0] := 0; children[1] := 0; flesh := nil; + tag := 0; height := 0; aabb.minX := 0; aabb.minY := 0; @@ -591,8 +601,8 @@ var currentNodeId: Integer; leftChild, rightChild, siblingNode: Integer; oldParentNode, newParentNode: Integer; - volumeAABB, mergedVolume: Float; - costS, costI, costLeft, costRight: Float; + volumeAABB, mergedVolume: TreeNumber; + costS, costI, costLeft, costRight: TreeNumber; begin // if the tree is empty if (mRootNodeId = TTreeNode.NullTreeNode) then @@ -618,10 +628,10 @@ begin mergedVolume := mergedAABBs.volume; // compute the cost of making the current node the sibling of the new node - costS := 2.0*mergedVolume; + costS := 2*mergedVolume; // compute the minimum cost of pushing the new node further down the tree (inheritance cost) - costI := 2.0*(mergedVolume-volumeAABB); + costI := 2*(mergedVolume-volumeAABB); // compute the cost of descending into the left child currentAndLeftAABB := AABB2D.Create(newNodeAABB, mNodes[leftChild].aabb); @@ -806,7 +816,7 @@ begin balanceFactor := nodeC.height-nodeB.height; // if the right node C is 2 higher than left node B - if (balanceFactor > 1.0) then + if (balanceFactor > 1) then begin {$IFDEF aabbtree_many_asserts}assert(not nodeC.leaf);{$ENDIF} @@ -1056,11 +1066,19 @@ function TDynAABBTree.forEachLeaf (dg: TForEachLeafCB): Boolean; assert(pNode.height >= 0); if (not pNode.aabb.valid) then begin + {$IFDEF aabbtree_use_floats} 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); + {$ELSE} + 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); + {$ENDIF} if pNode.leaf then begin getFleshAABB(aabb, pNode.flesh); + {$IFDEF aabbtree_use_floats} 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); + {$ELSE} + 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); + {$ENDIF} end; end; assert(pNode.aabb.valid); @@ -1105,9 +1123,12 @@ end; // return `true` from visitor to stop immediately // checker should check if this node should be considered to further checking // returns tree node if visitor says stop or -1 -function TDynAABBTree.visit (checker: TVisitCheckerCB; visitor: TVisitVisitorCB): Integer; +const ModeNoChecks = 0; +const ModeAABB = 1; +const ModePoint = 2; +function TDynAABBTree.visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; tagmask: Integer=-1): Integer; var - stack: array [0..255] of Integer; // stack with the nodes to visit + stack: array [0..2048] of Integer; // stack with the nodes to visit bigstack: array of Integer = nil; sp: Integer = 0; @@ -1140,7 +1161,6 @@ var end; end; - (* function spop (): Integer; inline; begin {$IFDEF aabbtree_many_asserts}assert(sp > 0);{$ENDIF} @@ -1157,11 +1177,11 @@ var result := bigstack[sp-length(stack)]; end; end; - *) var nodeId: Integer; node: PTreeNode; + doNode: Boolean = false; begin if not assigned(checker) then begin result := -1; exit; end; //if not assigned(visitor) then begin result := -1; exit; end; @@ -1178,37 +1198,28 @@ begin while (sp > 0) do begin // get the next node id to visit - //nodeId := spop(); - {$IFDEF aabbtree_many_asserts}assert(sp > 0);{$ENDIF} - if (sp <= length(stack)) then - begin - // use "small stack" - Dec(sp); - nodeId := stack[sp]; - end - else - begin - // use "big stack" - Dec(sp); - nodeId := bigstack[sp-length(stack)]; - end; - + nodeId := spop(); // skip it if it is a nil node if (nodeId = TTreeNode.NullTreeNode) then continue; {$IFDEF aabbtree_query_count}Inc(mNodesVisited);{$ENDIF} // get the corresponding node node := @mNodes[nodeId]; // should we investigate this node? - if (checker(node)) then + case mode of + ModeNoChecks: doNode := checker(node); + ModeAABB: doNode := caabb.overlaps(node.aabb); + ModePoint: doNode := node.aabb.contains(caabb.minX, caabb.minY); + end; + if doNode then begin // if the node is a leaf if (node.leaf) then begin // call visitor on it {$IFDEF aabbtree_query_count}Inc(mNodesDeepVisited);{$ENDIF} - if assigned(visitor) then + if ((node.tag and tagmask) <> 0) and assigned(visitor) then begin - if (visitor(node.flesh)) then begin result := nodeId; exit; end; + if (visitor(node.flesh, node.tag)) then begin result := nodeId; exit; end; end; end else @@ -1229,7 +1240,7 @@ end; // add `extraAABBGap` to bounding boxes so slight object movement won't cause tree rebuilds // 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 -constructor TDynAABBTree.Create (extraAABBGap: Float=0.0); +constructor TDynAABBTree.Create (extraAABBGap: TreeNumber=0); begin mExtraGap := extraAABBGap; setup(); @@ -1287,21 +1298,29 @@ end; // this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error // AABB for static object will not be "fat" (simple optimization) // WARNING! inserting the same object several times *WILL* break everything! -function TDynAABBTree.insertObject (flesh: TTreeFlesh; staticObject: Boolean=false): Integer; +function TDynAABBTree.insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer; var aabb: AABB2D; nodeId: Integer; begin if not getFleshAABB(aabb, flesh) then begin + {$IFDEF aabbtree_use_floats} 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); + {$ELSE} + 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); + {$ENDIF} //raise Exception.Create('trying to insert invalid flesh in dyntree'); result := -1; exit; end; if not aabb.valid then begin + {$IFDEF aabbtree_use_floats} 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); + {$ELSE} + 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); + {$ENDIF} raise Exception.Create('trying to insert invalid aabb in dyntree'); result := -1; exit; @@ -1310,6 +1329,7 @@ begin nodeId := insertObjectInternal(aabb, staticObject); {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF} mNodes[nodeId].flesh := flesh; + mNodes[nodeId].tag := tag; result := nodeId; end; @@ -1325,7 +1345,7 @@ begin end; -function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: Float; forceReinsert: Boolean=false): Boolean; +function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; var newAABB: AABB2D; begin @@ -1351,21 +1371,21 @@ begin end; // inflate the fat AABB in direction of the linear motion of the AABB - if (dispX < 0.0) then + if (dispX < 0) then begin - mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX+LinearMotionGapMultiplier*dispX; + mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX+LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF}; end else begin - mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+LinearMotionGapMultiplier*dispX; + mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+LinearMotionGapMultiplier*dispX {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF}; end; - if (dispY < 0.0) then + if (dispY < 0) then begin - mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY+LinearMotionGapMultiplier*dispY; + mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY+LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF}; end else begin - mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+LinearMotionGapMultiplier*dispY; + mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+LinearMotionGapMultiplier*dispY {$IFDEF aabbtree_use_floats}{$ELSE}div 10{$ENDIF}; end; {$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].aabb.contains(newAABB));{$ENDIF} @@ -1378,53 +1398,60 @@ end; // report all shapes overlapping with the AABB given in parameter -function TDynAABBTree.aabbQuery (ax, ay, aw, ah: Float; cb: TQueryOverlapCB): Boolean; +function TDynAABBTree.aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh; var caabb: AABB2D; function checker (node: PTreeNode): Boolean; begin result := caabb.overlaps(node.aabb); end; +var + nid: Integer; begin + result := nil; if not assigned(cb) then exit; if (aw < 1) or (ah < 1) then exit; caabb := AABB2D.Create(ax, ay, ax+aw, ay+ah); - result := (visit(checker, cb) <> -1); + nid := visit(caabb, ModeAABB, checker, cb, tagmask); + if (nid >= 0) then result := mNodes[nid].flesh else result := nil; end; // report body that contains the given point, or nil -function TDynAABBTree.pointQuery (ax, ay: Float; cb: TQueryOverlapCB): TTreeFlesh; -var - nid: Integer; +function TDynAABBTree.pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB): TTreeFlesh; function checker (node: PTreeNode): Boolean; begin result := node.aabb.contains(ax, ay); end; +var + nid: Integer; + caabb: AABB2D; begin - nid := visit(checker, cb); + caabb := AABB2D.Create(ax, ay, ax+1, ay+1); + nid := visit(caabb, ModePoint, checker, cb); {$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mNodeCount) and (mNodes[nid].leaf)));{$ENDIF} if (nid >= 0) then result := mNodes[nid].flesh else result := nil; end; // segment querying method -function TDynAABBTree.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: Float; cb: TSegQueryCallback): Boolean; +function TDynAABBTree.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback): Boolean; var - maxFraction: Float = 1.0e100; // infinity - curax, curay: Float; - curbx, curby: Float; - dirx, diry: Float; - invlen: Float; + maxFraction: Single = 1.0e100; // infinity + curax, curay: Single; + curbx, curby: Single; + dirx, diry: Single; + invlen: Single; + caabb: AABB2D; function checker (node: PTreeNode): Boolean; begin result := node.aabb.intersects(curax, curay, curbx, curby); end; - function visitor (flesh: TTreeFlesh): Boolean; + function visitor (flesh: TTreeFlesh; tag: Integer): Boolean; var - hitFraction: Float; + hitFraction: Single; begin hitFraction := cb(flesh, curax, curay, curbx, curby); // if the user returned a hitFraction of zero, it means that the raycasting should stop here @@ -1470,7 +1497,8 @@ begin dirx *= invlen; diry *= invlen; - visit(checker, visitor); + caabb := AABB2D.Create(0, 0, 1, 1); + visit(caabb, ModeNoChecks, checker, visitor); result := qr.valid; end;