1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 // universal spatial grid
16 {$INCLUDE ../shared/a_modes.inc}
17 {$IF DEFINED(D2F_DEBUG)}
18 {.$DEFINE D2F_DEBUG_RAYTRACE}
19 {.$DEFINE D2F_DEBUG_XXQ}
20 {.$DEFINE D2F_DEBUG_MOVER}
21 {$ENDIF}
22 {.$DEFINE GRID_USE_ORTHO_ACCEL}
23 {$DEFINE LINEAABB2}
26 interface
28 uses
29 mempool;
31 (*
32 * In order to make this usable for kind-of-recursive calls,
33 * we'll use "frame memory pool" to return results. That is,
34 * we will allocate a memory pool that will be cleared on
35 * frame start, and then used as a simple "no-free" allocator.
36 * Grid will put results into this pool, and will never bother
37 * to free it. Caller should call "release" on result, and
38 * the pool will throw away everything.
39 * No more callbacks, of course.
40 *)
42 const
45 type
54 type
58 public
67 private
68 const
71 public
72 type
75 private
82 private
94 public
109 private
110 type
119 TGridInternalCB = function (grida: Integer; bodyId: TBodyProxyId): Boolean of object; // return `true` to stop
121 private
122 //mTileSize: Integer;
126 public
129 type
131 private
135 public
142 private
155 public
157 {$IF DEFINED(D2F_DEBUG)}
159 {$ENDIF}
161 private
181 public
182 constructor Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer{; aTileSize: Integer=GridDefaultTileSize});
185 function insertBody (aObj: ITP; ax, ay, aWidth, aHeight: Integer; aTag: Integer=-1): TBodyProxyId;
194 // `false` if `body` is surely invalid
199 // return number of ITP thingys put into frame pool
200 // if `firstHit` is `true`, return on first hit (obviously)
201 function forEachInAABB (x, y, w, h: Integer; tagmask: Integer=-1; allowDisabled: Boolean=false; firstHit: Boolean=false): Iter;
203 // return number of ITP thingys put into frame pool
204 // if `firstHit` is `true`, return on first hit (obviously)
205 function forEachAtPoint (x, y: Integer; tagmask: Integer=-1; allowDisabled: Boolean=false; firstHit: Boolean=false): Iter;
209 // return object of the nearest hit or nil
211 function traceRay (out ex, ey: Integer; const ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): ITP;
213 // return `false` if we're still inside at the end
214 // line should be either strict horizontal, or strict vertical, otherwise an exception will be thrown
215 // `true`: endpoint will point at the last "inside" pixel
216 // `false`: endpoint will be (ax1, ay1)
217 function traceOrthoRayWhileIn (out ex, ey: Integer; ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): Boolean;
219 // trace line along the grid, put all objects from passed cells into frame pool, in no particular order
220 // return number of ITP thingys put into frame pool
221 function forEachAlongLine (ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1; log: Boolean=false): Iter;
223 // trace box with the given velocity; return object hit (if any)
224 // `cb` is used unconvetionally here: if it returns `false`, tracer will ignore the object
225 //WARNING: don't change tags in callbacks here!
226 function traceBox (out ex, ey: Integer; const ax0, ay0, aw, ah: Integer; const dx, dy: Integer; tagmask: Integer=-1): ITP;
228 // debug
229 function forEachBodyCell (body: TBodyProxyId): CellCoordIter; // this puts `TGridCellCoord` into frame pool for each cell
233 public
234 //WARNING! no sanity checks!
246 type
247 // common structure for all line tracers
249 public
252 private
260 public
261 // call `setyp` after this
266 // this will use `w[xy][01]` to clip coords
267 // return `false` if the whole line was clipped away
268 // on `true`, you should process first point, and go on
271 // call this *after* doing a step
272 // WARNING! if you will do a step when this returns `true`, you will fall into limbo
275 // as you will prolly call `done()` after doing a step anyway, this will do it for you
276 // move to next point, return `true` when the line is complete (i.e. you should stop)
279 // move to next tile; return `true` if the line is complete (and walker state is undefined then)
284 public
285 // current coords
292 //function minInt (a, b: Integer): Integer; inline;
293 //function maxInt (a, b: Integer): Integer; inline;
296 implementation
298 uses
302 // ////////////////////////////////////////////////////////////////////////// //
303 procedure swapInt (var a: Integer; var b: Integer); inline; var t: Integer; begin t := a; a := b; b := t; end;
304 //procedure swapInt (var a: Integer; var b: Integer); inline; begin a := a xor b; b := b xor a; a := a xor b; end;
305 //function minInt (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end;
306 //function maxInt (a, b: Integer): Integer; inline; begin if (a > b) then result := a else result := b; end;
309 // ////////////////////////////////////////////////////////////////////////// //
311 begin
316 begin
317 // clip rectange
325 var
327 begin
328 if (wx1 < wx0) or (wy1 < wy0) then begin stleft := 0; xd := x0; yd := y0; result := false; exit; end;
332 begin
334 end
335 else
336 begin
345 // check for ortho lines
347 begin
348 // horizontal
355 end
357 begin
358 // vertical
365 end
366 else
367 begin
368 // diagonal
370 begin
371 // horizontal
375 end
376 else
377 begin
378 // vertical
394 // true: done
396 begin
398 begin
402 end
403 else
404 begin
413 // true: done
415 var
419 begin
424 // strictly horizontal?
426 begin
427 // only xd
429 begin
430 // xd: to left edge
433 end
434 else
435 begin
436 // xd: to right edge
442 exit;
445 // strictly vertical?
447 begin
448 // only xd
450 begin
451 // yd: to top edge
454 end
455 else
456 begin
457 // yd: to bottom edge
463 exit;
466 // diagonal
468 // calculate xwalk
470 begin
473 end
474 else
475 begin
480 // calculate ywalk
482 begin
485 end
486 else
487 begin
492 {
493 while (xd <> ex) and (yd <> ey) do
494 begin
495 if horiz then
496 begin
497 xd += stx;
498 err += errinc;
499 if (err >= 0) then begin err -= errmax; yd += sty; end;
500 end
501 else
502 begin
503 yd += sty;
504 err += errinc;
505 if (err >= 0) then begin err -= errmax; xd += stx; end;
506 end;
507 Dec(stleft);
508 if (stleft < 1) then begin result := true; exit; end;
509 end;
510 }
514 begin
515 // in which dir we want to walk?
519 begin
522 begin
526 end
527 else
528 begin
531 begin
536 // check for walk completion
545 // ////////////////////////////////////////////////////////////////////////// //
546 procedure TBodyGridBase.TBodyProxyRec.setup (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer);
547 begin
560 begin
565 begin
570 begin
575 begin
580 begin
585 begin
590 // ////////////////////////////////////////////////////////////////////////// //
591 constructor TBodyGridBase.TAtPointEnumerator.Create (acells: TCellArray; aidx: Integer; agetpx: TGetProxyFn);
592 begin
601 begin
603 begin
605 begin
609 exit;
619 begin
624 // ////////////////////////////////////////////////////////////////////////// //
625 constructor TBodyGridBase.Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer{; aTileSize: Integer=GridDefaultTileSize});
626 var
628 begin
630 {$IF DEFINED(D2F_DEBUG)}
632 {$ENDIF}
633 {
634 if aTileSize < 1 then aTileSize := 1;
635 if aTileSize > 8192 then aTileSize := 8192; // arbitrary limit
636 mTileSize := aTileSize;
637 }
648 // init free list
650 begin
656 // init grid
658 // init proxies
666 e_WriteLog(Format('created grid with size: %dx%d (tile size: %d); pix: %dx%d', [mWidth, mHeight, mTileSize, mWidth*mTileSize, mHeight*mTileSize]), TMsgType.Notify);
671 begin
679 // ////////////////////////////////////////////////////////////////////////// //
681 var
683 begin
686 begin
690 begin
696 e_WriteLog(Format('grid size: %dx%d (tile size: %d); pix: %dx%d; used cells: %d; max bodies in cell: %d; max proxies allocated: %d; proxies used: %d', [mWidth, mHeight, mTileSize, mWidth*mTileSize, mHeight*mTileSize, mUsedCells, mcb, mProxyMaxCount, mProxyCount]), TMsgType.Notify);
701 var
705 begin
709 begin
712 begin
715 begin
718 begin
722 //cb((g mod mWidth)*mTileSize+mMinX, (g div mWidth)*mTileSize+mMinY);
725 // next cell
734 var
738 begin
742 if (x < 0) or (y < 0) or (x >= mWidth*mTileSize) or (y > mHeight*mTileSize) then begin result.finishIt(); exit; end;
745 begin
748 begin
750 //if cb(mProxies[cc.bodies[f]].mObj, mProxies[cc.bodies[f]].mTag) then begin result := mProxies[cc.bodies[f]].mObj; exit; end;
752 //presobj^ := mProxies[cc.bodies[f]].mObj;
755 // next cell
762 // ////////////////////////////////////////////////////////////////////////// //
763 function TBodyGridBase.getGridWidthPx (): Integer; inline; begin result := mWidth*mTileSize; end;
764 function TBodyGridBase.getGridHeightPx (): Integer; inline; begin result := mHeight*mTileSize; end;
768 begin
769 // fix coords
777 begin
779 begin
782 end
783 else
784 begin
793 begin
795 begin
798 end
799 else
800 begin
808 function TBodyGridBase.getBodyDims (body: TBodyProxyId; out rx, ry, rw, rh: Integer): Boolean; inline;
809 begin
811 begin
814 end
815 else
816 begin
827 // ////////////////////////////////////////////////////////////////////////// //
829 begin
830 if (pid >= 0) and (pid < Length(mProxies)) then result := ((mProxies[pid].mTag and TagDisabled) = 0) else result := false;
835 begin
837 begin
839 begin
841 end
842 else
843 begin
851 begin
856 // ////////////////////////////////////////////////////////////////////////// //
858 var
861 begin
863 begin
864 // no free cells, want more
868 begin
880 //e_WriteLog(Format('grid: allocated new cell #%d (total: %d)', [result, mUsedCells]), MSG_NOTIFY);
885 begin
887 begin
889 begin
900 // ////////////////////////////////////////////////////////////////////////// //
901 function TBodyGridBase.allocProxy (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer): TBodyProxyId;
902 var
905 begin
907 begin
908 // no free proxies, resize list
915 // get one from list
920 // add to used list
922 // statistics
928 begin
930 if (mProxyCount = 0) then raise Exception.Create('wutafuuuuu in grid (no allocated proxies, what i should free now?)');
931 // add to free list
939 // ////////////////////////////////////////////////////////////////////////// //
940 function TBodyGridBase.forGridRect (x, y, w, h: Integer; cb: TGridInternalCB; bodyId: TBodyProxyId): Boolean;
941 var
945 begin
948 // fix coords
951 // go on
960 // clip rect
966 // do the work
968 begin
970 begin
978 // ////////////////////////////////////////////////////////////////////////// //
980 var
985 begin
987 // add body to the given grid cell
990 begin
991 {$IF DEFINED(D2F_DEBUG)}
994 begin
997 begin
999 if (pi.bodies[f] = bodyId) then raise Exception.Create('trying to insert already inserted proxy');
1003 {$ENDIF}
1006 begin
1008 // check "has room" flag
1010 begin
1011 // can add here
1013 begin
1015 begin
1018 exit;
1023 // no room, go to next cell in list (if there is any)
1026 // no room in cells, add new cell to list
1028 // either no room, or no cell at all
1038 // assume that we cannot have one object added to bucket twice
1040 var
1044 begin
1046 // find and remove cell
1050 begin
1053 begin
1055 begin
1056 // i found her!
1058 begin
1059 // this cell contains no elements, remove it
1062 exit;
1064 // remove element from bucket
1066 begin
1071 exit;
1080 // ////////////////////////////////////////////////////////////////////////// //
1081 function TBodyGridBase.insertBody (aObj: ITP; aX, aY, aWidth, aHeight: Integer; aTag: Integer=-1): TBodyProxyId;
1082 begin
1085 //insertInternal(result);
1091 var
1093 begin
1096 //removeInternal(body);
1102 // ////////////////////////////////////////////////////////////////////////// //
1104 var
1107 begin
1114 {$IF DEFINED(D2F_DEBUG_MOVER)}
1115 e_WriteLog(Format('proxy #%d: MOVERESIZE: xg=%d;yg=%d;w=%d;h=%d;nx=%d;ny=%d;nw=%d;nh=%d', [body, x0-mMinX, y0-mMinY, w, h, nx-mMinX, ny-mMinY, nw, nh]), MSG_NOTIFY);
1116 {$ENDIF}
1118 // map -> grid
1123 // did any corner crossed tile boundary?
1128 begin
1129 //writeln('moveResizeBody: cell occupation changed! old=(', x0, ',', y0, ')-(', x0+w-1, ',', y0+h-1, '); new=(', nx, ',', ny, ')-(', nx+nw-1, ',', ny+nh-1, ')');
1130 //removeInternal(body);
1136 //insertInternal(body);
1138 end
1139 else
1140 begin
1149 //TODO: optimize for horizontal/vertical moves
1151 var
1159 begin
1161 // check if tile coords was changed
1166 // map -> grid
1171 // check for heavy work
1182 {$IF DEFINED(D2F_DEBUG_MOVER)}
1183 e_WriteLog(Format('proxy #%d: checkmove: xg=%d;yg=%d;w=%d;h=%d;nx=%d;ny=%d og:(%d,%d)-(%d,%d); ng:(%d,%d)-(%d,%d)', [body, x0, y0, pw, ph, nx, ny, ogx0, ogy0, ogx1, ogy1, ngx0, ngy0, ngx1, ngy1]), MSG_NOTIFY);
1184 {$ENDIF}
1186 begin
1187 // crossed tile boundary, do heavy work
1190 // cycle with old rect, remove body where it is necessary
1191 // optimized for horizontal moves
1192 {$IF DEFINED(D2F_DEBUG_MOVER)}
1193 e_WriteLog(Format('proxy #%d: xg=%d;yg=%d;w=%d;h=%d;nx=%d;ny=%d og:(%d,%d)-(%d,%d); ng:(%d,%d)-(%d,%d)', [body, x0, y0, pw, ph, nx, ny, ogx0, ogy0, ogx1, ogy1, ngx0, ngy0, ngx1, ngy1]), MSG_NOTIFY);
1194 {$ENDIF}
1195 // remove stale marks
1198 begin
1203 {$IF DEFINED(D2F_DEBUG_MOVER)}
1205 {$ENDIF}
1207 begin
1209 begin
1210 // this column is completely outside of new rect
1212 begin
1213 {$IF DEFINED(D2F_DEBUG_MOVER)}
1215 {$ENDIF}
1218 end
1219 else
1220 begin
1221 // heavy checks
1223 begin
1225 begin
1226 {$IF DEFINED(D2F_DEBUG_MOVER)}
1228 {$ENDIF}
1235 // cycle with new rect, add body where it is necessary
1238 begin
1243 {$IF DEFINED(D2F_DEBUG_MOVER)}
1245 {$ENDIF}
1247 begin
1249 begin
1250 // this column is completely outside of old rect
1252 begin
1253 {$IF DEFINED(D2F_DEBUG_MOVER)}
1255 {$ENDIF}
1258 end
1259 else
1260 begin
1261 // heavy checks
1263 begin
1265 begin
1266 {$IF DEFINED(D2F_DEBUG_MOVER)}
1268 {$ENDIF}
1275 // done
1276 end
1277 else
1278 begin
1279 {$IF DEFINED(D2F_DEBUG_MOVER)}
1280 e_WriteLog(Format('proxy #%d: GRID OK: xg=%d;yg=%d;w=%d;h=%d;nx=%d;ny=%d og:(%d,%d)-(%d,%d); ng:(%d,%d)-(%d,%d)', [body, x0, y0, pw, ph, nx, ny, ogx0, ogy0, ogx1, ogy1, ngx0, ngy0, ngx1, ngy1]), MSG_NOTIFY);
1281 {$ENDIF}
1283 // update coordinates
1290 var
1293 begin
1295 // check if tile coords was changed
1301 {$IF DEFINED(D2F_DEBUG_MOVER)}
1302 e_WriteLog(Format('proxy #%d: RESIZE: xg=%d;yg=%d;w=%d;h=%d;nw=%d;nh=%d', [body, x0, y0, w, h, nw, nh]), MSG_NOTIFY);
1303 {$ENDIF}
1306 begin
1307 // crossed tile boundary, do heavy work
1308 //removeInternal(body);
1312 //insertInternal(body);
1314 end
1315 else
1316 begin
1317 // nothing to do with the grid, just fix size
1324 // ////////////////////////////////////////////////////////////////////////// //
1326 var
1328 begin
1331 if (x >= 0) and (y >= 0) and (x < mWidth*mTileSize) and (y < mHeight*mTileSize) then ccidx := mGrid[(y div mTileSize)*mWidth+(x div mTileSize)];
1336 // ////////////////////////////////////////////////////////////////////////// //
1337 // no callback: return `true` on the first hit
1338 function TBodyGridBase.forEachAtPoint (x, y: Integer; tagmask: Integer=-1; allowDisabled: Boolean=false; firstHit: Boolean=false): Iter;
1339 var
1347 begin
1352 {$IF DEFINED(D2F_DEBUG_XXQ)}
1354 {$ENDIF}
1356 // make coords (0,0)-based
1359 if (x < 0) or (y < 0) or (x >= mWidth*mTileSize) or (y >= mHeight*mTileSize) then begin result.finishIt(); exit; end;
1363 {$IF DEFINED(D2F_DEBUG_XXQ)}
1364 if (assigned(cb)) then e_WriteLog(Format('1: grid pointquery: (%d,%d) (%d,%d) %d', [x, y, (x div mTileSize), (y div mTileSize), curci]), MSG_NOTIFY);
1365 {$ENDIF}
1367 // restore coords
1371 // increase query counter
1374 begin
1375 // just in case of overflow
1381 {$IF DEFINED(D2F_DEBUG_XXQ)}
1382 if (assigned(cb)) then e_WriteLog(Format('2: grid pointquery: (%d,%d); lq=%u', [x, y, lq]), MSG_NOTIFY);
1383 {$ENDIF}
1386 begin
1387 {$IF DEFINED(D2F_DEBUG_XXQ)}
1388 //if (assigned(cb)) then e_WriteLog(Format(' cell #%d', [curci]), MSG_NOTIFY);
1389 {$ENDIF}
1392 begin
1395 {$IF DEFINED(D2F_DEBUG_XXQ)}
1396 //if (assigned(cb)) then e_WriteLog(Format(' proxy #%d; qm:%u; tag:%08x; tagflag:%d %u', [cc.bodies[f], px.mQueryMark, px.mTag, (px.mTag and tagmask), LongWord(px.mObj)]), MSG_NOTIFY);
1397 {$ENDIF}
1404 begin
1416 // ////////////////////////////////////////////////////////////////////////// //
1417 // no callback: return `true` on the first hit
1418 // return number of ITP thingys put into frame pool
1419 function TBodyGridBase.forEachInAABB (x, y, w, h: Integer; tagmask: Integer=-1; allowDisabled: Boolean=false; firstHit: Boolean=false): Iter;
1420 var
1433 begin
1435 begin
1437 exit;
1449 // fix coords
1464 // clip rect
1471 // has something to do
1473 // increase query counter
1476 begin
1477 // just in case of overflow
1481 //e_WriteLog(Format('grid: query #%d: (%d,%d)-(%dx%d)', [mLastQuery, minx, miny, maxx, maxy]), MSG_NOTIFY);
1484 // go on
1486 begin
1488 begin
1489 // process cells
1492 begin
1495 begin
1498 // shit! has to do it this way, so i can change tag in callback
1518 // ////////////////////////////////////////////////////////////////////////// //
1519 function TBodyGridBase.forEachAlongLine (ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1; log: Boolean=false): Iter;
1520 var
1531 //px0, py0, px1, py1: Integer;
1533 begin
1544 // make query coords (0,0)-based
1553 // increase query counter
1556 begin
1557 // just in case of overflow
1563 repeat
1565 // check tile
1567 // process cells
1569 begin
1572 begin
1577 begin
1583 // next cell
1586 // done processing cells, move to next tile
1592 // ////////////////////////////////////////////////////////////////////////// //
1593 // trace box with the given velocity; return object hit (if any)
1594 function TBodyGridBase.traceBox (out ex, ey: Integer; const ax0, ay0, aw, ah: Integer; const dx, dy: Integer; tagmask: Integer=-1): ITP;
1595 var
1606 begin
1620 if (cx1 < 0) or (cy1 < 0) or (cx0 >= mWidth*mTileSize) or (cy0 >= mHeight*mTileSize) then exit;
1626 // just in case
1629 // increase query counter
1632 begin
1633 // just in case of overflow
1640 begin
1642 begin
1645 begin
1648 begin
1653 begin
1655 if not sweepAABB(ax0, ay0, aw, ah, dx, dy, px.mX, px.mY, px.mWidth, px.mHeight, @u0) then continue;
1657 begin
1662 begin
1665 exit;
1670 // next cell
1677 begin
1680 // just in case, compensate for floating point inexactness
1681 if (ex >= hitpx.mX) and (ey >= hitpx.mY) and (ex < hitpx.mX+hitpx.mWidth) and (ey < hitpx.mY+hitpx.mHeight) then
1682 begin
1690 // ////////////////////////////////////////////////////////////////////////// //
1691 {.$DEFINE D2F_DEBUG_OTR}
1692 function TBodyGridBase.traceOrthoRayWhileIn (out ex, ey: Integer; ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): Boolean;
1693 var
1704 {$IF DEFINED(D2F_DEBUG_OTR)}
1706 {$ENDIF}
1708 begin
1724 // offset query coords to (0,0)-based
1731 begin
1733 // vertical
1735 begin
1736 // down
1738 //if (ay0 < 0) then ay0 := 0;
1742 end
1743 else
1744 begin
1745 // up
1747 //if (ay1 < 0) then ay1 := 0;
1752 // check tile
1754 begin
1760 begin
1763 begin
1769 begin
1770 // bound c0 and c1 to cell
1773 // fill the thing
1774 {$IF DEFINED(D2F_DEBUG_OTR)}
1775 e_LogWritefln('**px.y0=%s; px.y1=%s; c0=%s; c1=%s; celly0=%s; celly1=%s; [%s..%s]', [px.y0-miny, px.y1-miny, c0, c1, celly0, celly1, c0-celly0, (c0-celly0)+(c1-c0)]);
1776 {$ENDIF}
1777 //assert(c0 <= c1);
1781 // next cell
1784 {$IF DEFINED(D2F_DEBUG_OTR)}
1785 s := formatstrf(' x=%s; ay0=%s; ay1=%s; y0=%s; celly0=%s; celly1=%s; dy=%s; [', [ax0, ay0, ay1, y0, celly0, celly1, dy]);
1789 {$ENDIF}
1790 // now go till we hit cell boundary or empty space
1792 begin
1793 // up
1795 begin
1796 {$IF DEFINED(D2F_DEBUG_OTR)}
1797 e_LogWritefln(' filled: cdy=%s; y0=%s; celly0=%s; ay0=%s; ay1=%s', [y0-celly0, y0, celly0, ay0, ay1]);
1798 {$ENDIF}
1802 {$IF DEFINED(D2F_DEBUG_OTR)}
1803 e_LogWritefln(' span done: cdy=%s; y0=%s; celly0=%s; ay0=%s; ay1=%s', [y0-celly0, y0, celly0, ay0, ay1]);
1804 {$ENDIF}
1806 if (y0 >= celly0) then begin ey := ay0+1; {assert(forEachAtPoint(ex, ey, nil, tagmask) <> nil);} result := true; exit; end;
1807 end
1808 else
1809 begin
1810 // down
1816 end
1817 else
1818 begin
1819 // horizontal
1825 // ////////////////////////////////////////////////////////////////////////// //
1827 var
1829 begin
1834 // you are not supposed to understand this
1835 function TBodyGridBase.traceRay (out ex, ey: Integer; const ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): ITP;
1836 var
1851 begin
1861 // make query coords (0,0)-based
1872 {$IF DEFINED(D2F_DEBUG)}
1873 //if assigned(dbgRayTraceTileHitCB) then e_LogWritefln('*** traceRay: (%s,%s)-(%s,%s)', [x0, y0, x1, y1]);
1874 {$ENDIF}
1876 // increase query counter
1879 begin
1880 // just in case of overflow
1886 repeat
1888 {$IF DEFINED(D2F_DEBUG)}
1890 {$ENDIF}
1891 // check tile
1893 // process cells
1896 begin
1899 begin
1904 begin
1906 // get adjusted proxy coords
1911 {$IF DEFINED(D2F_DEBUG)}
1912 //if assigned(dbgRayTraceTileHitCB) then e_LogWritefln(' cxy=(%s,%s); pan=(%s,%s)-(%s,%s)', [cx, cy, px0, py0, px1, py1]);
1913 {$ENDIF}
1914 // inside?
1916 begin
1917 // oops
1921 {$IF DEFINED(D2F_DEBUG)}
1923 {$ENDIF}
1924 exit;
1926 // do line-vs-aabb test
1928 begin
1929 // hit detected
1931 {$IF DEFINED(D2F_DEBUG)}
1932 //if assigned(dbgRayTraceTileHitCB) then e_LogWritefln(' hit=(%s,%s); distSq=%s; lastDistSq=%s', [hx, hy, distSq, lastDistSq]);
1933 {$ENDIF}
1935 begin
1945 // next cell
1948 // done processing cells; exit if we registered a hit
1949 // next cells can't have better candidates, obviously
1952 // move to next tile