GLHeightTileFile.pas 21 KB

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