GXS.TilePlane.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.TilePlane;
  5. (* Implements a tiled texture plane *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. GXS.XOpenGL,
  12. GXS.Scene,
  13. Stage.VectorGeometry,
  14. GXS.Context,
  15. GXS.Material,
  16. GXS.Objects,
  17. GXS.PersistentClasses,
  18. GXS.VectorLists,
  19. GXS.RenderContextInfo;
  20. type
  21. { Stores row information for a tiled area. }
  22. TgxTiledAreaRow = class(TgxPersistentObject)
  23. private
  24. FColMin, FColMax: Integer;
  25. FData: TgxIntegerList;
  26. protected
  27. procedure SetColMin(const val: Integer);
  28. procedure SetColMax(const val: Integer);
  29. function GetCell(col: Integer): Integer;
  30. procedure SetCell(col, val: Integer);
  31. public
  32. constructor Create; override;
  33. destructor Destroy; override;
  34. procedure WriteToFiler(writer: TgxVirtualWriter); override;
  35. procedure ReadFromFiler(reader: TgxVirtualReader); override;
  36. property Cell[col: Integer]: Integer read GetCell write SetCell; default;
  37. property ColMin: Integer read FColMin write SetColMin;
  38. property ColMax: Integer read FColMax write SetColMax;
  39. property Data: TgxIntegerList read FData;
  40. procedure Pack;
  41. function Empty: Boolean;
  42. procedure RemapTiles(remapList: TgxIntegerList);
  43. end;
  44. { Stores tile information in a tiled area.
  45. Each tile stores an integer value with zero the default value,
  46. assumed as "empty". }
  47. TgxTiledArea = class(TgxPersistentObject)
  48. private
  49. FRowMin, FRowMax: Integer;
  50. FRows: TgxPersistentObjectList;
  51. protected
  52. procedure SetRowMin(const val: Integer);
  53. procedure SetRowMax(const val: Integer);
  54. function GetTile(col, row: Integer): Integer;
  55. procedure SetTile(col, row, val: Integer);
  56. function GetRow(index: Integer): TgxTiledAreaRow;
  57. public
  58. constructor Create; override;
  59. destructor Destroy; override;
  60. procedure WriteToFiler(writer: TgxVirtualWriter); override;
  61. procedure ReadFromFiler(reader: TgxVirtualReader); override;
  62. property Tile[col, row: Integer]: Integer read GetTile
  63. write SetTile; default;
  64. property row[index: Integer]: TgxTiledAreaRow read GetRow;
  65. property RowMin: Integer read FRowMin write SetRowMin;
  66. property RowMax: Integer read FRowMax write SetRowMax;
  67. procedure Pack;
  68. procedure Clear;
  69. function Empty: Boolean;
  70. procedure RemapTiles(remapList: TgxIntegerList);
  71. end;
  72. { A tiled textured plane.
  73. This plane object stores and displays texture tiles that composes it,
  74. and is optimized to minimize texture switches when rendering.
  75. Its bounding dimensions are determined by its painted tile. }
  76. TgxTilePlane = class(TgxImmaterialSceneObject)
  77. private
  78. FNoZWrite: Boolean;
  79. FTiles: TgxTiledArea;
  80. FMaterialLibrary: TgxMaterialLibrary;
  81. FSortByMaterials: Boolean;
  82. protected
  83. procedure SetNoZWrite(const val: Boolean);
  84. procedure SetTiles(const val: TgxTiledArea);
  85. procedure SetMaterialLibrary(const val: TgxMaterialLibrary);
  86. procedure SetSortByMaterials(const val: Boolean);
  87. procedure Notification(AComponent: TComponent;
  88. Operation: TOperation); override;
  89. public
  90. constructor Create(AOwner: TComponent); override;
  91. destructor Destroy; override;
  92. procedure DoRender(var ARci: TgxRenderContextInfo;
  93. ARenderSelf, ARenderChildren: Boolean); override;
  94. procedure BuildList(var rci: TgxRenderContextInfo); override;
  95. { Access to the TiledArea data }
  96. property Tiles: TgxTiledArea read FTiles write SetTiles;
  97. { Controls the sorting of tiles by material.
  98. This property should ideally be left always at its default, True,
  99. except for debugging and performance measurement, which is why
  100. it's only public and not published. }
  101. property SortByMaterials: Boolean read FSortByMaterials
  102. write SetSortByMaterials;
  103. published
  104. { If True the tiles are rendered without writing to the ZBuffer. }
  105. property NoZWrite: Boolean read FNoZWrite write SetNoZWrite;
  106. { Material library where tiles materials will be stored/retrieved.
  107. The lower 16 bits of the tile integer value is understood as being
  108. the index of the tile's material in the library (material of
  109. index zero is thus unused). }
  110. property MaterialLibrary: TgxMaterialLibrary read FMaterialLibrary
  111. write SetMaterialLibrary;
  112. end;
  113. //=================================================================
  114. implementation
  115. //=================================================================
  116. // ------------------
  117. // ------------------ TgxTiledAreaRow ------------------
  118. // ------------------
  119. constructor TgxTiledAreaRow.Create;
  120. begin
  121. inherited;
  122. FData := TgxIntegerList.Create;
  123. FColMin := 0;
  124. FColMax := -1;
  125. end;
  126. destructor TgxTiledAreaRow.Destroy;
  127. begin
  128. FData.Free;
  129. inherited;
  130. end;
  131. procedure TgxTiledAreaRow.WriteToFiler(writer: TgxVirtualWriter);
  132. begin
  133. inherited WriteToFiler(writer);
  134. with writer do
  135. begin
  136. WriteInteger(0); // Archive Version 0
  137. WriteInteger(FColMin);
  138. FData.WriteToFiler(writer);
  139. end;
  140. end;
  141. procedure TgxTiledAreaRow.ReadFromFiler(reader: TgxVirtualReader);
  142. var
  143. archiveVersion: Integer;
  144. begin
  145. inherited ReadFromFiler(reader);
  146. archiveVersion := reader.ReadInteger;
  147. if archiveVersion = 0 then
  148. with reader do
  149. begin
  150. FColMin := ReadInteger;
  151. FData.ReadFromFiler(reader);
  152. FColMax := FColMin + FData.Count - 1;
  153. end;
  154. end;
  155. procedure TgxTiledAreaRow.Pack;
  156. var
  157. i, startSkip: Integer;
  158. begin
  159. startSkip := MaxInt;
  160. for i := 0 to FData.Count - 1 do
  161. begin
  162. if FData.List^[i] <> 0 then
  163. begin
  164. startSkip := i;
  165. Break;
  166. end;
  167. end;
  168. if startSkip = MaxInt then
  169. begin
  170. FData.Clear;
  171. FColMax := ColMin - 1;
  172. end
  173. else
  174. begin
  175. for i := FData.Count - 1 downto 0 do
  176. begin
  177. if FData.List^[i] <> 0 then
  178. begin
  179. FData.Count := i + 1;
  180. FColMax := FColMin + FData.Count - 1;
  181. Break;
  182. end;
  183. end;
  184. if startSkip > 0 then
  185. begin
  186. FData.DeleteItems(0, startSkip);
  187. FColMin := FColMin + startSkip;
  188. end;
  189. end;
  190. end;
  191. function TgxTiledAreaRow.Empty: Boolean;
  192. begin
  193. Result := (FData.Count = 0);
  194. end;
  195. procedure TgxTiledAreaRow.RemapTiles(remapList: TgxIntegerList);
  196. var
  197. i, k: Integer;
  198. begin
  199. for i := 0 to FData.Count - 1 do
  200. begin
  201. k := FData[i];
  202. if Cardinal(k) < Cardinal(remapList.Count) then
  203. FData[i] := remapList[k]
  204. else
  205. FData[i] := 0;
  206. end;
  207. end;
  208. // SetColMax
  209. //
  210. procedure TgxTiledAreaRow.SetColMax(const val: Integer);
  211. begin
  212. if val >= ColMin then
  213. FData.Count := val - ColMin + 1
  214. else
  215. FData.Clear;
  216. FColMax := val;
  217. end;
  218. // SetColMin
  219. //
  220. procedure TgxTiledAreaRow.SetColMin(const val: Integer);
  221. begin
  222. if ColMax >= val then
  223. begin
  224. if val < ColMin then
  225. FData.InsertNulls(0, ColMin - val)
  226. else
  227. FData.DeleteItems(0, val - ColMin);
  228. end
  229. else
  230. FData.Clear;
  231. FColMin := val;
  232. end;
  233. // GetCell
  234. //
  235. function TgxTiledAreaRow.GetCell(col: Integer): Integer;
  236. begin
  237. if (col >= ColMin) and (col <= ColMax) then
  238. Result := FData[col - ColMin]
  239. else
  240. Result := 0;
  241. end;
  242. // SetCell
  243. //
  244. procedure TgxTiledAreaRow.SetCell(col, val: Integer);
  245. var
  246. i: Integer;
  247. begin
  248. i := col - ColMin;
  249. if Cardinal(i) >= Cardinal(FData.Count) then
  250. begin
  251. if ColMin <= ColMax then
  252. begin
  253. if col < ColMin then
  254. ColMin := col;
  255. if col > ColMax then
  256. ColMax := col;
  257. end
  258. else
  259. begin
  260. FColMin := col;
  261. FColMax := col;
  262. FData.Add(val);
  263. Exit;
  264. end;
  265. end;
  266. FData[col - ColMin] := val;
  267. end;
  268. // ------------------
  269. // ------------------ TgxTiledArea ------------------
  270. // ------------------
  271. // Create
  272. //
  273. constructor TgxTiledArea.Create;
  274. begin
  275. inherited;
  276. FRows := TgxPersistentObjectList.Create;
  277. FRowMax := -1;
  278. end;
  279. // Destroy
  280. //
  281. destructor TgxTiledArea.Destroy;
  282. begin
  283. FRows.CleanFree;
  284. inherited;
  285. end;
  286. // WriteToFiler
  287. //
  288. procedure TgxTiledArea.WriteToFiler(writer: TgxVirtualWriter);
  289. begin
  290. inherited WriteToFiler(writer);
  291. with writer do
  292. begin
  293. WriteInteger(0); // Archive Version 0
  294. WriteInteger(FRowMin);
  295. FRows.WriteToFiler(writer);
  296. end;
  297. end;
  298. // ReadFromFiler
  299. //
  300. procedure TgxTiledArea.ReadFromFiler(reader: TgxVirtualReader);
  301. var
  302. archiveVersion: Integer;
  303. begin
  304. inherited ReadFromFiler(reader);
  305. archiveVersion := reader.ReadInteger;
  306. if archiveVersion = 0 then
  307. with reader do
  308. begin
  309. FRowMin := ReadInteger;
  310. FRows.ReadFromFiler(reader);
  311. FRowMax := FRowMin + FRows.Count - 1;
  312. end;
  313. end;
  314. // Pack
  315. //
  316. procedure TgxTiledArea.Pack;
  317. var
  318. i, firstNonNil, lastNonNil: Integer;
  319. r: TgxTiledAreaRow;
  320. begin
  321. // pack all rows, free empty ones, determine 1st and last non-nil
  322. lastNonNil := -1;
  323. firstNonNil := FRows.Count;
  324. for i := 0 to FRows.Count - 1 do
  325. begin
  326. r := TgxTiledAreaRow(FRows.List^[i]);
  327. if Assigned(r) then
  328. begin
  329. r.Pack;
  330. if r.FData.Count = 0 then
  331. begin
  332. r.Free;
  333. FRows.List^[i] := nil;
  334. end;
  335. end;
  336. if Assigned(r) then
  337. begin
  338. lastNonNil := i;
  339. if i < firstNonNil then
  340. firstNonNil := i;
  341. end;
  342. end;
  343. if lastNonNil >= 0 then
  344. begin
  345. FRows.Count := lastNonNil + 1;
  346. FRowMax := FRowMin + FRows.Count - 1;
  347. if firstNonNil > 0 then
  348. begin
  349. FRowMin := FRowMin + firstNonNil;
  350. FRows.DeleteItems(0, firstNonNil);
  351. end;
  352. end
  353. else
  354. FRows.Clear;
  355. end;
  356. // Clear
  357. //
  358. procedure TgxTiledArea.Clear;
  359. begin
  360. FRows.Clean;
  361. FRowMin := 0;
  362. FRowMax := -1;
  363. end;
  364. // Empty
  365. //
  366. function TgxTiledArea.Empty: Boolean;
  367. begin
  368. Result := (FRows.Count = 0);
  369. end;
  370. // RemapTiles
  371. //
  372. procedure TgxTiledArea.RemapTiles(remapList: TgxIntegerList);
  373. var
  374. i: Integer;
  375. r: TgxTiledAreaRow;
  376. begin
  377. for i := 0 to FRows.Count - 1 do
  378. begin
  379. r := TgxTiledAreaRow(FRows[i]);
  380. if Assigned(r) then
  381. r.RemapTiles(remapList);
  382. end;
  383. end;
  384. // GetTile
  385. //
  386. function TgxTiledArea.GetTile(col, row: Integer): Integer;
  387. var
  388. i: Integer;
  389. r: TgxTiledAreaRow;
  390. begin
  391. i := row - RowMin;
  392. if Cardinal(i) < Cardinal(FRows.Count) then
  393. begin
  394. r := TgxTiledAreaRow(FRows[row - RowMin]);
  395. if Assigned(r) then
  396. Result := r.Cell[col]
  397. else
  398. Result := 0;
  399. end
  400. else
  401. Result := 0;
  402. end;
  403. // SetTile
  404. //
  405. procedure TgxTiledArea.SetTile(col, row, val: Integer);
  406. var
  407. r: TgxTiledAreaRow;
  408. begin
  409. if row < RowMin then
  410. RowMin := row;
  411. if row > RowMax then
  412. RowMax := row;
  413. r := TgxTiledAreaRow(FRows[row - RowMin]);
  414. if not Assigned(r) then
  415. begin
  416. r := TgxTiledAreaRow.Create;
  417. FRows[row - RowMin] := r;
  418. end;
  419. r.Cell[col] := val;
  420. end;
  421. // GetRow
  422. //
  423. function TgxTiledArea.GetRow(index: Integer): TgxTiledAreaRow;
  424. begin
  425. index := index - RowMin;
  426. if Cardinal(index) < Cardinal(FRows.Count) then
  427. Result := TgxTiledAreaRow(FRows[index])
  428. else
  429. Result := nil;
  430. end;
  431. // SetRowMax
  432. //
  433. procedure TgxTiledArea.SetRowMax(const val: Integer);
  434. begin
  435. if val >= RowMin then
  436. begin
  437. if val > RowMax then
  438. FRows.AddNils(val - RowMax)
  439. else
  440. FRows.DeleteAndFreeItems(val - RowMin + 1, FRows.Count);
  441. end
  442. else
  443. FRows.Clean;
  444. FRowMax := val;
  445. end;
  446. // SetRowMin
  447. //
  448. procedure TgxTiledArea.SetRowMin(const val: Integer);
  449. begin
  450. if val <= RowMax then
  451. begin
  452. if val < RowMin then
  453. FRows.InsertNils(0, RowMin - val)
  454. else
  455. FRows.DeleteAndFreeItems(0, val - RowMin);
  456. end
  457. else
  458. FRows.Clean;
  459. FRowMin := val;
  460. end;
  461. // ------------------
  462. // ------------------ TgxTilePlane ------------------
  463. // ------------------
  464. // Create
  465. //
  466. constructor TgxTilePlane.Create(AOwner: TComponent);
  467. begin
  468. inherited Create(AOwner);
  469. FTiles := TgxTiledArea.Create;
  470. FSortByMaterials := True;
  471. end;
  472. // Destroy
  473. //
  474. destructor TgxTilePlane.Destroy;
  475. begin
  476. MaterialLibrary := nil;
  477. FTiles.Free;
  478. inherited;
  479. end;
  480. // SetNoZWrite
  481. //
  482. procedure TgxTilePlane.SetNoZWrite(const val: Boolean);
  483. begin
  484. if FNoZWrite <> val then
  485. begin
  486. FNoZWrite := val;
  487. StructureChanged;
  488. end;
  489. end;
  490. // SetTiles
  491. //
  492. procedure TgxTilePlane.SetTiles(const val: TgxTiledArea);
  493. begin
  494. if val <> FTiles then
  495. begin
  496. FTiles.Assign(val);
  497. StructureChanged;
  498. end;
  499. end;
  500. // SetMaterialLibrary
  501. //
  502. procedure TgxTilePlane.SetMaterialLibrary(const val: TgxMaterialLibrary);
  503. begin
  504. if FMaterialLibrary <> val then
  505. begin
  506. if Assigned(FMaterialLibrary) then
  507. begin
  508. DestroyHandle;
  509. FMaterialLibrary.RemoveFreeNotification(Self);
  510. end;
  511. FMaterialLibrary := val;
  512. if Assigned(FMaterialLibrary) then
  513. FMaterialLibrary.FreeNotification(Self);
  514. StructureChanged;
  515. end;
  516. end;
  517. // SetSortByMaterials
  518. //
  519. procedure TgxTilePlane.SetSortByMaterials(const val: Boolean);
  520. begin
  521. FSortByMaterials := val;
  522. StructureChanged;
  523. end;
  524. // Notification
  525. //
  526. procedure TgxTilePlane.Notification(AComponent: TComponent;
  527. Operation: TOperation);
  528. begin
  529. if Operation = opRemove then
  530. begin
  531. if AComponent = FMaterialLibrary then
  532. MaterialLibrary := nil;
  533. end;
  534. inherited;
  535. end;
  536. // DoRender
  537. //
  538. procedure TgxTilePlane.DoRender(var ARci: TgxRenderContextInfo;
  539. ARenderSelf, ARenderChildren: Boolean);
  540. var
  541. i: Integer;
  542. begin
  543. if (not ListHandleAllocated) and Assigned(FMaterialLibrary) then
  544. begin
  545. for i := 0 to MaterialLibrary.Materials.Count - 1 do
  546. MaterialLibrary.Materials[i].PrepareBuildList;
  547. end;
  548. inherited;
  549. end;
  550. // BuildList
  551. //
  552. procedure TgxTilePlane.BuildList(var rci: TgxRenderContextInfo);
  553. type
  554. TQuadListInfo = packed record
  555. x, y: TgxIntegerList;
  556. end;
  557. procedure IssueQuad(col, row: Integer);
  558. begin
  559. glTexCoord2f(col, row);
  560. glVertex2f(col, row);
  561. glTexCoord2f(col + 1, row);
  562. glVertex2f(col + 1, row);
  563. glTexCoord2f(col + 1, row + 1);
  564. glVertex2f(col + 1, row + 1);
  565. glTexCoord2f(col, row + 1);
  566. glVertex2f(col, row + 1);
  567. end;
  568. var
  569. i, j, row, col, t: Integer;
  570. r: TgxTiledAreaRow;
  571. libMat: TgxLibMaterial;
  572. quadInfos: array of TQuadListInfo;
  573. begin
  574. if MaterialLibrary = nil then
  575. Exit;
  576. // initialize infos
  577. glNormal3fv(@ZVector);
  578. if FNoZWrite then
  579. rci.gxStates.DepthWriteMask := False;
  580. if SortByMaterials then
  581. begin
  582. SetLength(quadInfos, MaterialLibrary.Materials.Count);
  583. for i := 0 to High(quadInfos) do
  584. begin // correction in (i:=0) from (i:=1)
  585. quadInfos[i].x := TgxIntegerList.Create;
  586. quadInfos[i].y := TgxIntegerList.Create;
  587. end;
  588. // collect quads into quadInfos, sorted by material
  589. for row := Tiles.RowMin to Tiles.RowMax do
  590. begin
  591. r := Tiles.row[row];
  592. if Assigned(r) then
  593. begin
  594. for col := r.ColMin to r.ColMax do
  595. begin
  596. t := r.Cell[col] and $FFFF;
  597. if (t > -1) and (t < MaterialLibrary.Materials.Count) then
  598. begin // correction in (t>-1) from (t>0)
  599. quadInfos[t].x.Add(col);
  600. quadInfos[t].y.Add(row);
  601. end;
  602. end;
  603. end;
  604. end;
  605. // render and cleanup
  606. for i := 0 to High(quadInfos) do
  607. begin // correction in (i:=0) from (i:=1)
  608. if quadInfos[i].x.Count > 0 then
  609. begin
  610. libMat := MaterialLibrary.Materials[i];
  611. libMat.Apply(rci);
  612. repeat
  613. glBegin(GL_QUADS);
  614. with quadInfos[i] do
  615. for j := 0 to x.Count - 1 do
  616. IssueQuad(x[j], y[j]);
  617. glEnd;
  618. until not libMat.UnApply(rci);
  619. end;
  620. quadInfos[i].x.Free;
  621. quadInfos[i].y.Free;
  622. end;
  623. end
  624. else
  625. begin
  626. // process all quads in order
  627. for row := Tiles.RowMin to Tiles.RowMax do
  628. begin
  629. r := Tiles.row[row];
  630. if Assigned(r) then
  631. begin
  632. for col := r.ColMin to r.ColMax do
  633. begin
  634. t := r.Cell[col] and $FFFF;
  635. if (t > -1) and (t < MaterialLibrary.Materials.Count) then
  636. begin // correction in (t>-1) from (t>0)
  637. libMat := MaterialLibrary.Materials[t];
  638. libMat.Apply(rci);
  639. repeat
  640. glBegin(GL_QUADS);
  641. IssueQuad(col, row);
  642. glEnd;
  643. until not libMat.UnApply(rci);
  644. end;
  645. end;
  646. end;
  647. end;
  648. end;
  649. if FNoZWrite then
  650. rci.gxStates.DepthWriteMask := True;
  651. end;
  652. // -------------------------------------------------------------
  653. // -------------------------------------------------------------
  654. // -------------------------------------------------------------
  655. initialization
  656. // -------------------------------------------------------------
  657. // -------------------------------------------------------------
  658. // -------------------------------------------------------------
  659. RegisterClasses([TgxTilePlane, TgxTiledAreaRow, TgxTiledArea]);
  660. end.