GXS.ROAMPatch.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.ROAMPatch;
  5. (* Class for managing a ROAM (square) patch *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. System.SysUtils,
  10. Stage.OpenGL4, // GL_ARB_vertex_buffer_object
  11. Stage.VectorTypes,
  12. GXS.XOpenGL,
  13. Stage.VectorGeometry,
  14. GXS.VectorLists,
  15. Stage.Strings,
  16. GXS.HeightData,
  17. GXS.Isolines,
  18. GXS.Context;
  19. type
  20. // Exception use by splitter for SafeTesselation
  21. EGLROAMException = class(Exception);
  22. PROAMTriangleNode = ^TgxROAMTriangleNode;
  23. TgxROAMTriangleNode = packed record
  24. Base, Left, Right: PROAMTriangleNode;
  25. LeftChild, RightChild: PROAMTriangleNode;
  26. end;
  27. TgxROAMRenderPoint = packed record
  28. X, Y: Integer;
  29. Idx: Integer;
  30. end;
  31. TgxROAMPatch = class(TObject)
  32. private
  33. FID: Integer;
  34. FHeightData: TgxHeightData; // Referred, not owned
  35. FHeightRaster: PSmallIntRaster;
  36. FTLNode, FBRNode: PROAMTriangleNode;
  37. FTLVariance, FBRVariance: array of cardinal;
  38. FPatchSize, FTriangleCount: Integer;
  39. FListHandle: TgxListHandle;
  40. FTag: Integer;
  41. FObserverPosition: TAffineVector;
  42. FNorth, FSouth, FWest, FEast: TgxROAMPatch; // neighbours
  43. FHighRes: Boolean;
  44. FMaxDepth: Integer;
  45. FVertexScale, FVertexOffset: TAffineVector;
  46. FTextureScale, FTextureOffset: TAffineVector;
  47. FMaxTLVarianceDepth, FMaxBRVarianceDepth: Integer;
  48. FOcclusionQuery: TgxOcclusionQueryHandle;
  49. FOcclusionSkip, FOcclusionCounter: Integer;
  50. FLastOcclusionTestPassed: Boolean;
  51. FContourInterval: Integer;
  52. FContourWidth: Integer;
  53. protected
  54. procedure SetHeightData(Val: TgxHeightData);
  55. procedure SetOcclusionSkip(Val: Integer);
  56. procedure RenderROAM(Vertices: TgxAffineVectorList;
  57. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList);
  58. procedure RenderAsStrips(Vertices: TgxAffineVectorList;
  59. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList);
  60. public
  61. constructor Create;
  62. destructor Destroy; override;
  63. procedure ComputeVariance(Variance: Integer);
  64. procedure ResetTessellation;
  65. procedure ConnectToTheWest(WestPatch: TgxROAMPatch);
  66. procedure ConnectToTheNorth(NorthPatch: TgxROAMPatch);
  67. // Returns false if MaxCLODTriangles limit is reached(Lin)
  68. function Tesselate: Boolean;
  69. (* AV free version of Tesselate.
  70. When IncreaseTrianglesCapacity is called, all PROAMTriangleNode
  71. values in higher function became invalid due to the memory shifting.
  72. Recursivity is the main problem, that's why SafeTesselate is calling
  73. Tesselate in a try..except. *)
  74. function SafeTesselate: Boolean;
  75. (* Render the patch in high-resolution.
  76. The lists are assumed to have enough capacity to allow AddNC calls
  77. (additions without capacity check). High-resolution renders use
  78. display lists, and are assumed to be made together. *)
  79. procedure RenderHighRes(Vertices: TgxAffineVectorList;
  80. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList; ForceROAM: Boolean);
  81. (* Render the patch by accumulating triangles.
  82. The lists are assumed to have enough capacity to allow AddNC calls
  83. (additions without capacity check).
  84. Once at least autoFlushVertexCount vertices have been accumulated,
  85. perform a FlushAccum *)
  86. procedure RenderAccum(Vertices: TgxAffineVectorList;
  87. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList;
  88. AutoFlushVertexCount: Integer);
  89. // Render all vertices accumulated in the arrays and set their count back to zero.
  90. class procedure FlushAccum(Vertices: TgxAffineVectorList;
  91. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList);
  92. property HeightData: TgxHeightData read FHeightData write SetHeightData;
  93. property VertexScale: TAffineVector read FVertexScale write FVertexScale;
  94. property VertexOffset: TAffineVector read FVertexOffset write FVertexOffset;
  95. property ObserverPosition: TAffineVector read FObserverPosition write FObserverPosition;
  96. property TextureScale: TAffineVector read FTextureScale write FTextureScale;
  97. property TextureOffset: TAffineVector read FTextureOffset write FTextureOffset;
  98. property HighRes: Boolean read FHighRes write FHighRes;
  99. // Number of frames to skip after an occlusion test returned zero pixels.
  100. property OcclusionSkip: Integer read FOcclusionSkip write SetOcclusionSkip;
  101. // Number of frames remaining to next occlusion test.
  102. property OcclusionCounter: Integer read FOcclusionCounter write FOcclusionCounter;
  103. (* Result for the last occlusion test.
  104. Note that this value is updated upon rendering the tile in
  105. non-high-res mode only. *)
  106. property LastOcclusionTestPassed: Boolean read FLastOcclusionTestPassed;
  107. property ID: Integer read FID;
  108. property TriangleCount: Integer read FTriangleCount;
  109. property Tag: Integer read FTag write FTag;
  110. // Distance between contours - zero (default) for no contours
  111. property ContourInterval: Integer read FContourInterval write FContourInterval default 0;
  112. // Width of contours
  113. property ContourWidth: Integer read FContourWidth write FContourWidth default 1;
  114. end;
  115. // Specifies the maximum number of ROAM triangles that may be allocated.
  116. procedure SetROAMTrianglesCapacity(nb: Integer);
  117. function GetROAMTrianglesCapacity: Integer;
  118. // Draw contours on rendering terrain patches
  119. procedure DrawContours(Vertices: TgxAffineVectorList; VertexIndices: TgxIntegerList;
  120. ContourInterval: Integer; ContourWidth: Integer; DecVal: Integer);
  121. implementation // -----------------------------------------------------------
  122. var
  123. FVBOVertHandle, FVBOTexHandle: TgxVBOArrayBufferHandle;
  124. FVBOIndicesHandle: TgxVBOElementArrayHandle;
  125. vNextPatchID: Integer;
  126. vNbTris, vTriangleNodesCapacity: Integer;
  127. vTriangleNodes: array of TgxROAMTriangleNode;
  128. RenderRaster: PSmallIntRaster;
  129. RenderIndices: PIntegerArray;
  130. RenderVertices: TgxAffineVectorList;
  131. RenderTexCoords: TgxTexPointList;
  132. TessMaxVariance: cardinal;
  133. TessMaxDepth: cardinal;
  134. TessCurrentVariance: PIntegerArray;
  135. TessObserverPosX, TessObserverPosY: Integer;
  136. type
  137. TROAMVariancePoint = packed record
  138. X, Y, Z: Integer;
  139. end;
  140. procedure SetROAMTrianglesCapacity(nb: Integer);
  141. begin
  142. vNbTris := 0;
  143. if vTriangleNodesCapacity <> nb then
  144. begin
  145. SetLength(vTriangleNodes, nb);
  146. vTriangleNodesCapacity := nb;
  147. end;
  148. end;
  149. function GetROAMTrianglesCapacity: Integer;
  150. begin
  151. Result := vTriangleNodesCapacity;
  152. end;
  153. procedure DrawContours(Vertices: TgxAffineVectorList; VertexIndices: TgxIntegerList;
  154. ContourInterval: Integer; ContourWidth: Integer; DecVal: Integer);
  155. var
  156. i: Integer;
  157. Contours: TgxAffineVectorList;
  158. CurColor: TVector4f;
  159. begin
  160. if ContourInterval > 0 then
  161. begin
  162. glPolygonOffset(1, 1);
  163. glEnable(GL_POLYGON_OFFSET_FILL);
  164. i := VertexIndices.Count - 3;
  165. Contours := TgxAffineVectorList.Create;
  166. while i >= 0 do
  167. begin
  168. TriangleElevationSegments(Vertices[VertexIndices[i]],
  169. Vertices[VertexIndices[i + 1]], Vertices[VertexIndices[i + 2]],
  170. ContourInterval, Contours);
  171. Dec(i, DecVal);
  172. end;
  173. glPushAttrib(GL_ENABLE_BIT or GL_CURRENT_BIT);
  174. glDisable(GL_TEXTURE_2D);
  175. glLineWidth(ContourWidth);
  176. glGetFloatv(GL_CURRENT_COLOR, @CurColor);
  177. glColor4f(0, 0, 0, 1);
  178. glBegin(GL_LINES);
  179. for i := 0 to Contours.Count - 1 do
  180. glVertex3fv(@Contours.List[i]);
  181. glEnd;
  182. glColor4fv(@CurColor);
  183. glPopAttrib;
  184. Contours.Free;
  185. end;
  186. end;
  187. // The result is the delta between the old address of the array and the new one
  188. function IncreaseTrianglesCapacity(NewCapacity: Integer): int64;
  189. procedure FixNodePtr(var p: PROAMTriangleNode; const delta: int64);
  190. begin
  191. if p = nil then
  192. exit;
  193. Inc(PByte(p), delta);
  194. end;
  195. var
  196. oldbase, newbase: pointer;
  197. node: PROAMTriangleNode;
  198. i, oldsize: Integer;
  199. begin
  200. Result := 0;
  201. if NewCapacity <= vTriangleNodesCapacity then
  202. exit;
  203. oldsize := vTriangleNodesCapacity;
  204. oldbase := pointer(vTriangleNodes);
  205. SetLength(vTriangleNodes, NewCapacity);
  206. vTriangleNodesCapacity := NewCapacity;
  207. newbase := pointer(vTriangleNodes);
  208. // Array has not been relocated, no need to fix
  209. if oldbase = newbase then
  210. exit;
  211. // go through all the old nodes and fix the pointers
  212. // YP: Delphi needs int64 dual casting to avoid overflow exception
  213. Result := int64(Cardinal(newbase)) - int64(Cardinal(oldbase));
  214. for i := 0 to oldsize - 1 do
  215. begin
  216. node := @vTriangleNodes[i];
  217. FixNodePtr(node^.Base, Result);
  218. FixNodePtr(node^.Left, Result);
  219. FixNodePtr(node^.Right, Result);
  220. FixNodePtr(node^.LeftChild, Result);
  221. FixNodePtr(node^.RightChild, Result);
  222. end;
  223. end;
  224. function AllocTriangleNode: PROAMTriangleNode;
  225. var
  226. nilNode: PROAMTriangleNode;
  227. begin
  228. if vNbTris >= vTriangleNodesCapacity then
  229. begin
  230. // grow by 50%
  231. IncreaseTrianglesCapacity(vTriangleNodesCapacity + (vTriangleNodesCapacity shr 1));
  232. end;
  233. Result := @vTriangleNodes[vNbTris]; ///Result := vNbTris;
  234. with Result^ do
  235. begin
  236. nilNode := nil;
  237. Left := nilNode;
  238. Right := nilNode;
  239. LeftChild := nilNode;
  240. RightChild := nilNode;
  241. end;
  242. Inc(vNbTris);
  243. end;
  244. function Split(tri: PROAMTriangleNode): Boolean;
  245. var
  246. n: Integer;
  247. lc, rc: PROAMTriangleNode;
  248. Shift: int64;
  249. begin
  250. Result := Assigned(tri.LeftChild);
  251. if Result then
  252. exit; // dont split if tri already has a left child
  253. with tri^ do
  254. begin
  255. // If this triangle is not in a proper diamond, force split our base neighbor
  256. if Assigned(Base) and (Base.Base <> tri) then
  257. Split(Base);
  258. n := vNbTris;
  259. end;
  260. if n >= vTriangleNodesCapacity - 1 then
  261. begin
  262. // grow by 50%
  263. Shift := IncreaseTrianglesCapacity(vTriangleNodesCapacity +
  264. (vTriangleNodesCapacity shr 1));
  265. if Shift <> 0 then
  266. begin
  267. raise EGLROAMException.Create
  268. ('PROAMTriangleNode addresses are invalid now');
  269. end;
  270. end;
  271. with tri^ do
  272. begin
  273. // Creates children and cross-link them
  274. lc := @vTriangleNodes[n]; // left child
  275. rc := @vTriangleNodes[n + 1]; // right child
  276. LeftChild := lc;
  277. RightChild := rc;
  278. rc.Base := Right; // right child
  279. rc.LeftChild := nil;
  280. rc.RightChild := LeftChild;
  281. rc.Right := LeftChild;
  282. lc.Base := Left; // left child
  283. lc.LeftChild := nil;
  284. lc.RightChild := LeftChild;
  285. lc.Left := RightChild;
  286. Inc(vNbTris, 2);
  287. // Link our Left Neighbor to the new children
  288. if Assigned(Left) then
  289. if Left.Base = tri then
  290. Left.Base := lc
  291. else if Left.Left = tri then
  292. Left.Left := lc
  293. else
  294. Left.Right := lc;
  295. // Link our Right Neighbor to the new children
  296. if Assigned(Right) then
  297. if Right.Base = tri then
  298. Right.Base := rc
  299. else if Right.Left = tri then
  300. Right.Left := rc
  301. else
  302. Right.Right := rc;
  303. // Link our Base Neighbor to the new children
  304. if Assigned(Base) then
  305. begin
  306. if Assigned(Base.LeftChild) then
  307. begin
  308. Base.LeftChild.Right := RightChild;
  309. RightChild.Left := Base.LeftChild;
  310. Base.RightChild.Left := LeftChild;
  311. LeftChild.Right := Base.RightChild;
  312. end
  313. else
  314. Split(Base);
  315. end
  316. else
  317. begin // An edge triangle, trivial case.
  318. LeftChild.Right := nil;
  319. RightChild.Left := nil;
  320. end;
  321. end;
  322. Result := True;
  323. end;
  324. // ------------------
  325. // ------------------ TgxROAMPatch ------------------
  326. // ------------------
  327. constructor TgxROAMPatch.Create;
  328. begin
  329. inherited Create;
  330. FID := vNextPatchID;
  331. Inc(vNextPatchID);
  332. FListHandle := TgxListHandle.Create;
  333. FContourInterval := 0;
  334. FOcclusionQuery := TgxOcclusionQueryHandle.Create;
  335. end;
  336. destructor TgxROAMPatch.Destroy;
  337. begin
  338. FListHandle.Free;
  339. FOcclusionQuery.Free;
  340. inherited Destroy;
  341. end;
  342. procedure TgxROAMPatch.SetHeightData(Val: TgxHeightData);
  343. begin
  344. FHeightData := Val;
  345. FPatchSize := FHeightData.Size - 1;
  346. FHeightRaster := Val.SmallIntRaster;
  347. end;
  348. procedure TgxROAMPatch.SetOcclusionSkip(Val: Integer);
  349. begin
  350. if Val < 0 then
  351. Val := 0;
  352. if FOcclusionSkip <> Val then
  353. begin
  354. FOcclusionSkip := Val;
  355. FOcclusionQuery.DestroyHandle;
  356. end;
  357. end;
  358. procedure TgxROAMPatch.ConnectToTheWest(WestPatch: TgxROAMPatch);
  359. begin
  360. if Assigned(WestPatch) then
  361. begin
  362. if not(WestPatch.HighRes or HighRes) then
  363. begin
  364. FTLNode.left := westPatch.FBRNode;
  365. westPatch.FBRNode.left := FTLNode;
  366. end;
  367. FWest := WestPatch;
  368. WestPatch.FEast := Self;
  369. end;
  370. end;
  371. procedure TgxROAMPatch.ConnectToTheNorth(NorthPatch: TgxROAMPatch);
  372. begin
  373. if Assigned(NorthPatch) then
  374. begin
  375. if not(NorthPatch.HighRes or HighRes) then
  376. begin
  377. FTLNode.right := northPatch.FBRNode;
  378. northPatch.FBRNode.right := FTLNode;
  379. end;
  380. FNorth := NorthPatch;
  381. NorthPatch.FSouth := Self;
  382. end;
  383. end;
  384. procedure TgxROAMPatch.ComputeVariance(variance: Integer);
  385. var
  386. raster: PSmallIntRaster;
  387. currentVariance: PIntegerArray;
  388. maxVarianceDepth: Integer;
  389. maxNonNullIndex: Integer;
  390. invVariance: Single;
  391. function ROAMVariancePoint(anX, anY: Integer): TROAMVariancePoint;
  392. begin
  393. Result.X := anX;
  394. Result.Y := anY;
  395. Result.Z := (Integer(FHeightRaster[anY][anX]) shl 8);
  396. end;
  397. function RecursComputeVariance(const Left, Right, apex: TROAMVariancePoint;
  398. node: Integer): cardinal;
  399. var
  400. half: TROAMVariancePoint;
  401. v: Cardinal;
  402. n2: Integer;
  403. begin
  404. with half do
  405. begin
  406. X := (Left.X + Right.X) shr 1;
  407. Y := (Left.Y + Right.Y) shr 1;
  408. Z := Integer(raster[Y][X]) shl 8;
  409. Result := ScaleAndRound(Abs(((Left.Z + Right.Z) div 2) - Z), invVariance);
  410. end;
  411. n2 := node shl 1;
  412. if n2 < maxVarianceDepth then
  413. begin
  414. v := RecursComputeVariance(apex, Left, half, n2);
  415. if v > Result then
  416. Result := v;
  417. v := RecursComputeVariance(Right, apex, half, 1 + n2);
  418. if v > Result then
  419. Result := v;
  420. end;
  421. currentVariance[node] := Result;
  422. end;
  423. procedure ScaleVariance(n, d: Integer);
  424. var
  425. newVal: Integer;
  426. begin
  427. if d >= 0 then
  428. newVal := (currentVariance[n] shl (d shr 1))
  429. else
  430. newVal := (currentVariance[n] shr (-d shr 1));
  431. currentVariance[n] := newVal;
  432. if newVal > 0 then
  433. if n > maxNonNullIndex then
  434. maxNonNullIndex := n;
  435. n := n shl 1;
  436. if n < maxVarianceDepth then
  437. begin
  438. Dec(d);
  439. ScaleVariance(n, d);
  440. ScaleVariance(n + 1, d);
  441. end;
  442. end;
  443. var
  444. s, p: Integer;
  445. begin
  446. invVariance := 1 / Variance;
  447. s := Sqr(FPatchSize);
  448. raster := FHeightRaster;
  449. FMaxDepth := 1;
  450. p := -1 - 8;
  451. repeat
  452. FMaxDepth := FMaxDepth shl 2;
  453. Inc(p);
  454. until FMaxDepth >= s;
  455. maxVarianceDepth := FMaxDepth;
  456. SetLength(FTLVariance, maxVarianceDepth);
  457. SetLength(FBRVariance, maxVarianceDepth);
  458. s := FPatchSize;
  459. currentVariance := @FTLVariance[0];
  460. maxNonNullIndex := 1;
  461. RecursComputeVariance(ROAMVariancePoint(0, s), ROAMVariancePoint(s, 0),
  462. ROAMVariancePoint(0, 0), 1);
  463. ScaleVariance(1, p);
  464. FMaxTLVarianceDepth := maxNonNullIndex + 1;
  465. SetLength(FTLVariance, FMaxTLVarianceDepth);
  466. currentVariance := @FBRVariance[0];
  467. maxNonNullIndex := 1;
  468. RecursComputeVariance(ROAMVariancePoint(s, 0), ROAMVariancePoint(0, s),
  469. ROAMVariancePoint(s, s), 1);
  470. ScaleVariance(1, p);
  471. FMaxBRVarianceDepth := maxNonNullIndex + 1;
  472. SetLength(FBRVariance, FMaxBRVarianceDepth);
  473. end;
  474. procedure TgxROAMPatch.ResetTessellation;
  475. begin
  476. FTLNode := AllocTriangleNode;
  477. FBRNode := AllocTriangleNode;
  478. FTLNode.Base := FBRNode;
  479. FTLNode.Left := nil;
  480. FTLNode.Right := nil;
  481. FBRNode.Base := FTLNode;
  482. FBRNode.Left := nil;
  483. FBRNode.Right := nil;
  484. FNorth := nil;
  485. FSouth := nil;
  486. FWest := nil;
  487. FEast := nil;
  488. end;
  489. // returns false if tessellation failed due to MaxCLODTriangles limit
  490. function RecursTessellate(tri: PROAMTriangleNode; n: Cardinal;
  491. const Left, Right, apex: Cardinal): Boolean;
  492. var
  493. d: Integer;
  494. begin
  495. Result := True;
  496. d := ((Left + Right) shr 1);
  497. if TessCurrentVariance[n] > d then
  498. begin
  499. Result := False;
  500. if Split(tri) then
  501. begin
  502. n := n shl 1;
  503. if n < TessMaxVariance then
  504. begin
  505. RecursTessellate(Tri.LeftChild, n, apex, Left, d);
  506. Result := RecursTessellate(Tri.RightChild, n + 1, Right, apex, d);
  507. end;
  508. end;
  509. end;
  510. end;
  511. function TgxROAMPatch.Tesselate: boolean;
  512. var
  513. tessFrameVarianceDelta: Integer;
  514. function VertexDist(X, Y: Integer): cardinal;
  515. var
  516. f: Single;
  517. const
  518. c1Div100: Single = 0.01;
  519. begin
  520. if HighRes then
  521. f := 0.2 * Sqr(FPatchSize)
  522. else
  523. f := Sqr(X - TessObserverPosX) + Sqr(Y - TessObserverPosY) +
  524. tessFrameVarianceDelta;
  525. Result := Round(Sqrt(f) + f * c1Div100);
  526. end;
  527. procedure FullBaseTess(tri: PROAMTriangleNode; n: cardinal); forward;
  528. procedure FullLeftTess(tri: PROAMTriangleNode; n: cardinal);
  529. begin
  530. if Split(tri) then
  531. begin
  532. n := n shl 1;
  533. if n < TessMaxDepth then
  534. FullBaseTess(tri.LeftChild, n);
  535. end;
  536. end;
  537. procedure FullRightTess(tri: PROAMTriangleNode; n: cardinal);
  538. begin
  539. if Split(tri) then
  540. begin
  541. n := n shl 1;
  542. if n < TessMaxDepth then
  543. FullBaseTess(tri.RightChild, n);
  544. end;
  545. end;
  546. procedure FullBaseTess(tri: PROAMTriangleNode; n: cardinal);
  547. begin
  548. if Split(tri) then
  549. begin
  550. n := n shl 1;
  551. if n < TessMaxDepth then
  552. begin
  553. FullRightTess(tri.LeftChild, n);
  554. FullLeftTess(tri.RightChild, n);
  555. end;
  556. end;
  557. end;
  558. var
  559. s: Integer;
  560. begin
  561. TessMaxDepth := FMaxDepth;
  562. TessObserverPosX := Round(FObserverPosition.X);
  563. TessObserverPosY := Round(FObserverPosition.Y);
  564. if HighRes then
  565. begin
  566. FullRightTess(FTLNode, 1);
  567. FullRightTess(FBRNode, 1);
  568. FullLeftTess(FBRNode, 1);
  569. FullLeftTess(FTLNode, 1);
  570. tessFrameVarianceDelta := 0;
  571. end
  572. else
  573. begin
  574. if Assigned(FNorth) and FNorth.HighRes then
  575. FullRightTess(FTLNode, 1);
  576. if Assigned(FSouth) and FSouth.HighRes then
  577. FullRightTess(FBRNode, 1);
  578. if Assigned(FEast) and FEast.HighRes then
  579. FullLeftTess(FBRNode, 1);
  580. if Assigned(FWest) and FWest.HighRes then
  581. FullLeftTess(FTLNode, 1);
  582. if FObserverPosition.Z > 0 then
  583. tessFrameVarianceDelta := Round(Sqr(FObserverPosition.Z * (1 / 16)))
  584. else
  585. tessFrameVarianceDelta := 0;
  586. end;
  587. s := FPatchSize;
  588. TessCurrentVariance := @FTLVariance[0];
  589. TessMaxVariance := FMaxTLVarianceDepth;
  590. Result := RecursTessellate(FTLNode, 1, VertexDist(0, s), VertexDist(s, 0), VertexDist(0, 0));
  591. TessCurrentVariance := @FBRVariance[0];
  592. TessMaxVariance := FMaxBRVarianceDepth;
  593. if Result then
  594. Result := RecursTessellate(FBRNode, 1, VertexDist(s, 0), VertexDist(0, s), VertexDist(s, s));
  595. end;
  596. function TgxROAMPatch.SafeTesselate: boolean;
  597. var
  598. Fail: Boolean;
  599. begin
  600. Result := False;
  601. Fail := True;
  602. repeat
  603. try
  604. Result := Tesselate;
  605. Fail := False;
  606. except
  607. on e: EGLROAMException do
  608. begin
  609. // Nothing to do, just wait the next iteration
  610. Fail := True;
  611. end;
  612. end;
  613. until not Fail;
  614. end;
  615. procedure TgxROAMPatch.RenderHighRes(vertices: TgxAffineVectorList;
  616. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList; ForceROAM: Boolean);
  617. var
  618. Primitive: Cardinal;
  619. begin
  620. // Prepare display list if needed
  621. if FListHandle.Handle = 0 then
  622. begin
  623. // either use brute-force strips or a high-res static tesselation
  624. if ForceROAM then
  625. begin
  626. ResetTessellation;
  627. SafeTesselate;
  628. RenderROAM(Vertices, VertexIndices, TexCoords);
  629. Primitive := GL_TRIANGLES;
  630. FTriangleCount := VertexIndices.Count div 3;
  631. end
  632. else
  633. begin
  634. RenderAsStrips(Vertices, VertexIndices, TexCoords);
  635. Primitive := GL_TRIANGLE_STRIP;
  636. FTriangleCount := VertexIndices.Count - 2 * FPatchSize;
  637. end;
  638. Vertices.Translate(VertexOffset);
  639. TexCoords.ScaleAndTranslate(PTexPoint(@TextureScale)^,
  640. PTexPoint(@TextureOffset)^);
  641. glVertexPointer(3, GL_FLOAT, 0, vertices.List);
  642. glTexCoordPointer(2, GL_FLOAT, 0, texCoords.List);
  643. FListHandle.AllocateHandle;
  644. glNewList(FListHandle.Handle, GL_COMPILE);
  645. glDrawElements(primitive, vertexIndices.Count, GL_UNSIGNED_INT, vertexIndices.List);
  646. glEndList;
  647. DrawContours(Vertices, VertexIndices, FContourInterval, FContourWidth, 1);
  648. Vertices.Count := 0;
  649. TexCoords.Count := 0;
  650. VertexIndices.Count := 0;
  651. end;
  652. // perform the render
  653. glCallList(FListHandle.Handle);
  654. end;
  655. procedure TgxROAMPatch.RenderAccum(vertices: TgxAffineVectorList;
  656. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList;
  657. AutoFlushVertexCount: Integer);
  658. var
  659. OcclusionPassed: Boolean;
  660. n, nb, nvi: Integer;
  661. begin
  662. // CLOD tiles are rendered via ROAM
  663. if (FOcclusionSkip > 0) and FOcclusionQuery.IsSupported then
  664. begin
  665. if FOcclusionQuery.Handle = 0 then
  666. begin
  667. FOcclusionQuery.AllocateHandle;
  668. FOcclusionCounter := -(ID mod (FOcclusionSkip));
  669. end;
  670. OcclusionPassed := (FOcclusionCounter <= 0) or (FOcclusionQuery.PixelCount > 0);
  671. Dec(FOcclusionCounter);
  672. if OcclusionPassed then
  673. begin
  674. if FOcclusionCounter <= 0 then
  675. Inc(FOcclusionCounter, FOcclusionSkip);
  676. FOcclusionQuery.BeginQuery;
  677. end;
  678. end
  679. else
  680. OcclusionPassed := True;
  681. FLastOcclusionTestPassed := OcclusionPassed;
  682. if OcclusionPassed then
  683. begin
  684. nvi := VertexIndices.Count;
  685. n := Vertices.Count;
  686. RenderROAM(Vertices, VertexIndices, TexCoords);
  687. nb := Vertices.Count - n;
  688. FTriangleCount := (VertexIndices.Count - nvi) div 3;
  689. Vertices.Translate(VertexOffset, n, nb);
  690. TexCoords.ScaleAndTranslate(PTexPoint(@TextureScale)^,
  691. PTexPoint(@TextureOffset)^, n, nb);
  692. DrawContours(Vertices, VertexIndices, FContourInterval, FContourWidth, 3);
  693. if FOcclusionQuery.Active then
  694. begin
  695. FlushAccum(Vertices, VertexIndices, TexCoords);
  696. FOcclusionQuery.EndQuery;
  697. end
  698. else if VertexIndices.Count > AutoFlushVertexCount then
  699. FlushAccum(Vertices, VertexIndices, TexCoords);
  700. end
  701. else
  702. FTriangleCount := 0;
  703. end;
  704. class procedure TgxROAMPatch.FlushAccum(vertices: TgxAffineVectorList;
  705. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList);
  706. begin
  707. if VertexIndices.Count = 0 then
  708. Exit;
  709. if GL_ARB_vertex_buffer_object then
  710. begin
  711. FVBOVertHandle.AllocateHandle;
  712. FVBOVertHandle.BindBufferData(Vertices.List, Vertices.DataSize,
  713. GL_STREAM_DRAW_ARB);
  714. glVertexPointer(3, GL_FLOAT, 0, nil);
  715. FVBOTexHandle.AllocateHandle;
  716. FVBOTexHandle.BindBufferData(texCoords.List, texCoords.DataSize,
  717. GL_STREAM_DRAW_ARB);
  718. glTexCoordPointer(2, GL_FLOAT, 0, nil);
  719. glDrawRangeElements(GL_TRIANGLES, 0, vertices.Count - 1,
  720. vertexIndices.Count, GL_UNSIGNED_INT, vertexIndices.List);
  721. glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
  722. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
  723. end
  724. else if GL_EXT_compiled_vertex_array and GL_EXT_draw_range_elements then
  725. begin
  726. glLockArraysEXT(0, vertices.Count);
  727. glDrawRangeElements(GL_TRIANGLES, 0, vertices.Count - 1,
  728. vertexIndices.Count, GL_UNSIGNED_INT, vertexIndices.List);
  729. glUnlockArraysEXT;
  730. end
  731. else
  732. begin
  733. glDrawElements(GL_TRIANGLES, vertexIndices.Count, GL_UNSIGNED_INT, vertexIndices.List);
  734. end;
  735. Vertices.Count := 0;
  736. TexCoords.Count := 0;
  737. VertexIndices.Count := 0;
  738. end;
  739. procedure RecursRender(const tri: PROAMTriangleNode;
  740. const Left, Right, apex: TgxROAMRenderPoint);
  741. var
  742. half: TgxROAMRenderPoint;
  743. LocalIndices: PIntegerArray;
  744. begin
  745. if Assigned(tri.LeftChild) then
  746. begin // = if node is split
  747. half.X := (Left.X + Right.X) shr 1;
  748. half.Y := (Left.Y + Right.Y) shr 1;
  749. RenderTexCoords.AddNC(@half.X);
  750. half.Idx := RenderVertices.AddNC(@half.X, RenderRaster[half.Y][half.X]);
  751. RecursRender(tri.LeftChild, apex, Left, half);
  752. RecursRender(tri.RightChild, Right, apex, half);
  753. end
  754. else
  755. begin
  756. LocalIndices := RenderIndices;
  757. LocalIndices[0] := Left.Idx;
  758. LocalIndices[1] := apex.Idx;
  759. LocalIndices[2] := Right.Idx;
  760. RenderIndices := PIntegerArray(@LocalIndices[3]);
  761. end;
  762. end;
  763. procedure TgxROAMPatch.RenderROAM(Vertices: TgxAffineVectorList;
  764. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList);
  765. procedure ROAMRenderPoint(var p: TgxROAMRenderPoint; anX, anY: Integer);
  766. begin
  767. p.X := anX;
  768. p.Y := anY;
  769. p.Idx := Vertices.Add(anX, anY, RenderRaster[anY][anX]);
  770. TexCoords.Add(anX, anY);
  771. end;
  772. var
  773. rtl, rtr, rbl, rbr: TgxROAMRenderPoint;
  774. begin
  775. RenderVertices := Vertices;
  776. RenderTexCoords := TexCoords;
  777. VertexIndices.AdjustCapacityToAtLeast(Sqr(FPatchSize) * 6 + 15000);
  778. // this is required, the actual item count is maintained out of the list scope
  779. VertexIndices.SetCountResetsMemory := False;
  780. RenderIndices := @VertexIndices.List[VertexIndices.Count];
  781. RenderRaster := FHeightData.SmallIntRaster;
  782. ROAMRenderPoint(rtl, 0, 0);
  783. ROAMRenderPoint(rtr, FPatchSize, 0);
  784. ROAMRenderPoint(rbl, 0, FPatchSize);
  785. ROAMRenderPoint(rbr, FPatchSize, FPatchSize);
  786. RecursRender(FTLNode, rbl, rtr, rtl);
  787. RecursRender(FBRNode, rtr, rbl, rbr);
  788. VertexIndices.Count := (Cardinal(RenderIndices) - Cardinal(VertexIndices.List)) div SizeOf(Integer);
  789. end;
  790. procedure TgxROAMPatch.RenderAsStrips(vertices: TgxAffineVectorList;
  791. VertexIndices: TgxIntegerList; TexCoords: TgxTexPointList);
  792. var
  793. X, Y, baseTop, rowLength: Integer;
  794. p: TAffineVector;
  795. Row: PSmallIntArray;
  796. raster: PSmallIntRaster;
  797. Tex: TTexPoint;
  798. VerticesList: PAffineVector;
  799. TexCoordsList: PTexPoint;
  800. IndicesList: PInteger;
  801. begin
  802. raster := FHeightData.SmallIntRaster;
  803. rowLength := FPatchSize + 1;
  804. // prepare vertex data
  805. Vertices.Count := Sqr(rowLength);
  806. VerticesList := PAffineVector(Vertices.List);
  807. TexCoords.Count := Sqr(rowLength);
  808. TexCoordsList := PTexPoint(TexCoords.List);
  809. for Y := 0 to FPatchSize do
  810. begin
  811. p.Y := Y;
  812. Tex.T := p.Y;
  813. Row := raster[Y];
  814. for X := 0 to FPatchSize do
  815. begin
  816. p.X := X;
  817. Tex.s := p.X;
  818. p.Z := Row[X];
  819. VerticesList^ := p;
  820. Inc(VerticesList);
  821. TexCoordsList^ := Tex;
  822. Inc(TexCoordsList);
  823. end;
  824. end;
  825. // build indices list
  826. baseTop := 0;
  827. VertexIndices.Count := (rowLength * 2 + 2) * FPatchSize - 1;
  828. IndicesList := PInteger(VertexIndices.List);
  829. Y := 0;
  830. while Y < FPatchSize do
  831. begin
  832. if Y > 0 then
  833. begin
  834. IndicesList^ := baseTop + FPatchSize;
  835. Inc(IndicesList);
  836. end;
  837. for X := baseTop + FPatchSize downto baseTop do
  838. begin
  839. IndicesList^ := X;
  840. PIntegerArray(IndicesList)[1] := rowLength + X;
  841. Inc(IndicesList, 2);
  842. end;
  843. IndicesList^ := baseTop + rowLength;
  844. Inc(baseTop, rowLength);
  845. PIntegerArray(IndicesList)[1] := baseTop + rowLength;
  846. Inc(IndicesList, 2);
  847. for X := baseTop to baseTop + FPatchSize do
  848. begin
  849. IndicesList^ := rowLength + X;
  850. PIntegerArray(IndicesList)[1] := X;
  851. Inc(IndicesList, 2);
  852. end;
  853. IndicesList^ := baseTop + FPatchSize;
  854. Inc(IndicesList);
  855. Inc(baseTop, rowLength);
  856. Inc(Y, 2);
  857. end;
  858. VertexIndices.Count := VertexIndices.Count - 1;
  859. end;
  860. // ------------------------------------------------------------------
  861. initialization
  862. // ------------------------------------------------------------------
  863. FVBOVertHandle := TgxVBOArrayBufferHandle.Create;
  864. FVBOTexHandle := TgxVBOArrayBufferHandle.Create;
  865. FVBOIndicesHandle := TgxVBOElementArrayHandle.Create;
  866. finalization //-------------------------------------------------------------
  867. FVBOVertHandle.Free;
  868. FVBOTexHandle.Free;
  869. FVBOIndicesHandle.Free;
  870. SetROAMTrianglesCapacity(0);
  871. // -------------------------------------------------------------------------
  872. end.