summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: cac277b)
raw | patch | inline | side by side (parent: cac277b)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Sat, 19 Aug 2017 19:07:36 +0000 (22:07 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Sat, 19 Aug 2017 19:17:13 +0000 (22:17 +0300) |
src/game/g_grid.pas | patch | blob | history | |
src/game/g_items.pas | patch | blob | history | |
src/game/g_map.pas | patch | blob | history | |
src/game/z_aabbtree.pas | patch | blob | history |
diff --git a/src/game/g_grid.pas b/src/game/g_grid.pas
index 81974570c4e201bd52d550e2b556fed261e8ffd6..d1149adf46c64d47c5790f5731c2ea56e90ea278 100644 (file)
--- a/src/game/g_grid.pas
+++ b/src/game/g_grid.pas
interface
-const
- GridDefaultTileSize = 32;
- GridCellBucketSize = 8; // WARNING! can't be less than 2!
type
- TGridQueryCB = function (obj: TObject; tag: Integer): Boolean is nested; // return `true` to stop
+ TBodyProxyId = Integer;
-type
- TBodyGrid = class;
-
- TBodyProxy = Integer;
+ generic TBodyGridBase<ITP> = class(TObject)
+ public
+ type TGridQueryCB = function (obj: ITP; tag: Integer): Boolean is nested; // return `true` to stop
- PBodyProxyRec = ^TBodyProxyRec;
- TBodyProxyRec = record
private
- mX, mY, mWidth, mHeight: Integer; // aabb
- mQueryMark: DWord; // was this object visited at this query?
- mObj: TObject;
- //mGrid: TBodyGrid;
- mTag: Integer;
- nextLink: TBodyProxy; // next free or nothing
+ const
+ GridDefaultTileSize = 32;
+ GridCellBucketSize = 8; // WARNING! can't be less than 2!
private
- procedure setup (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
-
- public
- //constructor Create (aGrid: TBodyGrid; aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
- //destructor Destroy (); override;
-
- property x: Integer read mX;
- property y: Integer read mY;
- property width: Integer read mWidth;
- property height: Integer read mHeight;
- property obj: TObject read mObj;
- property tag: Integer read mTag;
- //property grid: TBodyGrid read mGrid;
- end;
+ type
+ PBodyProxyRec = ^TBodyProxyRec;
+ TBodyProxyRec = record
+ private
+ mX, mY, mWidth, mHeight: Integer; // aabb
+ mQueryMark: DWord; // was this object visited at this query?
+ mObj: ITP;
+ mTag: Integer;
+ nextLink: TBodyProxyId; // next free or nothing
+
+ private
+ procedure setup (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer);
+ end;
- PGridCell = ^TGridCell;
- TGridCell = record
- {$IFDEF grid_use_buckets}
- bodies: array [0..GridCellBucketSize-1] of Integer; // -1: end of list
- {$ELSE}
- body: Integer;
- {$ENDIF}
- next: Integer; // in this cell; index in mCells
- end;
+ PGridCell = ^TGridCell;
+ TGridCell = record
+ {$IFDEF grid_use_buckets}
+ bodies: array [0..GridCellBucketSize-1] of Integer; // -1: end of list
+ {$ELSE}
+ body: Integer;
+ {$ENDIF}
+ next: Integer; // in this cell; index in mCells
+ end;
- TGridInternalCB = function (grida: Integer): Boolean is nested; // return `true` to stop
+ TGridInternalCB = function (grida: Integer): Boolean of object; // return `true` to stop
- TBodyGrid = class(TObject)
private
mTileSize: Integer;
mMinX, mMinY: Integer; // so grids can start at any origin
mLastQuery: DWord;
mUsedCells: Integer;
mProxies: array of TBodyProxyRec;
- mProxyFree: TBodyProxy; // free
+ mProxyFree: TBodyProxyId; // free
mProxyCount: Integer; // currently used
mProxyMaxCount: Integer;
+ mUData: TBodyProxyId; // for inserter/remover
+ mTagMask: Integer; // for iterator
+ mItCB: TGridQueryCB; // for iterator
+
private
function allocCell: Integer;
procedure freeCell (idx: Integer); // `next` is simply overwritten
- function allocProxy (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer): TBodyProxy;
- procedure freeProxy (body: TBodyProxy);
+ function allocProxy (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer): TBodyProxyId;
+ procedure freeProxy (body: TBodyProxyId);
- procedure insert (body: TBodyProxy);
- procedure remove (body: TBodyProxy);
+ procedure insert (body: TBodyProxyId);
+ procedure remove (body: TBodyProxyId);
function forGridRect (x, y, w, h: Integer; cb: TGridInternalCB): Boolean;
+ function inserter (grida: Integer): Boolean;
+ function remover (grida: Integer): Boolean;
+ function iterator (grida: Integer): Boolean;
+
public
constructor Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer; aTileSize: Integer=GridDefaultTileSize);
destructor Destroy (); override;
- function insertBody (aObj: TObject; ax, ay, aWidth, aHeight: Integer; aTag: Integer=0): TBodyProxy;
- procedure removeBody (aObj: TBodyProxy); // WARNING! this WILL destroy proxy!
+ function insertBody (aObj: ITP; ax, ay, aWidth, aHeight: Integer; aTag: Integer=0): TBodyProxyId;
+ procedure removeBody (aObj: TBodyProxyId); // WARNING! this WILL destroy proxy!
- procedure moveBody (body: TBodyProxy; dx, dy: Integer);
- procedure resizeBody (body: TBodyProxy; sx, sy: Integer);
- procedure moveResizeBody (body: TBodyProxy; dx, dy, sx, sy: Integer);
+ procedure moveBody (body: TBodyProxyId; dx, dy: Integer);
+ procedure resizeBody (body: TBodyProxyId; sx, sy: Integer);
+ procedure moveResizeBody (body: TBodyProxyId; dx, dy, sx, sy: Integer);
function forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
- //function getProxyForBody (aObj: TObject; x, y, w, h: Integer): TBodyProxy;
-
procedure dumpStats ();
end;
// ////////////////////////////////////////////////////////////////////////// //
-procedure TBodyProxyRec.setup (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer);
+procedure TBodyGridBase.TBodyProxyRec.setup (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer);
begin
mX := aX;
mY := aY;
// ////////////////////////////////////////////////////////////////////////// //
-constructor TBodyGrid.Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer; aTileSize: Integer=GridDefaultTileSize);
+constructor TBodyGridBase.Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer; aTileSize: Integer=GridDefaultTileSize);
var
idx: Integer;
begin
mProxyFree := 0;
mProxyCount := 0;
mProxyMaxCount := 0;
+ mUData := 0;
+ mTagMask := 0;
+ mItCB := nil;
e_WriteLog(Format('created grid with size: %dx%d (tile size: %d); pix: %dx%d', [mWidth, mHeight, mTileSize, mWidth*mTileSize, mHeight*mTileSize]), MSG_NOTIFY);
end;
-destructor TBodyGrid.Destroy ();
+destructor TBodyGridBase.Destroy ();
begin
mCells := nil;
mGrid := nil;
end;
-procedure TBodyGrid.dumpStats ();
+procedure TBodyGridBase.dumpStats ();
var
idx, mcb, cidx, cnt: Integer;
begin
end;
-function TBodyGrid.allocCell: Integer;
+function TBodyGridBase.allocCell: Integer;
var
idx: Integer;
begin
end;
-procedure TBodyGrid.freeCell (idx: Integer);
+procedure TBodyGridBase.freeCell (idx: Integer);
begin
if (idx >= 0) and (idx < High(mCells)) then
begin
end;
-function TBodyGrid.allocProxy (aX, aY, aWidth, aHeight: Integer; aObj: TObject; aTag: Integer): TBodyProxy;
+function TBodyGridBase.allocProxy (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer): TBodyProxyId;
var
olen, idx: Integer;
px: PBodyProxyRec;
if (mProxyMaxCount < mProxyCount) then mProxyMaxCount := mProxyCount;
end;
-procedure TBodyGrid.freeProxy (body: TBodyProxy);
+procedure TBodyGridBase.freeProxy (body: TBodyProxyId);
begin
if (body < 0) or (body > High(mProxies)) then exit; // just in case
if (mProxyCount = 0) then raise Exception.Create('wutafuuuuu in grid (no allocated proxies, what i should free now?)');
end;
-function TBodyGrid.forGridRect (x, y, w, h: Integer; cb: TGridInternalCB): Boolean;
+function TBodyGridBase.forGridRect (x, y, w, h: Integer; cb: TGridInternalCB): Boolean;
var
gx, gy: Integer;
begin
end;
-procedure TBodyGrid.insert (body: TBodyProxy);
-
- function inserter (grida: Integer): Boolean;
- var
- cidx: Integer;
- pc: PInteger;
- {$IFDEF grid_use_buckets}
- pi: PGridCell;
- f: Integer;
- {$ENDIF}
+function TBodyGridBase.inserter (grida: Integer): Boolean;
+var
+ cidx: Integer;
+ pc: PInteger;
+ {$IFDEF grid_use_buckets}
+ pi: PGridCell;
+ f: Integer;
+ {$ENDIF}
+begin
+ result := false; // never stop
+ // add body to the given grid cell
+ pc := @mGrid[grida];
+ {$IFDEF grid_use_buckets}
+ if (pc^ <> -1) then
begin
- result := false; // never stop
- // add body to the given grid cell
- pc := @mGrid[grida];
- {$IFDEF grid_use_buckets}
- if (pc^ <> -1) then
+ pi := @mCells[pc^];
+ f := 0;
+ for f := 0 to High(TGridCell.bodies) do
begin
- pi := @mCells[pc^];
- f := 0;
- for f := 0 to High(TGridCell.bodies) do
+ if (pi.bodies[f] = -1) then
begin
- if (pi.bodies[f] = -1) then
- begin
- // can add here
- pi.bodies[f] := body;
- if (f+1 < Length(TGridCell.bodies)) then pi.bodies[f+1] := -1;
- exit;
- end;
+ // can add here
+ pi.bodies[f] := mUData;
+ if (f+1 < Length(TGridCell.bodies)) then pi.bodies[f+1] := -1;
+ exit;
end;
end;
- // either no room, or no cell at all
- cidx := allocCell();
- mCells[cidx].bodies[0] := body;
- mCells[cidx].bodies[1] := -1;
- mCells[cidx].next := pc^;
- pc^ := cidx;
- {$ELSE}
- cidx := allocCell();
- //e_WriteLog(Format(' 01: allocated cell for grid coords (%d,%d), body coords:(%d,%d): #%d', [gx, gy, dx, dy, cidx]), MSG_NOTIFY);
- mCells[cidx].body := body;
- mCells[cidx].next := pc^;
- pc^ := cidx;
- {$ENDIF}
end;
+ // either no room, or no cell at all
+ cidx := allocCell();
+ mCells[cidx].bodies[0] := mUData;
+ mCells[cidx].bodies[1] := -1;
+ mCells[cidx].next := pc^;
+ pc^ := cidx;
+ {$ELSE}
+ cidx := allocCell();
+ //e_WriteLog(Format(' 01: allocated cell for grid coords (%d,%d), body coords:(%d,%d): #%d', [gx, gy, dx, dy, cidx]), MSG_NOTIFY);
+ mCells[cidx].body := mUData;
+ mCells[cidx].next := pc^;
+ pc^ := cidx;
+ {$ENDIF}
+end;
+
+procedure TBodyGridBase.insert (body: TBodyProxyId);
var
px: PBodyProxyRec;
+ oudata: Integer;
begin
if (body < 0) or (body > High(mProxies)) then exit; // just in case
px := @mProxies[body];
+ oudata := mUData;
+ mUData := body;
forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, inserter);
+ mUData := oudata;
end;
-// absolutely not tested
-procedure TBodyGrid.remove (body: TBodyProxy);
-
-(*
- function remover (grida: Integer): Boolean;
- var
- pidx, idx, tmp: Integer;
+function TBodyGridBase.remover (grida: Integer): Boolean;
+var
+ pidx, idx, tmp, f: Integer;
+ pc: PGridCell;
+begin
+ result := false; // never stop
+ // find and remove cell
+ pidx := -1;
+ idx := mGrid[grida];
+ while (idx >= 0) do
begin
- result := false; // never stop
- // find and remove cell
- pidx := -1;
- idx := mGrid[grida];
- while idx >= 0 do
+ tmp := mCells[idx].next;
+ {$IFDEF grid_use_buckets}
+ pc := @mCells[idx];
+ f := 0;
+ while (f < High(TGridCell.bodies)) do
begin
- tmp := mCells[idx].next;
- if (mCells[idx].body = body) then
+ if (pc.bodies[f] = mUData) then
begin
- if (pidx = -1) then mGrid[grida] := tmp else mCells[pidx].next := tmp;
- freeCell(idx);
- break; // assume that we cannot have one object added to bucket twice
- end
- else
- begin
- pidx := idx;
+ // i found her!
+ if (f = 0) and (pc.bodies[1] = -1) then
+ begin
+ // this cell contains no elements, remove it
+ tmp := mCells[idx].next;
+ if (pidx = -1) then mGrid[grida] := tmp else mCells[pidx].next := tmp;
+ freeCell(idx);
+ end
+ else
+ begin
+ // remove element from bucket
+ Inc(f);
+ while (f < High(TGridCell.bodies)) do
+ begin
+ pc.bodies[f-1] := pc.bodies[f];
+ if (pc.bodies[f] = -1) then break;
+ Inc(f);
+ end;
+ pc.bodies[High(TGridCell.bodies)] := -1; // just in case
+ end;
+ exit; // assume that we cannot have one object added to bucket twice
end;
- idx := tmp;
+ Inc(f);
+ end;
+ {$ELSE}
+ if (mCells[idx].body = mUData) then
+ begin
+ if (pidx = -1) then mGrid[grida] := tmp else mCells[pidx].next := tmp;
+ freeCell(idx);
+ exit; // assume that we cannot have one object added to bucket twice
end;
+ {$ENDIF}
+ pidx := idx;
+ idx := tmp;
end;
+end;
+
+// absolutely not tested
+procedure TBodyGridBase.remove (body: TBodyProxyId);
var
px: PBodyProxyRec;
-*)
+ oudata: Integer;
begin
-(*
if (body < 0) or (body > High(mProxies)) then exit; // just in case
px := @mProxies[body];
+ oudata := mUData;
+ mUData := body;
forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, remover);
-*)
- raise Exception.Create('TBodyGrid.remove: not yet, sorry');
+ mUData := oudata;
end;
-function TBodyGrid.insertBody (aObj: TObject; aX, aY, aWidth, aHeight: Integer; aTag: Integer=0): TBodyProxy;
+function TBodyGridBase.insertBody (aObj: ITP; aX, aY, aWidth, aHeight: Integer; aTag: Integer=0): TBodyProxyId;
begin
result := allocProxy(aX, aY, aWidth, aHeight, aObj, aTag);
insert(result);
end;
-procedure TBodyGrid.removeBody (aObj: TBodyProxy);
+procedure TBodyGridBase.removeBody (aObj: TBodyProxyId);
begin
if (aObj < 0) or (aObj > High(mProxies)) then exit; // just in case
- removeBody(aObj);
+ remove(aObj);
freeProxy(aObj);
end;
-procedure TBodyGrid.moveResizeBody (body: TBodyProxy; dx, dy, sx, sy: Integer);
+procedure TBodyGridBase.moveResizeBody (body: TBodyProxyId; dx, dy, sx, sy: Integer);
var
px: PBodyProxyRec;
begin
insert(body);
end;
-procedure TBodyGrid.moveBody (body: TBodyProxy; dx, dy: Integer);
+procedure TBodyGridBase.moveBody (body: TBodyProxyId; dx, dy: Integer);
begin
moveResizeBody(body, dx, dy, 0, 0);
end;
-procedure TBodyGrid.resizeBody (body: TBodyProxy; sx, sy: Integer);
+procedure TBodyGridBase.resizeBody (body: TBodyProxyId; sx, sy: Integer);
begin
moveResizeBody(body, 0, 0, sx, sy);
end;
-function TBodyGrid.forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
- function iterator (grida: Integer): Boolean;
- var
- idx: Integer;
- px: PBodyProxyRec;
- {$IFDEF grid_use_buckets}
- pi: PGridCell;
- f: Integer;
- {$ENDIF}
+function TBodyGridBase.iterator (grida: Integer): Boolean;
+var
+ idx: Integer;
+ px: PBodyProxyRec;
+ {$IFDEF grid_use_buckets}
+ pi: PGridCell;
+ f: Integer;
+ {$ENDIF}
+begin
+ result := false;
+ idx := mGrid[grida];
+ while (idx >= 0) do
begin
- result := false;
- idx := mGrid[grida];
- while (idx >= 0) do
+ {$IFDEF grid_use_buckets}
+ pi := @mCells[idx];
+ for f := 0 to High(TGridCell.bodies) do
begin
- {$IFDEF grid_use_buckets}
- pi := @mCells[idx];
- for f := 0 to High(TGridCell.bodies) do
+ if (pi.bodies[f] = -1) then break;
+ px := @mProxies[pi.bodies[f]];
+ if (px.mQueryMark <> mLastQuery) and ((px.mTag and mTagMask) <> 0) then
begin
- if (pi.bodies[f] = -1) then break;
- px := @mProxies[pi.bodies[f]];
- if (px.mQueryMark <> mLastQuery) and ((px.mTag and tagmask) <> 0) then
- begin
- //e_WriteLog(Format(' query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY);
- px.mQueryMark := mLastQuery;
- if (cb(px.mObj, px.mTag)) then begin result := true; exit; end;
- end;
+ //e_WriteLog(Format(' query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY);
+ px.mQueryMark := mLastQuery;
+ if (mItCB(px.mObj, px.mTag)) then begin result := true; exit; end;
end;
- idx := pi.next;
- {$ELSE}
- if (mCells[idx].body <> -1) then
+ end;
+ idx := pi.next;
+ {$ELSE}
+ if (mCells[idx].body <> -1) then
+ begin
+ px := @mProxies[mCells[idx].body];
+ if (px.mQueryMark <> mLastQuery) and ((px.mTag and mTagMask) <> 0) then
begin
- px := @mProxies[mCells[idx].body];
- if (px.mQueryMark <> mLastQuery) and ((px.mTag and tagmask) <> 0) then
- begin
- //e_WriteLog(Format(' query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY);
- px.mQueryMark := mLastQuery;
- if (cb(px.mObj, px.mTag)) then begin result := true; exit; end;
- end;
+ //e_WriteLog(Format(' query #%d body hit: (%d,%d)-(%dx%d) tag:%d', [mLastQuery, mCells[idx].body.mX, mCells[idx].body.mY, mCells[idx].body.mWidth, mCells[idx].body.mHeight, mCells[idx].body.mTag]), MSG_NOTIFY);
+ px.mQueryMark := mLastQuery;
+ if (mItCB(px.mObj, px.mTag)) then begin result := true; exit; end;
end;
- idx := mCells[idx].next;
- {$ENDIF}
end;
+ idx := mCells[idx].next;
+ {$ENDIF}
end;
+end;
+function TBodyGridBase.forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
var
idx: Integer;
+ otagmask: Integer;
+ ocb: TGridQueryCB;
begin
result := false;
if not assigned(cb) then exit;
end;
//e_WriteLog(Format('grid: query #%d: (%d,%d)-(%dx%d)', [mLastQuery, minx, miny, maxx, maxy]), MSG_NOTIFY);
+ otagmask := mTagMask;
+ mTagMask := tagmask;
+ ocb := mItCB;
+ mItCB := cb;
result := forGridRect(x, y, w, h, iterator);
+ mTagMask := otagmask;
+ mItCB := ocb;
end;
-(*
-function TBodyGrid.getProxyForBody (aObj: TObject; x, y, w, h: Integer): TBodyProxy;
-var
- res: TBodyProxy = -1;
-
- function iterator (grida: Integer): Boolean;
- var
- idx: Integer;
- begin
- result := false;
- idx := mGrid[grida];
- while idx >= 0 do
- begin
- if (mCells[idx].body <> -1) and (mProxies[mCells[idx].body].mObj = aObj) then
- begin
- result := true;
- res := mCells[idx].body;
- exit;
- end;
- idx := mCells[idx].next;
- end;
- end;
-
-begin
- result := -1;
- if (aObj = nil) then exit;
- forGridRect(x, y, w, h, iterator);
- result := res;
-end;
-*)
-
-
end.
diff --git a/src/game/g_items.pas b/src/game/g_items.pas
index 6d7cf5a10a7918ea8bc0480160d33c9769c0099e..4950e4c4b9a1412599f1b3e098f65d8f1f13b0e9 100644 (file)
--- a/src/game/g_items.pas
+++ b/src/game/g_items.pas
g_grid, z_aabbtree, binheap;
-// ////////////////////////////////////////////////////////////////////////// //
var
- itemTree: TDynAABBTree = nil;
ggItems: Array of TItem = nil;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+type
+ TDynAABBTreeItemBase = specialize TDynAABBTreeBase<Integer>;
+
+ TDynAABBTreeItem = class(TDynAABBTreeItemBase)
+ function getFleshAABB (out aabb: AABB2D; flesh: Integer; tag: Integer): Boolean; override;
+ end;
+
+function TDynAABBTreeItem.getFleshAABB (out aabb: AABB2D; flesh: Integer; tag: Integer): Boolean;
+var
+ it: PItem;
+begin
+ result := false;
+ if (flesh < 0) or (flesh > High(ggItems)) then raise Exception.Create('DynTree: trying to get dimensions of inexistant item');
+ it := @ggItems[flesh];
+ if (it.Obj.Rect.Width < 1) or (it.Obj.Rect.Height < 1) then exit;
+ aabb := AABB2D.Create(it.Obj.X, it.Obj.Y, it.Obj.X+it.Obj.Rect.Width-1, it.Obj.Y+it.Obj.Rect.Height-1);
+ if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
+ result := true;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+var
+ itemTree: TDynAABBTreeItem = nil;
freeIds: TBinaryHeapInt = nil; // free item ids
end;
-// ////////////////////////////////////////////////////////////////////////// //
-type
- TDynAABBTreeItem = class(TDynAABBTree)
- function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; override;
- end;
-
-function TDynAABBTreeItem.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean;
-var
- it: PItem;
-begin
- result := false;
- if (flesh = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
- //if not g_ItemValidId(tag) then raise Exception.Create('DynTree: trying to get dimensions of inexistant item');
- if (tag < 0) or (tag > High(ggItems)) then raise Exception.Create('DynTree: trying to get dimensions of inexistant item');
- it := @ggItems[tag];
- if (it.Obj.Rect.Width < 1) or (it.Obj.Rect.Height < 1) then exit;
- aabb := AABB2D.Create(it.Obj.X, it.Obj.Y, it.Obj.X+it.Obj.Rect.Width, it.Obj.Y+it.Obj.Rect.Height);
- if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
- result := true;
-end;
-
-
// ////////////////////////////////////////////////////////////////////////// //
procedure TItem.positionChanged ();
var
begin
if (treeNode = -1) then
begin
- treeNode := itemTree.insertObject(itemTree{doesn't matter}, arrIdx, true); // static object
+ treeNode := itemTree.insertObject(arrIdx, 0, true); // static object
itemTree.getNodeXY(treeNode, x, y);
{$IF DEFINED(D2F_DEBUG)}e_WriteLog(Format('item #%d: inserted into the tree; nodeid=%d; x=%d; y=%d', [arrIdx, treeNode, x, y]), MSG_NOTIFY);{$ENDIF}
end
itemTree.updateObject(treeNode);
{$ELSE}
itemTree.removeObject(treeNode);
- treeNode := itemTree.insertObject(itemTree{doesn't matter}, arrIdx, true); // static object
+ treeNode := itemTree.insertObject(arrIdx, 0, true); // static object
{$ENDIF}
itemTree.getNodeXY(treeNode, x, y);
diff --git a/src/game/g_map.pas b/src/game/g_map.pas
index cd2aff9951cc4908138407a965ef2acb17d5b77d..0543abe30aa620cb0506c08d8e2c480dc06a0356 100644 (file)
--- a/src/game/g_map.pas
+++ b/src/game/g_map.pas
FLAG_SIGNATURE = $47414C46; // 'FLAG'
+type
+ TPanelGrid = specialize TBodyGridBase<TPanel>;
+
+ TDynAABBTreePanelBase = specialize TDynAABBTreeBase<TPanel>;
+
+ TDynAABBTreeMap = class(TDynAABBTreePanelBase)
+ function getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean; override;
+ end;
+
+function TDynAABBTreeMap.getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean;
+begin
+ result := false;
+ if (pan = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
+ aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width-1, pan.Y+pan.Height-1);
+ if (pan.Width < 1) or (pan.Height < 1) then exit;
+ if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
+ result := true;
+end;
+
+
function panelTypeToTag (panelType: Word): Integer;
begin
case panelType of
PArrID: Integer;
end;
-type
- TDynAABBTreeMap = class(TDynAABBTree)
- function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; override;
- end;
-
-function TDynAABBTreeMap.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean;
-var
- pan: TPanel;
-begin
- result := false;
- if (flesh = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
- //pan := (flesh as TPanel);
- pan := TPanel(flesh);
- aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width, pan.Y+pan.Height);
- if (pan.Width < 1) or (pan.Height < 1) then exit;
- //if (pan.Width = 1) then aabb.maxX += 1;
- //if (pan.Height = 1) then aabb.maxY += 1;
- //if (pan.Width < 3) or (pan.Height < 3) then exit;
- //aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width-2, pan.Y+pan.Height-2);
- if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
- result := aabb.valid;
-end;
-
var
PanelById: array of TPanelID;
Textures: TLevelTextureArray;
RespawnPoints: Array of TRespawnPoint;
FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
//DOMFlagPoints: Array of TFlagPoint;
- gMapGrid: TBodyGrid = nil;
- mapTree: TDynAABBTree = nil;
+ gMapGrid: TPanelGrid = nil;
+ mapTree: TDynAABBTreeMap = nil;
procedure g_Map_ProfilersBegin ();
e_WriteLog(Format('map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
- gMapGrid := TBodyGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
+ gMapGrid := TPanelGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
mapTree := TDynAABBTreeMap.Create();
addPanelsToGrid(gWalls, PANEL_WALL);
// new algo
procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
- function checker (obj: TObject; tag: Integer): Boolean;
- var
- pan: TPanel;
+ function checker (pan: TPanel; tag: Integer): Boolean;
begin
result := false; // don't stop, ever
- //pan := (obj as TPanel);
- pan := TPanel(obj);
- //if (PanelType = PANEL_CLOSEDOOR) then begin if not pan.Door then exit; end else begin if pan.Door then exit; end;
if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
- //dplAddPanel(pan);
gDrawPanelList.insert(pan);
end;
gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
end;
// list will be rendered in `g_game.DrawPlayer()`
- (*
- while (gDrawPanelList.count > 0) do
- begin
- //(gDrawPanelList.front() as TPanel).Draw();
- TPanel(gDrawPanelList.front()).Draw();
- gDrawPanelList.popFront();
- end;
- *)
end;
procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
- function checker (obj: TObject; tag: Integer): Boolean;
- var
- pan: TPanel;
+ function checker (pan: TPanel; tag: Integer): Boolean;
begin
result := false; // don't stop, ever
- //if (tag <> GridTagWall) and (tag <> GridTagDoor) then exit; // only walls
- //pan := (obj as TPanel);
- pan := TPanel(obj);
pan.DrawShadowVolume(lightX, lightY, radius);
end;
function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
- function checker (obj: TObject; tag: Integer): Boolean;
- var
- pan: TPanel;
+ function checker (pan: TPanel; tag: Integer): Boolean;
begin
result := false; // don't stop, ever
- pan := TPanel(obj);
if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
begin
texid: DWORD;
// slightly different from the old code, but meh...
- function checker (obj: TObject; tag: Integer): Boolean;
- var
- pan: TPanel;
+ function checker (pan: TPanel; tag: Integer): Boolean;
begin
result := false; // don't stop, ever
//if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
//2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
end;
// collision?
- pan := (obj as TPanel);
if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
// yeah
- texid := TPanel(obj).GetTextureID();
+ texid := pan.GetTextureID();
// water? water has the highest priority, so stop right here
if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
// acid2?
index a64e6322b7cbd98518e4ee9671cdb2a70aaccd94..3371a1ea82e90e17db293748e4c43b581ba4ce72 100644 (file)
--- a/src/game/z_aabbtree.pas
+++ b/src/game/z_aabbtree.pas
type
{$IFDEF aabbtree_use_floats}TreeNumber = Single;{$ELSE}TreeNumber = Integer;{$ENDIF}
- TTreeFlesh = TObject;
-
// ////////////////////////////////////////////////////////////////////////// //
type
// ////////////////////////////////////////////////////////////////////////// //
// Dynamic AABB Tree: can be used to speed up broad phase in various engines
type
- TDynAABBTree = class(TObject)
+ generic TDynAABBTreeBase<ITP> = class(TObject)
+ public
+ type TTreeFlesh = ITP;
+
private
type
PTreeNode = ^TTreeNode;
procedure dumpToLog ();
end;
- TVisitCheckerCB = function (node: PTreeNode): Boolean is nested;
+ TVisitCheckerCB = function (node: PTreeNode): Boolean of object;
//TVisitVisitorCB = function (abody: TTreeFlesh; atag: Integer): Boolean is nested;
+ const ModeNoChecks = 0;
+ const ModeAABB = 1;
+ const ModePoint = 2;
+
public
// return `true` to stop
type TForEachLeafCB = function (abody: TTreeFlesh; var aabb: AABB2D): Boolean is nested; // WARNING! don't modify AABB here!
const LinearMotionGapMultiplier = 17; // *10
{$ENDIF}
- private
- mNodes: array of TTreeNode; // nodes of the tree
- mRootNodeId: Integer; // id of the root node of the tree
- mFreeNodeId: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
- mAllocCount: Integer; // number of allocated nodes in the tree
- mNodeCount: Integer; // number of nodes in the tree
-
- // 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: 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
+ PSegmentQueryResult = ^TSegmentQueryResult;
TSegmentQueryResult = record
dist: Single; // <0: nothing was hit
flesh: TTreeFlesh;
function valid (): Boolean; inline;
end;
+ private
+ mNodes: array of TTreeNode; // nodes of the tree
+ mRootNodeId: Integer; // id of the root node of the tree
+ mFreeNodeId: Integer; // id of the first node of the list of free (allocated) nodes in the tree that we can use
+ mAllocCount: Integer; // number of allocated nodes in the tree
+ mNodeCount: Integer; // number of nodes in the tree
+
+ // 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: TreeNumber;
+
+ chkAABB: AABB2D; // for checkers
+ qSRes: PSegmentQueryResult; // for queries
+ // for segment query
+ maxFraction: Single;
+ curax, curay: Single;
+ curbx, curby: Single;
+ dirx, diry: Single;
+ sqcb: TSegQueryCallback;
+
+ function checkerAABB (node: PTreeNode): Boolean;
+ function checkerPoint (node: PTreeNode): Boolean;
+ function checkerRay (node: PTreeNode): Boolean;
+ function visitorRay (flesh: TTreeFlesh; tag: Integer): Boolean;
+
+ type TQueryOverlapDg = function (abody: TTreeFlesh; atag: Integer): Boolean of object;
+
private
function allocateNode (): Integer;
procedure releaseNode (nodeId: Integer);
function computeHeight (nodeId: Integer): Integer;
function insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer;
procedure setup ();
- function visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; tagmask: Integer=-1): Integer;
+ function visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; visdg: TQueryOverlapDg; tagmask: Integer): Integer;
+
+ function forEachNode (nodeId: Integer; dg: TForEachLeafCB): Boolean;
public
{$IFDEF aabbtree_query_count}
function getNodeXY (nodeid: Integer; out x, y: Integer): Boolean; inline;
// return `false` for invalid flesh
- function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; virtual; abstract;
+ function getFleshAABB (out aabb: AABB2D; flesh: TTreeFlesh; tag: Integer): Boolean; virtual; abstract;
// insert an object into the tree
// this method creates a new leaf node in the tree and returns the id of the corresponding node or -1 on error
function updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
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 pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
+ function segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback; tagmask: Integer=-1): Boolean;
function computeTreeHeight (): Integer; // compute the height of the tree
end;
+function dtMinI (a, b: Integer): Integer; inline;
+function dtMaxI (a, b: Integer): Integer; inline;
+
+function dtMinF (a, b: TreeNumber): TreeNumber; inline;
+function dtMaxF (a, b: TreeNumber): TreeNumber; inline;
+
+
implementation
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 dtMinI (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end;
+function dtMaxI (a, b: Integer): Integer; 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;
+function dtMinF (a, b: TreeNumber): TreeNumber; inline; begin if (a < b) then result := a else result := b; end;
+function dtMaxF (a, b: TreeNumber): TreeNumber; inline; begin if (a > b) then result := a else result := b; end;
// ////////////////////////////////////////////////////////////////////////// //
setMergeTwo(aabb0, aabb1);
end;
-function AABB2D.getvalid (): Boolean; inline; begin result := (minX < maxX) and (minY < maxY); end;
+function AABB2D.getvalid (): Boolean; inline; begin result := (minX <= maxX) and (minY <= maxY); end;
{$IFDEF aabbtree_use_floats}
function AABB2D.getcenterX (): TreeNumber; inline; begin result := (minX+maxX)/2.0; end;
@@ -372,8 +403,8 @@ function AABB2D.getcenterY (): TreeNumber; inline; begin result := (minY+maxY)/2
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;
+function AABB2D.getextentX (): TreeNumber; inline; begin result := maxX-minX+1; end;
+function AABB2D.getextentY (): TreeNumber; inline; begin result := maxY-minY+1; end;
procedure AABB2D.copyFrom (var aabb: AABB2D); inline;
begin
procedure AABB2D.setDims (x0, y0, x1, y1: TreeNumber); inline;
begin
- minX := minF(x0, x1);
- minY := minF(y0, y1);
- maxX := maxF(x0, x1);
- maxY := maxF(y0, y1);
+ minX := dtMinF(x0, x1);
+ minY := dtMinF(y0, y1);
+ maxX := dtMaxF(x0, x1);
+ maxY := dtMaxF(y0, y1);
{$IF DEFINED(D2F_DEBUG)}
if not valid then raise Exception.Create('setDims: result is fucked');
{$ENDIF}
if not aabb0.valid then raise Exception.Create('setMergeTwo: aabb0 is fucked');
if not aabb1.valid then raise Exception.Create('setMergeTwo: aabb0 is fucked');
{$ENDIF}
- minX := minF(aabb0.minX, aabb1.minX);
- minY := minF(aabb0.minY, aabb1.minY);
- maxX := maxF(aabb0.maxX, aabb1.maxX);
- maxY := maxF(aabb0.maxY, aabb1.maxY);
+ minX := dtMinF(aabb0.minX, aabb1.minX);
+ minY := dtMinF(aabb0.minY, aabb1.minY);
+ maxX := dtMaxF(aabb0.maxX, aabb1.maxX);
+ maxY := dtMaxF(aabb0.maxY, aabb1.maxY);
{$IF DEFINED(D2F_DEBUG)}
if not valid then raise Exception.Create('setMergeTwo: result is fucked');
{$ENDIF}
function AABB2D.volume (): TreeNumber; inline;
begin
- result := (maxX-minX)*(maxY-minY);
+ result := (maxX-minX+1)*(maxY-minY+1);
end;
{$IF DEFINED(D2F_DEBUG)}
if not aabb.valid then raise Exception.Create('merge: aabb is fucked');
{$ENDIF}
- minX := minF(minX, aabb.minX);
- minY := minF(minY, aabb.minY);
- maxX := maxF(maxX, aabb.maxX);
- maxY := maxF(maxY, aabb.maxY);
+ minX := dtMinF(minX, aabb.minX);
+ minY := dtMinF(minY, aabb.minY);
+ maxX := dtMaxF(maxX, aabb.maxX);
+ maxY := dtMaxF(maxY, aabb.maxY);
{$IF DEFINED(D2F_DEBUG)}
if not valid then raise Exception.Create('setMergeTwo: result is fucked');
{$ENDIF}
// ////////////////////////////////////////////////////////////////////////// //
-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;
+procedure TDynAABBTreeBase.TSegmentQueryResult.reset (); inline; begin dist := -1; flesh := Default(ITP); end;
+function TDynAABBTreeBase.TSegmentQueryResult.valid (): Boolean; inline; begin result := (dist >= 0) and (flesh <> Default(ITP)); end;
// ////////////////////////////////////////////////////////////////////////// //
-function TDynAABBTree.TTreeNode.leaf (): Boolean; inline; begin result := (height = 0); end;
-function TDynAABBTree.TTreeNode.isfree (): Boolean; inline; begin result := (height = -1); end;
+function TDynAABBTreeBase.TTreeNode.leaf (): Boolean; inline; begin result := (height = 0); end;
+function TDynAABBTreeBase.TTreeNode.isfree (): Boolean; inline; begin result := (height = -1); end;
-procedure TDynAABBTree.TTreeNode.clear (); inline;
+procedure TDynAABBTreeBase.TTreeNode.clear (); inline;
begin
parentId := 0;
children[0] := 0;
children[1] := 0;
- flesh := nil;
+ flesh := Default(ITP);
tag := 0;
height := 0;
aabb.minX := 0;
aabb.maxY := 0;
end;
-procedure TDynAABBTree.TTreeNode.dumpToLog ();
+procedure TDynAABBTreeBase.TTreeNode.dumpToLog ();
begin
e_WriteLog(Format('NODE: parentId=%d; children=[%d,%d]; height=%d; tag=%d; fleshX=%d; fleshY=%d; aabb=(%d,%d)-(%d,%d)',
[parentId, children[0], children[1], Integer(height), tag, fleshX, fleshY, aabb.minX, aabb.minY, aabb.maxX, aabb.maxY]),
// ////////////////////////////////////////////////////////////////////////// //
// allocate and return a node to use in the tree
-function TDynAABBTree.allocateNode (): Integer;
+function TDynAABBTreeBase.allocateNode (): Integer;
var
i, newsz, freeNodeId: Integer;
node: PTreeNode;
// release a node
-procedure TDynAABBTree.releaseNode (nodeId: Integer);
+procedure TDynAABBTreeBase.releaseNode (nodeId: Integer);
begin
{$IFDEF aabbtree_many_asserts}assert(mNodeCount > 0);{$ENDIF}
{$IFDEF aabbtree_many_asserts}assert((nodeId >= 0) and (nodeId < mAllocCount));{$ENDIF}
{$IFDEF aabbtree_many_asserts}assert(mNodes[nodeId].height >= 0);{$ENDIF}
mNodes[nodeId].nextNodeId := mFreeNodeId;
mNodes[nodeId].height := -1;
- mNodes[nodeId].flesh := nil;
+ mNodes[nodeId].flesh := Default(ITP);
mFreeNodeId := nodeId;
Dec(mNodeCount);
// insert a leaf node in the tree
// 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
-procedure TDynAABBTree.insertLeafNode (nodeId: Integer);
+procedure TDynAABBTreeBase.insertLeafNode (nodeId: Integer);
var
newNodeAABB, mergedAABBs, currentAndLeftAABB, currentAndRightAABB: AABB2D;
currentNodeId: Integer;
{$IFDEF aabbtree_many_asserts}assert(rightChild <> TTreeNode.NullTreeNode);{$ENDIF}
// recompute the height of the node in the tree
- mNodes[currentNodeId].height := maxI(mNodes[leftChild].height, mNodes[rightChild].height)+1;
+ mNodes[currentNodeId].height := dtMaxI(mNodes[leftChild].height, mNodes[rightChild].height)+1;
{$IFDEF aabbtree_many_asserts}assert(mNodes[currentNodeId].height > 0);{$ENDIF}
// recompute the AABB of the node
// remove a leaf node from the tree
-procedure TDynAABBTree.removeLeafNode (nodeId: Integer);
+procedure TDynAABBTreeBase.removeLeafNode (nodeId: Integer);
var
currentNodeId, parentNodeId, grandParentNodeId, siblingNodeId: Integer;
leftChildId, rightChildId: Integer;
// recompute the AABB and the height of the current node
mNodes[currentNodeId].aabb.setMergeTwo(mNodes[leftChildId].aabb, mNodes[rightChildId].aabb);
- mNodes[currentNodeId].height := maxI(mNodes[leftChildId].height, mNodes[rightChildId].height)+1;
+ mNodes[currentNodeId].height := dtMaxI(mNodes[leftChildId].height, mNodes[rightChildId].height)+1;
{$IFDEF aabbtree_many_asserts}assert(mNodes[currentNodeId].height > 0);{$ENDIF}
currentNodeId := mNodes[currentNodeId].parentId;
// balance the sub-tree of a given node using left or right rotations
// the rotation schemes are described in the book "Introduction to Game Physics with Box2D" by Ian Parberry
// this method returns the new root node id
-function TDynAABBTree.balanceSubTreeAtNode (nodeId: Integer): Integer;
+function TDynAABBTreeBase.balanceSubTreeAtNode (nodeId: Integer): Integer;
var
nodeA, nodeB, nodeC, nodeF, nodeG: PTreeNode;
nodeBId, nodeCId, nodeFId, nodeGId: Integer;
nodeC.aabb.setMergeTwo(nodeA.aabb, nodeF.aabb);
// recompute the height of node A and C
- nodeA.height := maxI(nodeB.height, nodeG.height)+1;
- nodeC.height := maxI(nodeA.height, nodeF.height)+1;
+ nodeA.height := dtMaxI(nodeB.height, nodeG.height)+1;
+ nodeC.height := dtMaxI(nodeA.height, nodeF.height)+1;
{$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
{$IFDEF aabbtree_many_asserts}assert(nodeC.height > 0);{$ENDIF}
end
nodeC.aabb.setMergeTwo(nodeA.aabb, nodeG.aabb);
// recompute the height of node A and C
- nodeA.height := maxI(nodeB.height, nodeF.height)+1;
- nodeC.height := maxI(nodeA.height, nodeG.height)+1;
+ nodeA.height := dtMaxI(nodeB.height, nodeF.height)+1;
+ nodeC.height := dtMaxI(nodeA.height, nodeG.height)+1;
{$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
{$IFDEF aabbtree_many_asserts}assert(nodeC.height > 0);{$ENDIF}
end;
nodeB.aabb.setMergeTwo(nodeA.aabb, nodeF.aabb);
// recompute the height of node A and B
- nodeA.height := maxI(nodeC.height, nodeG.height)+1;
- nodeB.height := maxI(nodeA.height, nodeF.height)+1;
+ nodeA.height := dtMaxI(nodeC.height, nodeG.height)+1;
+ nodeB.height := dtMaxI(nodeA.height, nodeF.height)+1;
{$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
{$IFDEF aabbtree_many_asserts}assert(nodeB.height > 0);{$ENDIF}
end
nodeB.aabb.setMergeTwo(nodeA.aabb, nodeG.aabb);
// recompute the height of node A and B
- nodeA.height := maxI(nodeC.height, nodeF.height)+1;
- nodeB.height := maxI(nodeA.height, nodeG.height)+1;
+ nodeA.height := dtMaxI(nodeC.height, nodeF.height)+1;
+ nodeB.height := dtMaxI(nodeA.height, nodeG.height)+1;
{$IFDEF aabbtree_many_asserts}assert(nodeA.height > 0);{$ENDIF}
{$IFDEF aabbtree_many_asserts}assert(nodeB.height > 0);{$ENDIF}
end;
// compute the height of a given node in the tree
-function TDynAABBTree.computeHeight (nodeId: Integer): Integer;
+function TDynAABBTreeBase.computeHeight (nodeId: Integer): Integer;
var
node: PTreeNode;
leftHeight, rightHeight: Integer;
rightHeight := computeHeight(node.children[TTreeNode.Right]);
// return the height of the node
- result := 1+maxI(leftHeight, rightHeight);
+ result := 1+dtMaxI(leftHeight, rightHeight);
end;
// internally add an object into the tree
-function TDynAABBTree.insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer;
+function TDynAABBTreeBase.insertObjectInternal (var aabb: AABB2D; staticObject: Boolean): Integer;
var
nodeId: Integer;
node: PTreeNode;
// initialize the tree
-procedure TDynAABBTree.setup ();
+procedure TDynAABBTreeBase.setup ();
var
i: Integer;
begin
// also, checks if the tree structure is valid (for debugging purpose)
-function TDynAABBTree.forEachLeaf (dg: TForEachLeafCB): Boolean;
- function forEachNode (nodeId: Integer): Boolean;
- var
- pNode: PTreeNode;
- leftChild, rightChild, height: Integer;
- aabb: AABB2D;
+function TDynAABBTreeBase.forEachNode (nodeId: Integer; dg: TForEachLeafCB): Boolean;
+var
+ pNode: PTreeNode;
+ leftChild, rightChild, height: Integer;
+ aabb: AABB2D;
+begin
+ result := false;
+ if (nodeId = TTreeNode.NullTreeNode) then exit;
+ // if it is the root
+ if (nodeId = mRootNodeId) then assert(mNodes[nodeId].parentId = TTreeNode.NullTreeNode);
+ // get the children nodes
+ pNode := @mNodes[nodeId];
+ assert(pNode.height >= 0);
+ if (not pNode.aabb.valid) then
begin
- result := false;
- if (nodeId = TTreeNode.NullTreeNode) then exit;
- // if it is the root
- if (nodeId = mRootNodeId) then assert(mNodes[nodeId].parentId = TTreeNode.NullTreeNode);
- // get the children nodes
- pNode := @mNodes[nodeId];
- assert(pNode.height >= 0);
- if (not pNode.aabb.valid) then
+ {$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, pNode.tag);
{$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);
+ 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('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);
+ 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}
- if pNode.leaf then
- begin
- getFleshAABB(aabb, pNode.flesh, pNode.tag);
- {$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);
- assert(pNode.aabb.volume > 0);
- // if the current node is a leaf
- if (pNode.leaf) then
- begin
- assert(pNode.height = 0);
- if assigned(dg) then result := dg(pNode.flesh, pNode.aabb);
- end
- else
- begin
- leftChild := pNode.children[TTreeNode.Left];
- rightChild := pNode.children[TTreeNode.Right];
- // check that the children node Ids are valid
- assert((0 <= leftChild) and (leftChild < mAllocCount));
- assert((0 <= rightChild) and (rightChild < mAllocCount));
- // check that the children nodes have the correct parent node
- assert(mNodes[leftChild].parentId = nodeId);
- assert(mNodes[rightChild].parentId = nodeId);
- // check the height of node
- height := 1+maxI(mNodes[leftChild].height, mNodes[rightChild].height);
- assert(mNodes[nodeId].height = height);
- // check the AABB of the node
- 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);
- assert(aabb.maxY = mNodes[nodeId].aabb.maxY);
- // recursively check the children nodes
- result := forEachNode(leftChild);
- if not result then result := forEachNode(rightChild);
end;
end;
+ 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);
+ if assigned(dg) then result := dg(pNode.flesh, pNode.aabb);
+ end
+ else
+ begin
+ leftChild := pNode.children[TTreeNode.Left];
+ rightChild := pNode.children[TTreeNode.Right];
+ // check that the children node Ids are valid
+ assert((0 <= leftChild) and (leftChild < mAllocCount));
+ assert((0 <= rightChild) and (rightChild < mAllocCount));
+ // check that the children nodes have the correct parent node
+ assert(mNodes[leftChild].parentId = nodeId);
+ assert(mNodes[rightChild].parentId = nodeId);
+ // check the height of node
+ height := 1+dtMaxI(mNodes[leftChild].height, mNodes[rightChild].height);
+ assert(mNodes[nodeId].height = height);
+ // check the AABB of the node
+ 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);
+ assert(aabb.maxY = mNodes[nodeId].aabb.maxY);
+ // recursively check the children nodes
+ result := forEachNode(leftChild, dg);
+ if not result then result := forEachNode(rightChild, dg);
+ end;
+end;
+
+// also, checks if the tree structure is valid (for debugging purpose)
+function TDynAABBTreeBase.forEachLeaf (dg: TForEachLeafCB): Boolean;
begin
// recursively check each node
- result := forEachNode(mRootNodeId);
+ result := forEachNode(mRootNodeId, dg);
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
-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;
+function TDynAABBTreeBase.visit (var caabb: AABB2D; mode: Integer; checker: TVisitCheckerCB; visitor: TQueryOverlapCB; visdg: TQueryOverlapDg; tagmask: Integer): Integer;
var
stack: array [0..2048] of Integer; // stack with the nodes to visit
bigstack: array of Integer = nil;
sp: Integer = 0;
+ (*
procedure spush (id: Integer); inline;
var
xsp: Integer;
result := bigstack[sp-length(stack)];
end;
end;
+ *)
var
nodeId: Integer;
node: PTreeNode;
doNode: Boolean = false;
+ xsp: Integer;
begin
if not assigned(checker) then begin result := -1; exit; end;
- if not assigned(visitor) then raise Exception.Create('dyntree: empty visitors aren''t supported');
- //if not assigned(visitor) then begin result := -1; exit; end;
+ //if not assigned(visitor) and not assigned(visdg) then raise Exception.Create('dyntree: empty visitors aren''t supported');
//try
{$IFDEF aabbtree_query_count}
mNodesVisited := 0;
{$ENDIF}
// start from root node
- spush(mRootNodeId);
+ {$IF FALSE}
+ spush(mRootNodeId);
+ {$ELSE}
+ if (sp < length(stack)) then begin stack[sp] := mRootNodeId; Inc(sp); end
+ else begin xsp := sp-length(stack); if (xsp < length(bigstack)) then bigstack[xsp] := mRootNodeId
+ else begin SetLength(bigstack, length(bigstack)+1); bigstack[high(bigstack)] := mRootNodeId; end;
+ end;
+ Inc(sp);
+ {$ENDIF}
// while there are still nodes to visit
while (sp > 0) do
begin
// get the next node id to visit
- {$IF TRUE}
+ {$IF FALSE}
nodeId := spop();
{$ELSE}
- 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;
+ if (sp <= length(stack)) then begin Dec(sp); nodeId := stack[sp]; end
+ else begin Dec(sp); nodeId := bigstack[sp-length(stack)]; end;
{$ENDIF}
// skip it if it is a nil node
if (nodeId = TTreeNode.NullTreeNode) then continue;
{$IFDEF aabbtree_query_count}Inc(mNodesDeepVisited);{$ENDIF}
if ((node.tag and tagmask) <> 0) then
begin
- if (visitor(node.flesh, node.tag)) then begin result := nodeId; bigstack := nil; exit; end;
+ if assigned(visitor) then
+ begin
+ if (visitor(node.flesh, node.tag)) then begin result := nodeId; bigstack := nil; exit; end;
+ end;
+ if assigned(visdg) then
+ begin
+ if (visdg(node.flesh, node.tag)) then begin result := nodeId; bigstack := nil; exit; end;
+ end;
end;
end
else
begin
// if the node is not a leaf, we need to visit its children
- spush(node.children[TTreeNode.Left]);
- spush(node.children[TTreeNode.Right]);
+ {$IF FALSE}
+ spush(node.children[TTreeNode.Left]);
+ spush(node.children[TTreeNode.Right]);
+ {$ELSE}
+ if (sp < length(stack)) then begin stack[sp] := node.children[TTreeNode.Left]; Inc(sp); end
+ else begin xsp := sp-length(stack); if (xsp < length(bigstack)) then bigstack[xsp] := node.children[TTreeNode.Left]
+ else begin SetLength(bigstack, length(bigstack)+1); bigstack[high(bigstack)] := node.children[TTreeNode.Left]; end;
+ end;
+ Inc(sp);
+
+ if (sp < length(stack)) then begin stack[sp] := node.children[TTreeNode.Right]; Inc(sp); end
+ else begin xsp := sp-length(stack); if (xsp < length(bigstack)) then bigstack[xsp] := node.children[TTreeNode.Right]
+ else begin SetLength(bigstack, length(bigstack)+1); bigstack[high(bigstack)] := node.children[TTreeNode.Right]; end;
+ end;
+ Inc(sp);
+
+ {$ENDIF}
end;
end;
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: TreeNumber=0);
+constructor TDynAABBTreeBase.Create (extraAABBGap: TreeNumber=0);
begin
mExtraGap := extraAABBGap;
setup();
end;
-destructor TDynAABBTree.Destroy ();
+destructor TDynAABBTreeBase.Destroy ();
begin
mNodes := nil;
inherited;
// clear all the nodes and reset the tree
-procedure TDynAABBTree.reset ();
+procedure TDynAABBTreeBase.reset ();
begin
mNodes := nil;
setup();
end;
-function TDynAABBTree.computeTreeHeight (): Integer; begin result := computeHeight(mRootNodeId); end;
+function TDynAABBTreeBase.computeTreeHeight (): Integer; begin result := computeHeight(mRootNodeId); end;
// return the root AABB of the tree
-procedure TDynAABBTree.getRootAABB (var aabb: AABB2D);
+procedure TDynAABBTreeBase.getRootAABB (var aabb: AABB2D);
begin
{$IFDEF aabbtree_many_asserts}assert((mRootNodeId >= 0) and (mRootNodeId < mAllocCount));{$ENDIF}
aabb := mNodes[mRootNodeId].aabb;
// does the given id represents a valid object?
// WARNING: ids of removed objects can be reused on later insertions!
-function TDynAABBTree.isValidId (id: Integer): Boolean;
+function TDynAABBTreeBase.isValidId (id: Integer): Boolean;
begin
result := (id >= 0) and (id < mAllocCount) and (mNodes[id].leaf);
end;
// get object by nodeid; can return nil for invalid ids
-function TDynAABBTree.getNodeObjectId (nodeid: Integer): TTreeFlesh;
+function TDynAABBTreeBase.getNodeObjectId (nodeid: Integer): TTreeFlesh;
begin
- if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := nil;
+ if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then result := mNodes[nodeid].flesh else result := Default(ITP);
end;
// get fat object AABB by nodeid; returns random shit for invalid ids
-procedure TDynAABBTree.getNodeFatAABB (var aabb: AABB2D; nodeid: Integer);
+procedure TDynAABBTreeBase.getNodeFatAABB (var aabb: AABB2D; nodeid: Integer);
begin
if (nodeid >= 0) and (nodeid < mAllocCount) and (not mNodes[nodeid].isfree) then aabb.copyFrom(mNodes[nodeid].aabb) else aabb.setDims(0, 0, 0, 0);
end;
-function TDynAABBTree.getNodeXY (nodeid: Integer; out x, y: Integer): Boolean; inline;
+function TDynAABBTreeBase.getNodeXY (nodeid: Integer; out x, y: Integer): Boolean; inline;
begin
if (nodeid >= 0) and (nodeid < mAllocCount) and (mNodes[nodeid].leaf) then
begin
// 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; tag: Integer; staticObject: Boolean=false): Integer;
+function TDynAABBTreeBase.insertObject (flesh: TTreeFlesh; tag: Integer; staticObject: Boolean=false): Integer;
var
aabb: AABB2D;
nodeId, fx, fy: Integer;
// remove an object from the tree
// WARNING: ids of removed objects can be reused on later insertions!
-procedure TDynAABBTree.removeObject (nodeId: Integer);
+procedure TDynAABBTreeBase.removeObject (nodeId: Integer);
begin
- if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree');
+ if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTreeBase');
// remove the node from the tree
removeLeafNode(nodeId);
releaseNode(nodeId);
end;
-function TDynAABBTree.updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
+function TDynAABBTreeBase.updateObject (nodeId: Integer; forceReinsert: Boolean=false): Boolean; overload;
var
newAABB: AABB2D;
dispX, dispY: TreeNumber;
begin
- if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
+ if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTreeBase.updateObject');
- if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTree.updateObject');
- if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTree.updateObject');
+ if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTreeBase.updateObject');
+ if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTreeBase.updateObject');
dispX := newAABB.minX-mNodes[nodeId].fleshX;
dispY := newAABB.minY-mNodes[nodeId].fleshY;
result := updateObject(nodeId, dispX, dispY, forceReinsert);
end;
-function TDynAABBTree.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
+function TDynAABBTreeBase.updateObject (nodeId: Integer; dispX, dispY: TreeNumber; forceReinsert: Boolean=false): Boolean; overload;
var
newAABB: AABB2D;
fx, fy: Integer;
node: PTreeNode;
begin
- if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTree.updateObject');
+ if (nodeId < 0) or (nodeId >= mAllocCount) or (not mNodes[nodeId].leaf) then raise Exception.Create('invalid node id in TDynAABBTreeBase.updateObject');
- if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTree.updateObject');
- if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTree.updateObject');
+ if not getFleshAABB(newAABB, mNodes[nodeId].flesh, mNodes[nodeId].tag) then raise Exception.Create('invalid flesh dimensions in TDynAABBTreeBase.updateObject');
+ if not newAABB.valid then raise Exception.Create('invalid flesh aabb in TDynAABBTreeBase.updateObject');
fx := newAABB.minX;
fy := newAABB.minY;
end;
+function TDynAABBTreeBase.checkerAABB (node: PTreeNode): Boolean;
+begin
+ result := chkAABB.overlaps(node.aabb);
+end;
+
+
// report all shapes overlapping with the AABB given in parameter
-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;
+function TDynAABBTreeBase.aabbQuery (ax, ay, aw, ah: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
var
nid: Integer;
+ oldaabb: AABB2D;
begin
- result := nil;
+ result := Default(ITP);
if not assigned(cb) then exit;
if (aw < 1) or (ah < 1) then exit;
- //caabb := AABB2D.Create(ax, ay, ax+aw, ay+ah);
- caabb.minX := ax;
- caabb.minY := ay;
- caabb.maxX := ax+aw;
- caabb.maxY := ay+ah;
- nid := visit(caabb, ModeAABB, checker, cb, tagmask);
- if (nid >= 0) then result := mNodes[nid].flesh else result := nil;
+ //chkAABB := AABB2D.Create(ax, ay, ax+aw, ay+ah);
+ oldaabb := chkAABB;
+ chkAABB.minX := ax;
+ chkAABB.minY := ay;
+ chkAABB.maxX := ax+aw;
+ chkAABB.maxY := ay+ah;
+ nid := visit(chkAABB, ModeAABB, checkerAABB, cb, nil, tagmask);
+ chkAABB := oldaabb;
+ if (nid >= 0) then result := mNodes[nid].flesh else result := Default(ITP);
+end;
+
+
+function TDynAABBTreeBase.checkerPoint (node: PTreeNode): Boolean;
+begin
+ result := node.aabb.contains(chkAABB.minX, chkAABB.minY);
end;
// report body that contains the given point, or nil
-function TDynAABBTree.pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB): TTreeFlesh;
- function checker (node: PTreeNode): Boolean;
- begin
- result := node.aabb.contains(ax, ay);
- end;
- function dummycb (abody: TTreeFlesh; atag: Integer): Boolean; begin result := false; end;
+function TDynAABBTreeBase.pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
var
nid: Integer;
- caabb: AABB2D;
+ oldaabb: AABB2D;
begin
- if not assigned(cb) then cb := dummycb;
- caabb := AABB2D.Create(ax, ay, ax+1, ay+1);
- nid := visit(caabb, ModePoint, checker, cb);
+ oldaabb := chkAABB;
+ chkAABB := AABB2D.Create(ax, ay, ax+1, ay+1);
+ nid := visit(chkAABB, ModePoint, checkerPoint, cb, nil, tagmask);
{$IFDEF aabbtree_many_asserts}assert((nid < 0) or ((nid >= 0) and (nid < mAllocCount) and (mNodes[nid].leaf)));{$ENDIF}
- if (nid >= 0) then result := mNodes[nid].flesh else result := nil;
+ chkAABB := oldaabb;
+ if (nid >= 0) then result := mNodes[nid].flesh else result := Default(ITP);
end;
-// segment querying method
-function TDynAABBTree.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback): Boolean;
-var
- maxFraction: Single = 1.0e100; // infinity
- curax, curay: Single;
- curbx, curby: Single;
- dirx, diry: Single;
- invlen: Single;
- caabb: AABB2D;
+function TDynAABBTreeBase.checkerRay (node: PTreeNode): Boolean;
+begin
+ result := node.aabb.intersects(curax, curay, curbx, curby);
+end;
- function checker (node: PTreeNode): Boolean;
+function TDynAABBTreeBase.visitorRay (flesh: TTreeFlesh; tag: Integer): Boolean;
+var
+ hitFraction: Single;
+begin
+ hitFraction := sqcb(flesh, curax, curay, curbx, curby);
+ // if the user returned a hitFraction of zero, it means that the raycasting should stop here
+ if (hitFraction = 0.0) then
begin
- result := node.aabb.intersects(curax, curay, curbx, curby);
+ qSRes.dist := 0;
+ qSRes.flesh := flesh;
+ result := true;
+ exit;
end;
-
- function visitor (flesh: TTreeFlesh; tag: Integer): Boolean;
- var
- hitFraction: Single;
+ // if the user returned a positive fraction
+ if (hitFraction > 0.0) then
begin
- hitFraction := cb(flesh, curax, curay, curbx, curby);
- // if the user returned a hitFraction of zero, it means that the raycasting should stop here
- if (hitFraction = 0.0) then
- begin
- qr.dist := 0;
- qr.flesh := flesh;
- result := true;
- exit;
- end;
- // if the user returned a positive fraction
- if (hitFraction > 0.0) then
+ // we update the maxFraction value and the ray AABB using the new maximum fraction
+ if (hitFraction < maxFraction) then
begin
- // we update the maxFraction value and the ray AABB using the new maximum fraction
- if (hitFraction < maxFraction) then
- begin
- maxFraction := hitFraction;
- qr.dist := hitFraction;
- qr.flesh := flesh;
- // fix curb here
- //curb := cura+dir*hitFraction;
- curbx := curax+dirx*hitFraction;
- curby := curay+diry*hitFraction;
- end;
+ maxFraction := hitFraction;
+ qSRes.dist := hitFraction;
+ qSRes.flesh := flesh;
+ // fix curb here
+ //curb := cura+dir*hitFraction;
+ curbx := curax+dirx*hitFraction;
+ curby := curay+diry*hitFraction;
end;
- result := false; // continue
end;
+ result := false; // continue
+end;
+
+// segment querying method
+function TDynAABBTreeBase.segmentQuery (var qr: TSegmentQueryResult; ax, ay, bx, by: TreeNumber; cb: TSegQueryCallback; tagmask: Integer=-1): Boolean;
+var
+ oldmaxFraction: Single;
+ oldcurax, oldcuray: Single;
+ oldcurbx, oldcurby: Single;
+ olddirx, olddiry: Single;
+ invlen: Single;
+ osres: PSegmentQueryResult;
+ osqcb: TSegQueryCallback;
begin
qr.reset();
if (ax >= bx) or (ay >= by) then begin result := false; exit; end;
+ oldmaxFraction := maxFraction;
+ oldcurax := curax;
+ oldcuray := curay;
+ oldcurbx := curbx;
+ oldcurby := curby;
+ olddirx := dirx;
+ olddiry := diry;
+
+ maxFraction := 1.0e100; // infinity
curax := ax;
curay := ay;
curbx := bx;
curby := by;
- dirx := (curbx-curax);
- diry := (curby-curay);
+ dirx := curbx-curax;
+ diry := curby-curay;
// normalize
invlen := 1.0/sqrt(dirx*dirx+diry*diry);
dirx *= invlen;
diry *= invlen;
- caabb := AABB2D.Create(0, 0, 1, 1);
- visit(caabb, ModeNoChecks, checker, visitor);
+ //chkAABB := AABB2D.Create(0, 0, 1, 1);
+ osres := qSRes;
+ qSRes := @qr;
+ osqcb := sqcb;
+ sqcb := cb;
+ visit(chkAABB, ModeNoChecks, checkerRay, nil, visitorRay, tagmask);
+ qSRes := osres;
+ sqcb := osqcb;
+
+ curax := oldcurax;
+ curay := oldcuray;
+ curbx := oldcurbx;
+ curby := oldcurby;
+ dirx := olddirx;
+ diry := olddiry;
+ maxFraction := oldmaxFraction;
result := qr.valid;
end;