2
0

GLS.ROAMPatch.pas 27 KB

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