GLHeightTileFile.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLHeightTileFile;
  5. (*
  6. Access to large tiled height data files.
  7. Performance vs Raw file accesses (for perfect tile match):
  8. Cached data:
  9. "Smooth" terrain 1:2 to 1:10
  10. Random terrain 1:1
  11. Non-cached data:
  12. "Smooth" terrain 1:100 to 1:1000
  13. Random terrain 1:100
  14. *)
  15. interface
  16. {$I GLScene.inc}
  17. uses
  18. System.Classes,
  19. System.SysUtils,
  20. GLApplicationFileIO;
  21. type
  22. PByte = ^Byte;
  23. TIntegerArray = array [0 .. MaxInt shr 3] of Integer;
  24. PIntegerArray = ^TIntegerArray;
  25. PInteger = ^Integer;
  26. TSmallIntArray = array [0 .. MaxInt shr 2] of SmallInt;
  27. PSmallIntArray = ^TSmallIntArray;
  28. PSmallInt = ^SmallInt;
  29. TShortIntArray = array [0 .. MaxInt shr 2] of ShortInt;
  30. PShortIntArray = ^TShortIntArray;
  31. PShortInt = ^ShortInt;
  32. TGLHeightTileInfo = packed record
  33. left, top, width, height: Integer;
  34. min, max, average: SmallInt;
  35. fileOffset: Int64; // offset to tile data in the file
  36. end;
  37. PHeightTileInfo = ^TGLHeightTileInfo;
  38. PPHeightTileInfo = ^PHeightTileInfo;
  39. TGLHeightTile = packed record
  40. info: TGLHeightTileInfo;
  41. data: array of SmallInt;
  42. end;
  43. PHeightTile = ^TGLHeightTile;
  44. THTFHeader = packed record
  45. FileVersion: array [0 .. 5] of AnsiChar;
  46. TileIndexOffset: Int64;
  47. SizeX, SizeY: Integer;
  48. TileSize: Integer;
  49. DefaultZ: SmallInt;
  50. end;
  51. const
  52. cHTFHashTableSize = 1023;
  53. cHTFQuadTableSize = 31;
  54. type
  55. // Interfaces a Tiled file
  56. TGLHeightTileFile = class(TObject)
  57. private
  58. FFile: TStream;
  59. FHeader: THTFHeader;
  60. FTileIndex: packed array of TGLHeightTileInfo;
  61. FTileMark: array of Cardinal;
  62. FLastMark: Cardinal;
  63. FHashTable: array [0 .. cHTFHashTableSize] of array of Integer;
  64. FQuadTable: array [0 .. cHTFQuadTableSize, 0 .. cHTFQuadTableSize] of array of Integer;
  65. FCreating: Boolean;
  66. FHeightTile: TGLHeightTile;
  67. FInBuf: array of ShortInt;
  68. protected
  69. function GetTiles(index: Integer): PHeightTileInfo;
  70. function QuadTableX(x: Integer): Integer;
  71. function QuadTableY(y: Integer): Integer;
  72. procedure PackTile(aWidth, aHeight: Integer; src: PSmallIntArray);
  73. procedure UnPackTile(source: PShortIntArray);
  74. property TileIndexOffset: Int64 read FHeader.TileIndexOffset write FHeader.TileIndexOffset;
  75. public
  76. (* Creates a new HTF file.
  77. Read and data access methods are not available when creating. *)
  78. constructor CreateNew(const fileName: String; aSizeX, aSizeY, aTileSize: Integer);
  79. constructor Create(const fileName: String);
  80. destructor Destroy; override;
  81. // Returns tile index for corresponding left/top.
  82. function GetTileIndex(aLeft, aTop: Integer): Integer;
  83. // Returns tile of corresponding left/top.
  84. function GetTile(aLeft, aTop: Integer; pTileInfo: PPHeightTileInfo = nil): PHeightTile;
  85. (* Stores and compresses give tile data.
  86. aLeft and top MUST be a multiple of TileSize, aWidth and aHeight
  87. MUST be lower or equal to TileSize. *)
  88. procedure CompressTile(aLeft, aTop, aWidth, aHeight: Integer; aData: PSmallIntArray);
  89. (* Extract a single row from the HTF file.
  90. This is NOT the fastest way to access HTF data.
  91. All of the row must be contained in the world, otherwise result is undefined. *)
  92. procedure ExtractRow(x, y, len: Integer; dest: PSmallIntArray);
  93. // Returns the tile that contains x and y.
  94. function XYTileInfo(anX, anY: Integer): PHeightTileInfo;
  95. (* Returns the height at given coordinates.
  96. This is definetely NOT the fastest way to access HTF data and should
  97. only be used as utility function. *)
  98. function XYHeight(anX, anY: Integer): SmallInt;
  99. // Clears the list then add all tiles that overlap the rectangular area.
  100. procedure TilesInRect(aLeft, aTop, aRight, aBottom: Integer; destList: TList);
  101. function TileCount: Integer;
  102. property Tiles[index: Integer]: PHeightTileInfo read GetTiles;
  103. function IndexOfTile(aTile: PHeightTileInfo): Integer;
  104. function TileCompressedSize(tileIndex: Integer): Integer;
  105. property SizeX: Integer read FHeader.SizeX;
  106. property SizeY: Integer read FHeader.SizeY;
  107. (* Maximum width and height for a tile.
  108. Actual tiles may not be square, can assume random layouts, and may overlap. *)
  109. property TileSize: Integer read FHeader.TileSize;
  110. property DefaultZ: SmallInt read FHeader.DefaultZ write FHeader.DefaultZ;
  111. end;
  112. // ------------------------------------------------------------------
  113. implementation
  114. // ------------------------------------------------------------------
  115. const
  116. cFileVersion = 'HTF100';
  117. procedure FillSmallInt(p: PSmallInt; count: Integer; v: SmallInt);
  118. var
  119. I: Integer;
  120. begin
  121. for I := count - 1 downto 0 do
  122. begin
  123. p^ := v;
  124. Inc(p);
  125. end;
  126. end;
  127. // ------------------
  128. // ------------------ TGLHeightTileFile ------------------
  129. // ------------------
  130. constructor TGLHeightTileFile.CreateNew(const fileName: String;
  131. aSizeX, aSizeY, aTileSize: Integer);
  132. begin
  133. with FHeader do
  134. begin
  135. FileVersion := cFileVersion;
  136. SizeX := aSizeX;
  137. SizeY := aSizeY;
  138. TileSize := aTileSize;
  139. end;
  140. FFile := TFileStream.Create(fileName, fmCreate);
  141. FFile.Write(FHeader, SizeOf(FHeader));
  142. FCreating := True;
  143. SetLength(FHeightTile.data, aTileSize * aTileSize);
  144. end;
  145. constructor TGLHeightTileFile.Create(const fileName: String);
  146. var
  147. n, I, key, qx, qy: Integer;
  148. begin
  149. FFile := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  150. // Read Header
  151. FFile.Read(FHeader, SizeOf(FHeader));
  152. if FHeader.FileVersion <> cFileVersion then
  153. raise Exception.Create('Invalid file type');
  154. // Read TileIndex
  155. FFile.Position := TileIndexOffset;
  156. FFile.Read(n, 4);
  157. SetLength(FTileIndex, n);
  158. FFile.Read(FTileIndex[0], SizeOf(TGLHeightTileInfo) * n);
  159. // Prepare HashTable & QuadTable
  160. for n := 0 to High(FTileIndex) do
  161. begin
  162. with FTileIndex[n] do
  163. begin
  164. key := left + (top shl 4);
  165. key := ((key and cHTFHashTableSize) + (key shr 10) + (key shr 20)) and
  166. cHTFHashTableSize;
  167. I := Length(FHashTable[key]);
  168. SetLength(FHashTable[key], I + 1);
  169. FHashTable[key][I] := n;
  170. for qx := QuadTableX(left) to QuadTableX(left + width - 1) do
  171. begin
  172. for qy := QuadTableY(top) to QuadTableY(top + height - 1) do
  173. begin
  174. I := Length(FQuadTable[qx, qy]);
  175. SetLength(FQuadTable[qx, qy], I + 1);
  176. FQuadTable[qx, qy][I] := n;
  177. end;
  178. end;
  179. end;
  180. end;
  181. FHeightTile.info.left := MaxInt; // mark as not loaded
  182. SetLength(FHeightTile.data, TileSize * TileSize);
  183. SetLength(FInBuf, TileSize * (TileSize + 1) * 2);
  184. SetLength(FTileMark, Length(FTileIndex));
  185. end;
  186. destructor TGLHeightTileFile.Destroy;
  187. var
  188. n: Integer;
  189. begin
  190. if FCreating then
  191. begin
  192. TileIndexOffset := FFile.Position;
  193. // write tile index
  194. n := Length(FTileIndex);
  195. FFile.Write(n, 4);
  196. FFile.Write(FTileIndex[0], SizeOf(TGLHeightTileInfo) * n);
  197. // write data size
  198. FFile.Position := 0;
  199. FFile.Write(FHeader, SizeOf(FHeader));
  200. end;
  201. FFile.Free;
  202. inherited Destroy;
  203. end;
  204. function TGLHeightTileFile.QuadTableX(x: Integer): Integer;
  205. begin
  206. Result := ((x * (cHTFQuadTableSize + 1)) div (SizeX + 1)) and cHTFQuadTableSize;
  207. end;
  208. function TGLHeightTileFile.QuadTableY(y: Integer): Integer;
  209. begin
  210. Result := ((y * (cHTFQuadTableSize + 1)) div (SizeY + 1)) and cHTFQuadTableSize;
  211. end;
  212. procedure TGLHeightTileFile.PackTile(aWidth, aHeight: Integer; src: PSmallIntArray);
  213. var
  214. packWidth: Integer;
  215. function DiffEncode(src: PSmallIntArray; dest: PShortIntArray): Cardinal;
  216. var
  217. I: Integer;
  218. v, delta: SmallInt;
  219. begin
  220. Result := Cardinal(dest);
  221. v := src[0];
  222. PSmallIntArray(dest)[0] := v;
  223. dest := PShortIntArray(Cardinal(dest) + 2);
  224. I := 1;
  225. while I < packWidth do
  226. begin
  227. delta := src[I] - v;
  228. v := src[I];
  229. if Abs(delta) <= 127 then
  230. begin
  231. dest[0] := ShortInt(delta);
  232. dest := PShortIntArray(Cardinal(dest) + 1);
  233. end
  234. else
  235. begin
  236. dest[0] := -128;
  237. dest := PShortIntArray(Cardinal(dest) + 1);
  238. PSmallIntArray(dest)[0] := v;
  239. dest := PShortIntArray(Cardinal(dest) + 2);
  240. end;
  241. Inc(I);
  242. end;
  243. Result := Cardinal(dest) - Result;
  244. end;
  245. function RLEEncode(src: PSmallIntArray; dest: PAnsiChar): Cardinal;
  246. var
  247. v: SmallInt;
  248. I, n: Integer;
  249. begin
  250. I := 0;
  251. Result := Cardinal(dest);
  252. while (I < packWidth) do
  253. begin
  254. v := src[I];
  255. Inc(I);
  256. n := 0;
  257. PSmallIntArray(dest)[0] := v;
  258. Inc(dest, 2);
  259. while (src[I] = v) and (I < packWidth) do
  260. begin
  261. Inc(n);
  262. if n = 255 then
  263. begin
  264. dest[0] := #255;
  265. Inc(dest);
  266. n := 0;
  267. end;
  268. Inc(I);
  269. end;
  270. if (I < packWidth) or (n > 0) then
  271. begin
  272. dest[0] := AnsiChar(n);
  273. Inc(dest);
  274. end;
  275. end;
  276. Result := Cardinal(dest) - Result;
  277. end;
  278. var
  279. y: Integer;
  280. p: PSmallIntArray;
  281. buf, bestBuf: array of Byte;
  282. bestLength, len: Integer;
  283. leftPack, rightPack: Byte;
  284. bestMethod: Byte; // 0=RAW, 1=Diff, 2=RLE
  285. av: Int64;
  286. v: SmallInt;
  287. begin
  288. SetLength(buf, TileSize * 4); // worst case situation
  289. SetLength(bestBuf, TileSize * 4); // worst case situation
  290. with FHeightTile.info do
  291. begin
  292. min := src[0];
  293. max := src[0];
  294. av := src[0];
  295. for y := 1 to aWidth * aHeight - 1 do
  296. begin
  297. v := src[y];
  298. if v < min then
  299. min := v
  300. else if v > max then
  301. max := v;
  302. av := av + v;
  303. end;
  304. average := av div (aWidth * aHeight);
  305. if min = max then
  306. Exit; // no need to store anything
  307. end;
  308. for y := 0 to aHeight - 1 do
  309. begin
  310. p := @src[aWidth * y];
  311. packWidth := aWidth;
  312. // Lookup leftPack
  313. leftPack := 0;
  314. while (leftPack < 255) and (packWidth > 0) and (p[0] = DefaultZ) do
  315. begin
  316. p := PSmallIntArray(Cardinal(p) + 2);
  317. Dec(packWidth);
  318. Inc(leftPack);
  319. end;
  320. // Lookup rightPack
  321. rightPack := 0;
  322. while (rightPack < 255) and (packWidth > 0) and
  323. (p[packWidth - 1] = DefaultZ) do
  324. begin
  325. Dec(packWidth);
  326. Inc(rightPack);
  327. end;
  328. // Default encoding = RAW
  329. bestLength := packWidth * 2;
  330. bestMethod := 0;
  331. Move(p^, bestBuf[0], bestLength);
  332. // Diff encoding
  333. len := DiffEncode(p, PShortIntArray(@buf[0]));
  334. if len < bestLength then
  335. begin
  336. bestLength := len;
  337. bestMethod := 1;
  338. Move(buf[0], bestBuf[0], bestLength);
  339. end;
  340. // RLE encoding
  341. len := RLEEncode(p, PAnsiChar(@buf[0]));
  342. if len < bestLength then
  343. begin
  344. bestLength := len;
  345. bestMethod := 2;
  346. Move(buf[0], bestBuf[0], bestLength);
  347. end;
  348. // Write to file
  349. if (leftPack or rightPack) = 0 then
  350. begin
  351. FFile.Write(bestMethod, 1);
  352. FFile.Write(bestBuf[0], bestLength);
  353. end
  354. else
  355. begin
  356. if leftPack > 0 then
  357. begin
  358. if rightPack > 0 then
  359. begin
  360. bestMethod := bestMethod + $C0;
  361. FFile.Write(bestMethod, 1);
  362. FFile.Write(leftPack, 1);
  363. FFile.Write(rightPack, 1);
  364. FFile.Write(bestBuf[0], bestLength);
  365. end
  366. else
  367. begin
  368. bestMethod := bestMethod + $80;
  369. FFile.Write(bestMethod, 1);
  370. FFile.Write(leftPack, 1);
  371. FFile.Write(bestBuf[0], bestLength);
  372. end;
  373. end
  374. else
  375. begin
  376. bestMethod := bestMethod + $40;
  377. FFile.Write(bestMethod, 1);
  378. FFile.Write(rightPack, 1);
  379. FFile.Write(bestBuf[0], bestLength);
  380. end;
  381. end;
  382. end;
  383. end;
  384. procedure TGLHeightTileFile.UnPackTile(source: PShortIntArray);
  385. var
  386. unpackWidth, tileWidth: Cardinal;
  387. src: PShortInt;
  388. dest: PSmallInt;
  389. procedure DiffDecode;
  390. var
  391. v: SmallInt;
  392. delta: SmallInt;
  393. locSrc: PShortInt;
  394. destEnd, locDest: PSmallInt;
  395. begin
  396. locSrc := PShortInt(Cardinal(src) - 1);
  397. locDest := dest;
  398. destEnd := PSmallInt(Cardinal(dest) + unpackWidth * 2);
  399. while Cardinal(locDest) < Cardinal(destEnd) do
  400. begin
  401. Inc(locSrc);
  402. v := PSmallInt(locSrc)^;
  403. Inc(locSrc, 2);
  404. locDest^ := v;
  405. Inc(locDest);
  406. while (Cardinal(locDest) < Cardinal(destEnd)) do
  407. begin
  408. delta := locSrc^;
  409. if delta <> -128 then
  410. begin
  411. v := v + delta;
  412. Inc(locSrc);
  413. locDest^ := v;
  414. Inc(locDest);
  415. end
  416. else
  417. Break;
  418. end;
  419. end;
  420. src := locSrc;
  421. dest := locDest;
  422. end;
  423. procedure RLEDecode;
  424. var
  425. n, j: Cardinal;
  426. v: SmallInt;
  427. locSrc: PShortInt;
  428. destEnd, locDest: PSmallInt;
  429. begin
  430. locSrc := src;
  431. locDest := dest;
  432. destEnd := PSmallInt(Cardinal(dest) + unpackWidth * 2);
  433. while Cardinal(locDest) < Cardinal(destEnd) do
  434. begin
  435. v := PSmallIntArray(locSrc)[0];
  436. Inc(locSrc, 2);
  437. repeat
  438. if Cardinal(locDest) = Cardinal(destEnd) - 2 then
  439. begin
  440. locDest^ := v;
  441. Inc(locDest);
  442. n := 0;
  443. end
  444. else
  445. begin
  446. n := Integer(locSrc^ and 255);
  447. Inc(locSrc);
  448. for j := 0 to n do
  449. begin
  450. locDest^ := v;
  451. Inc(locDest);
  452. end;
  453. end;
  454. until (n < 255) or (Cardinal(locDest) >= Cardinal(destEnd));
  455. end;
  456. src := locSrc;
  457. dest := locDest;
  458. end;
  459. var
  460. y: Integer;
  461. n: Byte;
  462. method: Byte;
  463. begin
  464. dest := @FHeightTile.data[0];
  465. with FHeightTile.info do
  466. begin
  467. if min = max then
  468. begin
  469. FillSmallInt(dest, width * height, min);
  470. Exit;
  471. end;
  472. tileWidth := width;
  473. end;
  474. src := PShortInt(source);
  475. n := 0;
  476. for y := 0 to FHeightTile.info.height - 1 do
  477. begin
  478. method := Byte(src^);
  479. Inc(src);
  480. unpackWidth := tileWidth;
  481. // Process left pack if any
  482. if (method and $80) <> 0 then
  483. begin
  484. n := PByte(src)^;
  485. Inc(src);
  486. FillSmallInt(dest, n, DefaultZ);
  487. Dec(unpackWidth, n);
  488. Inc(dest, n);
  489. end;
  490. // Read right pack if any
  491. if (method and $40) <> 0 then
  492. begin
  493. PByte(@n)^ := PByte(src)^;
  494. Inc(src);
  495. Dec(unpackWidth, n)
  496. end
  497. else
  498. n := 0;
  499. // Process main data
  500. case (method and $3F) of
  501. 1:
  502. DiffDecode;
  503. 2:
  504. RLEDecode;
  505. else
  506. Move(src^, dest^, unpackWidth * 2);
  507. Inc(src, unpackWidth * 2);
  508. Inc(dest, unpackWidth);
  509. end;
  510. // Process right pack if any
  511. if n > 0 then
  512. begin
  513. FillSmallInt(dest, n, DefaultZ);
  514. Inc(dest, n);
  515. end;
  516. end;
  517. end;
  518. function TGLHeightTileFile.GetTileIndex(aLeft, aTop: Integer): Integer;
  519. var
  520. I, key, n: Integer;
  521. p: PIntegerArray;
  522. begin
  523. Result := -1;
  524. key := aLeft + (aTop shl 4);
  525. key := ((key and cHTFHashTableSize) + (key shr 10) + (key shr 20)) and
  526. cHTFHashTableSize;
  527. n := Length(FHashTable[key]);
  528. if n > 0 then
  529. begin
  530. p := @FHashTable[key][0];
  531. for I := 0 to n - 1 do
  532. begin
  533. with FTileIndex[p[I]] do
  534. begin
  535. if (left = aLeft) and (top = aTop) then
  536. begin
  537. Result := p[I];
  538. Break;
  539. end;
  540. end;
  541. end;
  542. end;
  543. end;
  544. function TGLHeightTileFile.GetTile(aLeft, aTop: Integer;
  545. pTileInfo: PPHeightTileInfo = nil): PHeightTile;
  546. var
  547. I, n: Integer;
  548. tileInfo: PHeightTileInfo;
  549. begin
  550. with FHeightTile.info do
  551. if (left = aLeft) and (top = aTop) then
  552. begin
  553. Result := @FHeightTile;
  554. if Assigned(pTileInfo) then
  555. pTileInfo^ := @Result.info;
  556. Exit;
  557. end;
  558. I := GetTileIndex(aLeft, aTop);
  559. if I >= 0 then
  560. begin
  561. tileInfo := @FTileIndex[I];
  562. if Assigned(pTileInfo) then
  563. pTileInfo^ := tileInfo;
  564. if I < High(FTileIndex) then
  565. n := FTileIndex[I + 1].fileOffset - tileInfo.fileOffset
  566. else
  567. n := TileIndexOffset - tileInfo.fileOffset;
  568. Result := @FHeightTile;
  569. FHeightTile.info := tileInfo^;
  570. FFile.Position := tileInfo.fileOffset;
  571. FFile.Read(FInBuf[0], n);
  572. UnPackTile(@FInBuf[0]);
  573. end
  574. else
  575. begin
  576. Result := nil;
  577. if Assigned(pTileInfo) then
  578. pTileInfo^ := nil;
  579. end;
  580. end;
  581. procedure TGLHeightTileFile.CompressTile(aLeft, aTop, aWidth, aHeight: Integer;
  582. aData: PSmallIntArray);
  583. begin
  584. Assert(aWidth <= TileSize);
  585. Assert(aHeight <= TileSize);
  586. with FHeightTile.info do
  587. begin
  588. left := aLeft;
  589. top := aTop;
  590. width := aWidth;
  591. height := aHeight;
  592. fileOffset := FFile.Position;
  593. end;
  594. PackTile(aWidth, aHeight, aData);
  595. SetLength(FTileIndex, Length(FTileIndex) + 1);
  596. FTileIndex[High(FTileIndex)] := FHeightTile.info
  597. end;
  598. procedure TGLHeightTileFile.ExtractRow(x, y, len: Integer;
  599. dest: PSmallIntArray);
  600. var
  601. rx: Integer;
  602. n: Cardinal;
  603. tileInfo: PHeightTileInfo;
  604. tile: PHeightTile;
  605. begin
  606. while len > 0 do
  607. begin
  608. tileInfo := XYTileInfo(x, y);
  609. if not Assigned(tileInfo) then
  610. Exit;
  611. rx := x - tileInfo.left;
  612. n := Cardinal(tileInfo.width - rx);
  613. if n > Cardinal(len) then
  614. n := Cardinal(len);
  615. tile := GetTile(tileInfo.left, tileInfo.top);
  616. Move(tile.data[(y - tileInfo.top) * tileInfo.width + rx], dest^, n * 2);
  617. dest := PSmallIntArray(Cardinal(dest) + n * 2);
  618. Dec(len, n);
  619. Inc(x, n);
  620. end;
  621. end;
  622. function TGLHeightTileFile.XYTileInfo(anX, anY: Integer): PHeightTileInfo;
  623. var
  624. tileList: TList;
  625. begin
  626. tileList := TList.Create;
  627. try
  628. TilesInRect(anX, anY, anX + 1, anY + 1, tileList);
  629. if tileList.count > 0 then
  630. Result := PHeightTileInfo(tileList.First)
  631. else
  632. Result := nil;
  633. finally
  634. tileList.Free;
  635. end;
  636. end;
  637. function TGLHeightTileFile.XYHeight(anX, anY: Integer): SmallInt;
  638. var
  639. tileInfo: PHeightTileInfo;
  640. tile: PHeightTile;
  641. begin
  642. // Current tile per chance?
  643. with FHeightTile.info do
  644. begin
  645. if (left <= anX) and (left + width > anX) and (top <= anY) and
  646. (top + height > anY) then
  647. begin
  648. Result := FHeightTile.data[(anX - left) + (anY - top) * width];
  649. Exit;
  650. end;
  651. end;
  652. // Find corresponding tile if any
  653. tileInfo := XYTileInfo(anX, anY);
  654. if Assigned(tileInfo) then
  655. with tileInfo^ do
  656. begin
  657. tile := GetTile(left, top);
  658. Result := tile.data[(anX - left) + (anY - top) * width];
  659. end
  660. else
  661. Result := DefaultZ;
  662. end;
  663. procedure TGLHeightTileFile.TilesInRect(aLeft, aTop, aRight, aBottom: Integer;
  664. destList: TList);
  665. var
  666. I, n, qx, qy, idx: Integer;
  667. p: PIntegerArray;
  668. tileInfo: PHeightTileInfo;
  669. begin
  670. destList.count := 0;
  671. // Clamp to world
  672. if (aLeft > SizeX) or (aRight < 0) or (aTop > SizeY) or (aBottom < 0) then
  673. Exit;
  674. if aLeft < 0 then
  675. aLeft := 0;
  676. if aRight > SizeX then
  677. aRight := SizeX;
  678. if aTop < 0 then
  679. aTop := 0;
  680. if aBottom > SizeY then
  681. aBottom := SizeY;
  682. // Collect tiles on quads
  683. Inc(FLastMark);
  684. for qy := QuadTableY(aTop) to QuadTableY(aBottom) do
  685. begin
  686. for qx := QuadTableX(aLeft) to QuadTableX(aRight) do
  687. begin
  688. n := High(FQuadTable[qx, qy]);
  689. p := @FQuadTable[qx, qy][0];
  690. for I := 0 to n do
  691. begin
  692. idx := p[I];
  693. if FTileMark[idx] <> FLastMark then
  694. begin
  695. FTileMark[idx] := FLastMark;
  696. tileInfo := @FTileIndex[idx];
  697. with tileInfo^ do
  698. begin
  699. if (left <= aRight) and (top <= aBottom) and (aLeft < left + width)
  700. and (aTop < top + height) then
  701. destList.Add(tileInfo);
  702. end;
  703. end;
  704. end;
  705. end;
  706. end;
  707. end;
  708. function TGLHeightTileFile.TileCount: Integer;
  709. begin
  710. Result := Length(FTileIndex);
  711. end;
  712. function TGLHeightTileFile.GetTiles(index: Integer): PHeightTileInfo;
  713. begin
  714. Result := @FTileIndex[index];
  715. end;
  716. function TGLHeightTileFile.IndexOfTile(aTile: PHeightTileInfo): Integer;
  717. var
  718. c: Cardinal;
  719. begin
  720. c := Cardinal(aTile) - Cardinal(@FTileIndex[0]);
  721. if (c mod SizeOf(TGLHeightTileInfo)) = 0 then
  722. begin
  723. Result := (c div SizeOf(TGLHeightTileInfo));
  724. if (Result < 0) or (Result > High(FTileIndex)) then
  725. Result := -1;
  726. end
  727. else
  728. Result := -1;
  729. end;
  730. function TGLHeightTileFile.TileCompressedSize(tileIndex: Integer): Integer;
  731. begin
  732. if tileIndex < High(FTileIndex) then
  733. Result := FTileIndex[tileIndex + 1].fileOffset - FTileIndex[tileIndex]
  734. .fileOffset
  735. else
  736. Result := TileIndexOffset - FTileIndex[tileIndex].fileOffset;
  737. end;
  738. end.