summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: da892f7)
raw | patch | inline | side by side (parent: da892f7)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Fri, 18 Aug 2017 15:31:56 +0000 (18:31 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Fri, 18 Aug 2017 18:29:25 +0000 (21:29 +0300) |
src/game/g_map.pas | patch | blob | history | |
src/game/z_aabbtree.pas | patch | blob | history |
diff --git a/src/game/g_map.pas b/src/game/g_map.pas
index c8d9f14b31f3cc352757d703b32bc996f61b659e..11a7d79459680c945d174ca81c4bb45c4618d279 100644 (file)
--- a/src/game/g_map.pas
+++ b/src/game/g_map.pas
pan: TPanel;
begin
pan := (flesh as TPanel);
- aabb.setXYWH(pan.X, pan.Y, pan.Width, pan.Height);
+ aabb.setDims(pan.X, pan.Y, pan.X+pan.Width-1, pan.Y+pan.Height-1);
result := true;
end;
gMapGrid.dumpStats();
e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY);
+ mapTree.forEachLeaf(nil);
end;
function g_Map_Load(Res: String): Boolean;
index 73f02f7cac9fb8faf88f7ce753ec5def78051f33..8260d9e82aea1122836db9da2e21624f4c312909 100644 (file)
--- a/src/game/z_aabbtree.pas
+++ b/src/game/z_aabbtree.pas
interface
+uses e_log;
+
+
// ////////////////////////////////////////////////////////////////////////// //
type
Float = Single;
dirX, dirY: Float;
public
- procedure normalizeDir ();
+ constructor Create (ax, ay: Float; aangle: Float); overload;
+ constructor Create (ax0, ay0, ax1, ay1: Float); 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;
function getextentY (): Float; inline;
public
- procedure setX0Y0X1Y1 (x0, y0, x1, y1: Float); inline;
- procedure setXYWH (ax, ay, aw, ah: Float); inline;
+ constructor Create (x0, y0, x1, y1: Float); overload;
+ constructor Create (const aabb: AABB2D); overload;
+ constructor Create (const aabb0, aabb1: AABB2D); overload;
- procedure setMergeTwo (var aabb0, aabb1: AABB2D); inline;
+ procedure copyFrom (const aabb: AABB2D); inline;
+ procedure setDims (x0, y0, x1, y1: Float); inline;
+
+ procedure setMergeTwo (const aabb0, aabb1: AABB2D); inline;
function volume (): Float; inline;
- procedure merge (var aabb: AABB2D); inline;
+ procedure merge (const aabb: AABB2D); inline;
// return true if the current AABB contains the AABB given in parameter
- function contains (var aabb: AABB2D): Boolean; inline; overload;
+ function contains (const aabb: AABB2D): Boolean; inline; overload;
function contains (ax, ay: Float): 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 (var aabb: AABB2D): Boolean; inline; overload;
+ function overlaps (const aabb: AABB2D): Boolean; inline; overload;
// ray direction must be normalized
- function intersects (var ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload;
- function intersects (ax, ay, bx, by: Float): Boolean; overload;
+ function intersects (const ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload;
+ function intersects (ax, ay, bx, by: Float): Boolean; inline; overload;
property valid: Boolean read getvalid;
property centerX: Float read getcenterX;
* based on the one from Erin Catto in Box2D as described in the book
* "Introduction to Game Physics with Box2D" by Ian Parberry.
*)
-type
- PDTProxyRec = ^TDTProxyRec;
- TDTProxyRec = record
- private
- mX, mY, mWidth, mHeight: Integer;
- mQueryMark: DWord; // was this object visited at this query?
- mObj: TObject;
- mTag: Integer;
- nextfree: Integer;
-
- private
- procedure setup (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
-
- function getx1 (): Integer; inline;
- function gety1 (): Integer; inline;
-
- public
- property x: Integer read mX;
- property y: Integer read mY;
- property width: Integer read mWidth;
- property height: Integer read mHeight;
- property x0: Integer read mX;
- property y0: Integer read mY;
- property x1: Integer read getx1;
- property y1: Integer read gety1;
- property obj: TObject read mObj;
- property tag: Integer read mTag;
- end;
-
// ////////////////////////////////////////////////////////////////////////// //
// Dynamic AABB Tree: can be used to speed up broad phase in various engines
type
aabb: AABB2D;
public
// return true if the node is a leaf of the tree
- procedure clear ();
+ procedure clear (); inline;
function leaf (): Boolean; inline;
function free (): Boolean; inline;
property nextNodeId: Integer read parentId write parentId;
// 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
- const LinearMotionGapMultiplier = Float(1.7);
+ const LinearMotionGapMultiplier = 1.7;
private
mNodes: array of TTreeNode; // nodes of the tree
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;
+
// ////////////////////////////////////////////////////////////////////////// //
-procedure TDTProxyRec.setup (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
-begin
- mX := aX;
- mY := aY;
- mWidth := aWidth;
- mHeight := aHeight;
- mQueryMark := 0;
- mObj := aObj;
- mTag := aTag;
- nextfree := -1;
-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 (const aray: Ray2D); overload; begin copyFrom(aray); end;
-function TDTProxyRec.getx1 (): Integer; begin result := mX+mWidth-1; end;
-function TDTProxyRec.gety1 (): Integer; begin result := mY+mHeight-1; end;
+procedure Ray2D.copyFrom (const aray: Ray2D); inline;
+begin
+ origX := aray.origX;
+ origY := aray.origY;
+ dirX := aray.dirX;
+ dirY := aray.dirY;
+end;
-// ////////////////////////////////////////////////////////////////////////// //
-procedure Ray2D.normalizeDir ();
+procedure Ray2D.normalizeDir (); inline;
var
invlen: Float;
begin
- invlen := Float(1.0)/sqrt(dirX*dirX+dirY*dirY);
+ invlen := 1.0/sqrt(dirX*dirX+dirY*dirY);
dirX *= invlen;
dirY *= invlen;
end;
-procedure Ray2D.setXYAngle (ax, ay: Float; aangle: Float);
+procedure Ray2D.setXYAngle (ax, ay: Float; aangle: Float); inline;
begin
origX := ax;
origY := ay;
dirY := sin(aangle);
end;
-procedure Ray2D.setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Float);
+procedure Ray2D.setX0Y0X1Y1 (ax0, ay0, ax1, ay1: Float); inline;
begin
origX := ax0;
origY := ay0;
// ////////////////////////////////////////////////////////////////////////// //
-function AABB2D.getvalid (): Boolean; begin result := (minX <= maxX) and (minY <= maxY); end;
-
-function AABB2D.getcenterX (): Float; begin result := (minX+maxX)/2; end;
-function AABB2D.getcenterY (): Float; begin result := (minY+maxY)/2; end;
-function AABB2D.getextentX (): Float; begin result := (maxX-minX)+1; end;
-function AABB2D.getextentY (): Float; begin result := (maxY-minY)+1; end;
+constructor AABB2D.Create (x0, y0, x1, y1: Float); overload;
+begin
+ setDims(x0, y0, x1, y1);
+end;
+constructor AABB2D.Create (const aabb: AABB2D); overload;
+begin
+ copyFrom(aabb);
+end;
-procedure AABB2D.setX0Y0X1Y1 (x0, y0, x1, y1: Float);
+constructor AABB2D.Create (const aabb0, aabb1: AABB2D); overload;
begin
- if (x0 < x1) then begin minX := x0; maxX := x1; end else begin minX := x1; maxX := x0; end;
- if (y0 < y1) then begin minY := y0; maxY := y1; end else begin minY := y1; maxY := y0; end;
+ setMergeTwo(aabb0, aabb1);
end;
+function AABB2D.getvalid (): Boolean; inline; begin result := (minX < maxX) and (minY < maxY); end;
-procedure AABB2D.setXYWH (ax, ay, aw, ah: Float);
+function AABB2D.getcenterX (): Float; inline; begin result := (minX+maxX)/2; end;
+function AABB2D.getcenterY (): Float; inline; begin result := (minY+maxY)/2; end;
+function AABB2D.getextentX (): Float; inline; begin result := (maxX-minX)+1; end;
+function AABB2D.getextentY (): Float; inline; begin result := (maxY-minY)+1; end;
+
+
+procedure AABB2D.copyFrom (const aabb: AABB2D); inline;
begin
- if (aw < 0) then aw := 0;
- if (ah < 0) then ah := 0;
- minX := ax;
- minY := ay;
- maxX := ax+aw-1;
- maxY := ay+ah-1;
+ minX := aabb.minX;
+ minY := aabb.minY;
+ maxX := aabb.maxX;
+ maxY := aabb.maxY;
end;
-procedure AABB2D.setMergeTwo (var aabb0, aabb1: AABB2D);
-var
- x0, y0, x1, y1: Float;
+procedure AABB2D.setDims (x0, y0, x1, y1: Float); inline;
begin
- if (aabb0.minX < aabb1.minX) then x0 := aabb0.minX else x0 := aabb1.minX;
- if (aabb0.minY < aabb1.minY) then y0 := aabb0.minY else y0 := aabb1.minY;
+ minX := minF(x0, x1);
+ minY := minF(y0, y1);
+ maxX := maxF(x0, x1);
+ maxY := maxF(y0, y1);
+end;
- if (aabb0.maxX > aabb1.maxX) then x1 := aabb0.maxX else x1 := aabb1.maxX;
- if (aabb0.maxY > aabb1.maxY) then y1 := aabb0.maxY else y1 := aabb1.maxY;
- minX := x0;
- minY := y0;
- maxX := x1;
- maxY := y1;
+procedure AABB2D.setMergeTwo (const aabb0, aabb1: AABB2D); inline;
+begin
+ minX := minF(aabb0.minX, aabb1.minX);
+ minY := minF(aabb0.minY, aabb1.minY);
+ maxX := maxF(aabb0.maxX, aabb1.maxX);
+ maxY := maxF(aabb0.maxY, aabb1.maxY);
end;
-function AABB2D.volume (): Float;
-var
- diffX, diffY: Float;
+function AABB2D.volume (): Float; inline;
begin
- diffX := maxX-minX;
- diffY := maxY-minY;
- result := diffX*diffY;
+ result := (maxX-minX)*(maxY-minY);
end;
-procedure AABB2D.merge (var aabb: AABB2D);
+procedure AABB2D.merge (const aabb: AABB2D); inline;
begin
- if (minX > aabb.minX) then minX := aabb.minX;
- if (minY > aabb.minY) then minY := aabb.minY;
- if (maxX < aabb.maxX) then maxX := aabb.maxX;
- if (maxY < aabb.maxY) then maxY := aabb.maxY;
+ minX := minF(minX, aabb.minX);
+ minY := minF(minY, aabb.minY);
+ maxX := maxF(maxX, aabb.maxX);
+ maxY := maxF(maxY, aabb.maxY);
end;
-function AABB2D.contains (var aabb: AABB2D): Boolean; overload;
+function AABB2D.contains (const aabb: AABB2D): Boolean; inline; overload;
begin
result :=
(aabb.minX >= minX) and (aabb.minY >= minY) and
end;
-function AABB2D.contains (ax, ay: Float): Boolean; overload;
+function AABB2D.contains (ax, ay: Float): Boolean; inline; overload;
begin
result := (ax >= minX) and (ay >= minY) and (ax <= maxX) and (ay <= maxY);
end;
-function AABB2D.overlaps (var aabb: AABB2D): Boolean; overload;
+function AABB2D.overlaps (const aabb: AABB2D): Boolean; inline; overload;
begin
result := false;
// exit with no intersection if found separated along any axis
// 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 (var ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload;
+function AABB2D.intersects (const ray: Ray2D; tmino: PFloat=nil; tmaxo: PFloat=nil): Boolean; overload;
var
dinv, t1, t2, tmp: Float;
tmin, tmax: Float;
// do X
if (ray.dirX <> 0.0) then
begin
- dinv := Float(1.0)/ray.dirX;
+ dinv := 1.0/ray.dirX;
t1 := (minX-ray.origX)*dinv;
t2 := (maxX-ray.origX)*dinv;
if (t1 < t2) then tmin := t1 else tmin := t2;
// do Y
if (ray.dirY <> 0.0) then
begin
- dinv := Float(1.0)/ray.dirY;
+ dinv := 1.0/ray.dirY;
t1 := (minY-ray.origY)*dinv;
t2 := (maxY-ray.origY)*dinv;
// tmin
end;
end;
-function AABB2D.intersects (ax, ay, bx, by: Float): Boolean; overload;
+function AABB2D.intersects (ax, ay, bx, by: Float): Boolean; inline; overload;
var
tmin: Float;
ray: Ray2D;
if (ax >= minX) and (ay >= minY) and (ax <= maxX) and (ay <= maxY) then exit; // a
if (bx >= minX) and (by >= minY) and (bx <= maxX) and (by <= maxY) then exit; // b
// nope, do it hard way
- ray.setX0Y0X1Y1(ax, ay, bx, by);
+ ray := Ray2D.Create(ax, ay, bx, by);
if not intersects(ray, @tmin) then begin result := false; exit; end;
if (tmin < 0) then exit; // inside, just in case
bx := bx-ax;
// ////////////////////////////////////////////////////////////////////////// //
-procedure TDynAABBTree.TSegmentQueryResult.reset (); begin dist := -1; flesh := nil; end;
-function TDynAABBTree.TSegmentQueryResult.valid (): Boolean; begin result := (dist >= 0) and (flesh <> nil); end;
+procedure TDynAABBTree.TSegmentQueryResult.reset (); inline; begin dist := -1; flesh := nil; end;
+function TDynAABBTree.TSegmentQueryResult.valid (): Boolean; inline; begin result := (dist >= 0) and (flesh <> nil); end;
// ////////////////////////////////////////////////////////////////////////// //
-function TDynAABBTree.TTreeNode.leaf (): Boolean; begin result := (height = 0); end;
-function TDynAABBTree.TTreeNode.free (): Boolean; begin result := (height = -1); end;
+function TDynAABBTree.TTreeNode.leaf (): Boolean; inline; begin result := (height = 0); end;
+function TDynAABBTree.TTreeNode.free (): Boolean; inline; begin result := (height = -1); end;
-procedure TDynAABBTree.TTreeNode.clear ();
+procedure TDynAABBTree.TTreeNode.clear (); inline;
begin
parentId := 0;
children[0] := 0;
children[1] := 0;
flesh := nil;
height := 0;
- aabb.setX0Y0X1Y1(0, 0, 0, 0);
+ //aabb.setX0Y0X1Y1(0, 0, 0, 0);
+ aabb.minX := 0;
+ aabb.minY := 0;
+ aabb.maxX := -1;
+ aabb.maxY := -1;
end;
function TDynAABBTree.allocateNode (): Integer;
var
i, newsz, freeNodeId: Integer;
+ node: PTreeNode;
begin
// if there is no more allocated node to use
if (mFreeNodeId = TTreeNode.NullTreeNode) then
// get the next free node
freeNodeId := mFreeNodeId;
{$IFDEF aabbtree_many_asserts}assert((freeNodeId >= mNodeCount) and (freeNodeId < mAllocCount));{$ENDIF}
- mFreeNodeId := mNodes[freeNodeId].nextNodeId;
- mNodes[freeNodeId].parentId := TTreeNode.NullTreeNode;
- mNodes[freeNodeId].height := 0;
+ node := @mNodes[freeNodeId];
+ mFreeNodeId := node.nextNodeId;
+ node.clear();
+ node.parentId := TTreeNode.NullTreeNode;
+ node.height := 0;
Inc(mNodeCount);
result := freeNodeId;
end;
// compute the merged AABB
volumeAABB := mNodes[currentNodeId].aabb.volume;
- mergedAABBs.setMergeTwo(mNodes[currentNodeId].aabb, newNodeAABB);
+ mergedAABBs := AABB2D.Create(mNodes[currentNodeId].aabb, newNodeAABB);
mergedVolume := mergedAABBs.volume;
// compute the cost of making the current node the sibling of the new node
- costS := Float(2.0)*mergedVolume;
+ costS := 2.0*mergedVolume;
// compute the minimum cost of pushing the new node further down the tree (inheritance cost)
- costI := Float(2.0)*(mergedVolume-volumeAABB);
+ costI := 2.0*(mergedVolume-volumeAABB);
// compute the cost of descending into the left child
- currentAndLeftAABB.setMergeTwo(newNodeAABB, mNodes[leftChild].aabb);
- if (mNodes[leftChild].leaf) then
- begin
- costLeft := currentAndLeftAABB.volume+costI;
- end
- else
- begin
- costLeft := costI+currentAndLeftAABB.volume-mNodes[leftChild].aabb.volume;
- end;
+ currentAndLeftAABB := AABB2D.Create(newNodeAABB, mNodes[leftChild].aabb);
+ costLeft := currentAndLeftAABB.volume+costI;
+ if not mNodes[leftChild].leaf then costLeft := costLeft-mNodes[leftChild].aabb.volume;
// compute the cost of descending into the right child
- currentAndRightAABB.setMergeTwo(newNodeAABB, mNodes[rightChild].aabb);
- if (mNodes[rightChild].leaf) then
- begin
- costRight := currentAndRightAABB.volume+costI;
- end
- else
- begin
- costRight := costI+currentAndRightAABB.volume-mNodes[rightChild].aabb.volume;
- end;
+ currentAndRightAABB := AABB2D.Create(newNodeAABB, mNodes[rightChild].aabb);
+ costRight := currentAndRightAABB.volume+costI;
+ if not mNodes[rightChild].leaf then costRight := costRight-mNodes[rightChild].aabb.volume;
// 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
if (costS < costLeft) and (costS < costRight) then break;
balanceFactor := nodeC.height-nodeB.height;
// if the right node C is 2 higher than left node B
- if (balanceFactor > 1) then
+ if (balanceFactor > 1.0) then
begin
{$IFDEF aabbtree_many_asserts}assert(not nodeC.leaf);{$ENDIF}
mNodes[nodeId].aabb := aabb;
if (not staticObject) then
begin
- mNodes[nodeId].aabb.minX := mNodes[nodeId].aabb.minX-mExtraGap;
- mNodes[nodeId].aabb.minY := mNodes[nodeId].aabb.minY-mExtraGap;
- mNodes[nodeId].aabb.maxX := mNodes[nodeId].aabb.maxX+mExtraGap;
- mNodes[nodeId].aabb.maxY := mNodes[nodeId].aabb.maxY+mExtraGap;
+ mNodes[nodeId].aabb.minX -= mExtraGap;
+ mNodes[nodeId].aabb.minY -= mExtraGap;
+ mNodes[nodeId].aabb.maxX += mExtraGap;
+ mNodes[nodeId].aabb.maxY += mExtraGap;
end;
// set the height of the node in the tree
// get the children nodes
pNode := @mNodes[nodeId];
assert(pNode.height >= 0);
+ e_WriteLog(Format('AABB:(%f,%f)-(%f,%f); volume=%f; valid=%d', [pNode.aabb.minX, pNode.aabb.minY, pNode.aabb.maxX, pNode.aabb.maxY, pNode.aabb.volume, Integer(pNode.aabb.valid)]), MSG_NOTIFY);
+ assert(pNode.aabb.valid);
assert(pNode.aabb.volume > 0);
// if the current node is a leaf
if (pNode.leaf) then
begin
assert(pNode.height = 0);
- result := dg(pNode.flesh, pNode.aabb);
+ if assigned(dg) then result := dg(pNode.flesh, pNode.aabb);
end
else
begin
height := 1+maxI(mNodes[leftChild].height, mNodes[rightChild].height);
assert(mNodes[nodeId].height = height);
// check the AABB of the node
- aabb.setMergeTwo(mNodes[leftChild].aabb, mNodes[rightChild].aabb);
+ aabb := AABB2D.Create(mNodes[leftChild].aabb, mNodes[rightChild].aabb);
assert(aabb.minX = mNodes[nodeId].aabb.minX);
assert(aabb.minY = mNodes[nodeId].aabb.minY);
assert(aabb.maxX = mNodes[nodeId].aabb.maxX);
end;
begin
- result := false;
- if not assigned(dg) then exit;
// recursively check each node
result := forEachNode(mRootNodeId);
end;
// get fat object AABB by nodeid; returns random shit for invalid ids
procedure TDynAABBTree.getNodeFatAABB (var aabb: AABB2D; nodeid: Integer);
begin
- if (nodeid >= 0) and (nodeid < mNodeCount) and (not mNodes[nodeid].free) then aabb := mNodes[nodeid].aabb else aabb.setX0Y0X1Y1(0, 0, -1, -1);
+ if (nodeid >= 0) and (nodeid < mNodeCount) and (not mNodes[nodeid].free) then aabb.copyFrom(mNodes[nodeid].aabb) else aabb.setDims(0, 0, 0, 0);
end;
aabb: AABB2D;
nodeId: Integer;
begin
- if not getFleshAABB(aabb, flesh) then begin result := -1; exit; end;
+ if not getFleshAABB(aabb, flesh) then
+ begin
+ 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);
+ result := -1;
+ exit;
+ end;
+ 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);
nodeId := insertObjectInternal(aabb, staticObject);
{$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].leaf);{$ENDIF}
mNodes[nodeId].flesh := flesh;
end;
begin
if not assigned(cb) then exit;
- caabb.setXYWH(ax, ay, aw, ah);
+ if (aw < 1) or (ah < 1) then exit;
+ caabb := AABB2D.Create(ax, ay, ax+aw-1, ay+ah-1);
visit(checker, cb);
end;
dirx := (curbx-curax);
diry := (curby-curay);
// normalize
- invlen := Float(1.0)/sqrt(dirx*dirx+diry*diry);
+ invlen := 1.0/sqrt(dirx*dirx+diry*diry);
dirx *= invlen;
diry *= invlen;