summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 46bfd9c)
raw | patch | inline | side by side (parent: 46bfd9c)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Fri, 18 Aug 2017 17:45:19 +0000 (20:45 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Fri, 18 Aug 2017 18:29:57 +0000 (21:29 +0300) |
src/game/z_aabbtree.pas | patch | blob | history |
index 19a5c7474f29ac4e319d7b524aef51dffd99a094..a168a6028e2e319be6d7c1c766dcd9283a5142a7 100644 (file)
--- a/src/game/z_aabbtree.pas
+++ b/src/game/z_aabbtree.pas
{$INCLUDE ../shared/a_modes.inc}
{.$DEFINE aabbtree_many_asserts}
{.$DEFINE aabbtree_query_count}
+{.$DEFINE aabbtree_use_floats}
unit z_aabbtree;
interface
// ////////////////////////////////////////////////////////////////////////// //
type
- Float = Single;
- PFloat = ^Float;
+ {$IFDEF aabbtree_use_floats}Float = Single;{$ELSE}Float = Integer;{$ENDIF}
+ //PFloat = ^Float;
TTreeFlesh = TObject;
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;
// ////////////////////////////////////////////////////////////////////////// //
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;
// 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
// 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: Float): Float is nested; // return dist from (ax,ay) to abody
+ type TSegQueryCallback = function (abody: TTreeFlesh; ax, ay, bx, by: Single): Single is nested; // return dist from (ax,ay) to abody
TSegmentQueryResult = record
- dist: Float; // <0: nothing was hit
+ dist: Single; // <0: nothing was hit
flesh: TTreeFlesh;
procedure reset (); inline;
{$ENDIF}
public
- constructor Create (extraAABBGap: Float=0.0);
+ constructor Create (extraAABBGap: Float=0);
destructor Destroy (); override;
// clear all the nodes and reset the tree
@@ -295,8 +300,8 @@ function maxF (a, b: Float): Float; inline; begin if (a > b) then result := a el
// ////////////////////////////////////////////////////////////////////////// //
-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;
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;
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;
function AABB2D.getvalid (): Boolean; inline; begin result := (minX < maxX) and (minY < maxY); end;
+{$IFDEF aabbtree_use_floats}
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;
-
+{$ELSE}
+function AABB2D.getcenterX (): Float; inline; begin result := (minX+maxX) div 2; end;
+function AABB2D.getcenterY (): Float; inline; begin result := (minY+maxY) div 2; end;
+{$ENDIF}
+function AABB2D.getextentX (): Float; inline; begin result := (maxX-minX); end;
+function AABB2D.getextentY (): Float; inline; begin result := (maxY-minY); end;
procedure AABB2D.copyFrom (const aabb: AABB2D); inline;
begin
// 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;
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;
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);
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}
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);
// 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: Float=0);
begin
mExtraGap := extraAABBGap;
setup();
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;
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}
// segment querying method
function TDynAABBTree.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: Float; 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;
function checker (node: PTreeNode): Boolean;
begin
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