2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Class for managing a ROAM (square) patch.
8
29/12/14 - PW - Fixed SafeTesselation function that caused gaps between tiles
9
22/08/10 - DaStr - Fixed compiler warning
10
27/07/10 - YP - Safe tesselation operation to avoid AV after a memory shift
11
26/07/10 - YP - Invalid range test when splitting, we need to check space for n and n+1
12
20/05/10 - Yar - Fixes for Linux x64
13
16/10/08 - UweR - Compatibility fix for Delphi 2009
14
30/03/07 - DaStr - Added $I GLScene.inc
15
19/10/06 - LC - Added code to gracefully handle the case when MaxCLODTriangles is reached.
16
It will now increase the buffer instead of not splitting. Bugtracker ID=1574111
17
09/10/06 - Lin - Added OnMaxCLODTrianglesReached event.
18
09/06/06 - Lin - Bugfix: Stop splitting Triangles when MaxCLODTriangles is reached (prevents Access Violations)
19
10/06/05 - Mathx - Protection against cards that have GL_EXT_compiled_vertex_array
20
but not GL_EXT_draw_range_elements
21
25/04/04 - EG - Occlusion testing support
22
06/02/03 - EG - Adaptative variance computation
23
03/12/02 - EG - Minor ROAM tessel/render optimizations
24
15/06/02 - EG - Fixed patch rendering bug "introduced" by TBaseList fix
25
24/02/02 - EG - Hybrid ROAM-stripifier engine
26
10/09/01 - EG - Creation
37
GLVectorGeometry, GLHeightData, GLVectorLists, GLCrossPlatform, GLContext,
38
OpenGLTokens, XOpenGL;
42
// Exception use by Split for SafeTesselate
43
EGLROAMException = class(Exception);
47
PROAMTriangleNode = ^TROAMTriangleNode;
49
TROAMTriangleNode = packed record
50
base, left, right: PROAMTriangleNode;
51
leftChild, rightChild: PROAMTriangleNode;
56
TROAMRenderPoint = packed record
63
TGLROAMPatch = class(TObject)
67
FHeightData: TGLHeightData; // Referred, not owned
68
FHeightRaster: PSmallIntRaster;
69
FTLNode, FBRNode: Integer;
70
FTLVariance, FBRVariance: array of cardinal;
71
FPatchSize, FTriangleCount: Integer;
72
FListHandle: TGLListHandle;
74
FObserverPosition: TAffineVector;
75
FNorth, FSouth, FWest, FEast: TGLROAMPatch; // neighbours
78
FVertexScale, FVertexOffset: TAffineVector;
79
FTextureScale, FTextureOffset: TAffineVector;
80
FMaxTLVarianceDepth, FMaxBRVarianceDepth: Integer;
82
FOcclusionQuery: TGLOcclusionQueryHandle;
83
FOcclusionSkip, FOcclusionCounter: Integer;
84
FLastOcclusionTestPassed: Boolean;
88
procedure SeTGLHeightData(val: TGLHeightData);
89
procedure SetOcclusionSkip(val: Integer);
91
procedure RenderROAM(vertices: TAffineVectorList;
92
vertexIndices: TIntegerList; texCoords: TTexPointList);
93
procedure RenderAsStrips(vertices: TAffineVectorList;
94
vertexIndices: TIntegerList; texCoords: TTexPointList);
96
function Tesselate: boolean;
97
// Returns false if MaxCLODTriangles limit is reached(Lin)
101
destructor Destroy; override;
103
procedure ComputeVariance(variance: Integer);
105
procedure ResetTessellation;
106
procedure ConnectToTheWest(westPatch: TGLROAMPatch);
107
procedure ConnectToTheNorth(northPatch: TGLROAMPatch);
109
{ : AV free version of Tesselate.
110
When IncreaseTrianglesCapacity is called, all PROAMTriangleNode
111
values in higher function became invalid due to the memory shifting.
112
Recursivity is the main problem, that's why SafeTesselate is calling
113
Tesselate in a try..except . }
114
function SafeTesselate: boolean;
116
{ : Render the patch in high-resolution.
117
The lists are assumed to have enough capacity to allow AddNC calls
118
(additions without capacity check). High-resolution renders use
119
display lists, and are assumed to be made together. }
120
procedure RenderHighRes(vertices: TAffineVectorList;
121
vertexIndices: TIntegerList; texCoords: TTexPointList;
123
{ : Render the patch by accumulating triangles.
124
The lists are assumed to have enough capacity to allow AddNC calls
125
(additions without capacity check).
126
Once at least autoFlushVertexCount vertices have been accumulated,
127
perform a FlushAccum }
128
procedure RenderAccum(vertices: TAffineVectorList;
129
vertexIndices: TIntegerList; texCoords: TTexPointList;
130
autoFlushVertexCount: Integer);
131
{ : Render all vertices accumulated in the arrays and set their count
133
class procedure FlushAccum(vertices: TAffineVectorList;
134
vertexIndices: TIntegerList; texCoords: TTexPointList);
136
property HeightData: TGLHeightData read FHeightData write SeTGLHeightData;
137
property VertexScale: TAffineVector read FVertexScale write FVertexScale;
138
property VertexOffset: TAffineVector read FVertexOffset write FVertexOffset;
140
property ObserverPosition: TAffineVector read FObserverPosition
141
write FObserverPosition;
143
property TextureScale: TAffineVector read FTextureScale write FTextureScale;
144
property TextureOffset: TAffineVector read FTextureOffset
145
write FTextureOffset;
147
property HighRes: Boolean read FHighRes write FHighRes;
149
{ : Number of frames to skip after an occlusion test returned zero pixels. }
150
property OcclusionSkip: Integer read FOcclusionSkip write SetOcclusionSkip;
151
{ : Number of frames remaining to next occlusion test. }
152
property OcclusionCounter: Integer read FOcclusionCounter
153
write FOcclusionCounter;
154
{ : Result for the last occlusion test.
155
Note that this value is updated upon rendering the tile in
156
non-high-res mode only. }
157
property LastOcclusionTestPassed: Boolean read FLastOcclusionTestPassed;
159
property ID: Integer read FID;
160
property TriangleCount: Integer read FTriangleCount;
161
property Tag: Integer read FTag write FTag;
164
{ : Specifies the maximum number of ROAM triangles that may be allocated. }
165
procedure SetROAMTrianglesCapacity(nb: Integer);
166
function GetROAMTrianglesCapacity: Integer;
168
// ------------------------------------------------------------------
169
// ------------------------------------------------------------------
170
// ------------------------------------------------------------------
173
// ------------------------------------------------------------------
174
// ------------------------------------------------------------------
175
// ------------------------------------------------------------------
178
FVBOVertHandle, FVBOTexHandle: TGLVBOArrayBufferHandle;
179
FVBOIndicesHandle: TGLVBOElementArrayHandle;
183
// TROAMVariancePoint
185
TROAMVariancePoint = packed record
191
vNextPatchID: Integer;
192
vNbTris, vTriangleNodesCapacity: Integer;
193
vTriangleNodes: array of TROAMTriangleNode;
195
// SetROAMTrianglesCapacity
197
procedure SetROAMTrianglesCapacity(nb: Integer);
200
if vTriangleNodesCapacity <> nb then
202
SetLength(vTriangleNodes, nb);
203
vTriangleNodesCapacity := nb;
207
function GetROAMTrianglesCapacity: Integer;
209
Result := vTriangleNodesCapacity;
212
// The result is the delta between the old address of the array and the new one
213
function IncreaseTrianglesCapacity(NewCapacity: Integer): int64;
215
procedure FixNodePtr(var p: PROAMTriangleNode; const delta: int64);
220
Inc(PByte(p), delta);
224
oldbase, newbase: pointer;
225
node: PROAMTriangleNode;
229
if NewCapacity <= vTriangleNodesCapacity then
232
oldsize := vTriangleNodesCapacity;
234
oldbase := pointer(vTriangleNodes);
235
SetLength(vTriangleNodes, NewCapacity);
237
vTriangleNodesCapacity := NewCapacity;
239
newbase := pointer(vTriangleNodes);
241
// Array has not been relocated, no need to fix
242
if oldbase = newbase then
245
// go through all the old nodes and fix the pointers
246
// YP: Delphi needs int64 dual casting to avoid overflow exception
247
Result := int64(PtrUInt(newbase)) - int64(PtrUInt(oldbase));
248
for i := 0 to oldsize - 1 do
250
node := @vTriangleNodes[i];
252
FixNodePtr(node^.base, Result);
253
FixNodePtr(node^.left, Result);
254
FixNodePtr(node^.right, Result);
255
FixNodePtr(node^.leftChild, Result);
256
FixNodePtr(node^.rightChild, Result);
262
function AllocTriangleNode: Integer;
264
nilNode: PROAMTriangleNode;
266
if vNbTris >= vTriangleNodesCapacity then
269
IncreaseTrianglesCapacity(vTriangleNodesCapacity +
270
(vTriangleNodesCapacity shr 1));
273
with vTriangleNodes[vNbTris] do
278
leftChild := nilNode;
279
rightChild := nilNode;
286
function Split(tri: PROAMTriangleNode): Boolean;
289
lc, rc: PROAMTriangleNode;
292
Result := Assigned(tri.leftChild);
294
exit; // dont split if tri already has a left child
297
if Assigned(base) and (base.base <> tri) then
299
// If this triangle is not in a proper diamond, force split our base neighbor
303
if n >= vTriangleNodesCapacity - 1 then
306
Shift := IncreaseTrianglesCapacity(vTriangleNodesCapacity +
307
(vTriangleNodesCapacity shr 1));
310
raise EGLROAMException.Create
311
('PROAMTriangleNode addresses are invalid now');
318
// Create children and cross-link them
319
lc := @vTriangleNodes[n]; // left child
320
rc := @vTriangleNodes[n + 1]; // right child
325
rc.base := right; // right child
327
rc.rightChild := leftChild;
328
rc.right := leftChild;
330
lc.base := left; // left child
332
lc.rightChild := leftChild;
333
lc.left := rightChild;
337
if Assigned(left) then // Link our Left Neighbour to the new children
338
if left.base = tri then
340
else if left.left = tri then
344
if Assigned(right) then // Link our Right Neighbour to the new children
345
if right.base = tri then
347
else if right.left = tri then
351
// Link our Base Neighbor to the new children
352
if Assigned(base) then
354
if Assigned(base.leftChild) then
356
base.leftChild.right := rightChild;
357
rightChild.left := base.leftChild;
358
base.rightChild.left := leftChild;
359
leftChild.right := base.rightChild;
365
begin // An edge triangle, trivial case.
366
leftChild.right := nil;
367
rightChild.left := nil;
375
// ------------------ TGLROAMPatch ------------------
380
constructor TGLROAMPatch.Create;
385
FListHandle := TGLListHandle.Create;
386
FOcclusionQuery := TGLOcclusionQueryHandle.Create;
391
destructor TGLROAMPatch.Destroy;
394
FOcclusionQuery.Free;
400
procedure TGLROAMPatch.SeTGLHeightData(val: TGLHeightData);
403
FPatchSize := FHeightData.Size - 1;
404
FHeightRaster := val.SmallIntRaster;
409
procedure TGLROAMPatch.SetOcclusionSkip(val: Integer);
413
if FOcclusionSkip <> val then
415
FOcclusionSkip := val;
416
FOcclusionQuery.DestroyHandle;
422
procedure TGLROAMPatch.ConnectToTheWest(westPatch: TGLROAMPatch);
424
if Assigned(westPatch) then
426
if not(westPatch.HighRes or HighRes) then
428
vTriangleNodes[FTLNode].left := @vTriangleNodes[westPatch.FBRNode];
429
vTriangleNodes[westPatch.FBRNode].left := @vTriangleNodes[FTLNode];
432
westPatch.FEast := Self;
438
procedure TGLROAMPatch.ConnectToTheNorth(northPatch: TGLROAMPatch);
440
if Assigned(northPatch) then
442
if not(northPatch.HighRes or HighRes) then
444
vTriangleNodes[FTLNode].right := @vTriangleNodes[northPatch.FBRNode];
445
vTriangleNodes[northPatch.FBRNode].right := @vTriangleNodes[FTLNode];
447
FNorth := northPatch;
448
northPatch.FSouth := Self;
454
procedure TGLROAMPatch.ComputeVariance(variance: Integer);
456
raster: PSmallIntRaster;
457
currentVariance: PIntegerArray;
458
maxVarianceDepth: Integer;
459
maxNonNullIndex: Integer;
462
function ROAMVariancePoint(anX, anY: Integer): TROAMVariancePoint;
466
Result.Z := (Integer(FHeightRaster[anY][anX]) shl 8);
469
function RecursComputeVariance(const left, right, apex: TROAMVariancePoint;
470
node: Integer): Cardinal;
472
half: TROAMVariancePoint;
478
X := (left.X + right.X) shr 1;
479
Y := (left.Y + right.Y) shr 1;
480
Z := Integer(raster[Y][X]) shl 8;
481
Result := ScaleAndRound(Abs(((left.Z + right.Z) div 2) - Z), invVariance);
485
if n2 < maxVarianceDepth then
487
v := RecursComputeVariance(apex, left, half, n2);
490
v := RecursComputeVariance(right, apex, half, 1 + n2);
494
currentVariance[node] := Result;
497
procedure ScaleVariance(n, d: Integer);
502
newVal := (currentVariance[n] shl (d shr 1))
504
newVal := (currentVariance[n] shr (-d shr 1));
505
currentVariance[n] := newVal;
507
if n > maxNonNullIndex then
508
maxNonNullIndex := n;
510
if n < maxVarianceDepth then
514
ScaleVariance(n + 1, d);
521
invVariance := 1 / variance;
522
s := Sqr(FPatchSize);
523
raster := FHeightRaster;
527
FMaxDepth := FMaxDepth shl 2;
529
until FMaxDepth >= s;
530
maxVarianceDepth := FMaxDepth;
531
SetLength(FTLVariance, maxVarianceDepth);
532
SetLength(FBRVariance, maxVarianceDepth);
535
currentVariance := @FTLVariance[0];
536
maxNonNullIndex := 1;
537
RecursComputeVariance(ROAMVariancePoint(0, s), ROAMVariancePoint(s, 0),
538
ROAMVariancePoint(0, 0), 1);
540
FMaxTLVarianceDepth := maxNonNullIndex + 1;
541
SetLength(FTLVariance, FMaxTLVarianceDepth);
542
currentVariance := @FBRVariance[0];
543
maxNonNullIndex := 1;
544
RecursComputeVariance(ROAMVariancePoint(s, 0), ROAMVariancePoint(0, s),
545
ROAMVariancePoint(s, s), 1);
547
FMaxBRVarianceDepth := maxNonNullIndex + 1;
548
SetLength(FBRVariance, FMaxBRVarianceDepth);
553
procedure TGLROAMPatch.ResetTessellation;
555
FTLNode := AllocTriangleNode;
556
FBRNode := AllocTriangleNode;
557
vTriangleNodes[FTLNode].base := @vTriangleNodes[FBRNode];
558
vTriangleNodes[FBRNode].base := @vTriangleNodes[FTLNode];
568
tessMaxVariance: Cardinal;
569
tessMaxDepth: Cardinal;
570
tessCurrentVariance: PIntegerArray;
571
tessObserverPosX, tessObserverPosY: Integer;
573
function RecursTessellate(tri: PROAMTriangleNode; n: cardinal;
574
const left, right, apex: cardinal): boolean;
575
// returns false if tessellation failed due to MaxCLODTriangles limit
580
d := ((left + right) shr 1);
581
if tessCurrentVariance[n] > d then
587
if n < tessMaxVariance then
589
RecursTessellate(tri.leftChild, n, apex, left, d);
590
Result := RecursTessellate(tri.rightChild, n + 1, right, apex, d);
596
function TGLROAMPatch.Tesselate: boolean;
597
// Returns false if MaxCLODTriangles limit is reached.
599
tessFrameVarianceDelta: Integer;
601
function VertexDist(X, Y: Integer): cardinal;
605
c1Div100: Single = 0.01;
608
f := 0.2 * Sqr(FPatchSize)
610
f := Sqr(X - tessObserverPosX) + Sqr(Y - tessObserverPosY) +
611
tessFrameVarianceDelta;
612
Result := Round(Sqrt(f) + f * c1Div100);
615
procedure FullBaseTess(tri: PROAMTriangleNode; n: Cardinal); forward;
617
procedure FullLeftTess(tri: PROAMTriangleNode; n: Cardinal);
622
if n < tessMaxDepth then
623
FullBaseTess(tri.leftChild, n);
627
procedure FullRightTess(tri: PROAMTriangleNode; n: Cardinal);
632
if n < tessMaxDepth then
633
FullBaseTess(tri.rightChild, n);
637
procedure FullBaseTess(tri: PROAMTriangleNode; n: Cardinal);
642
if n < tessMaxDepth then
644
FullRightTess(tri.leftChild, n);
645
FullLeftTess(tri.rightChild, n);
653
tessMaxDepth := FMaxDepth;
654
tessObserverPosX := Round(FObserverPosition.X);
655
tessObserverPosY := Round(FObserverPosition.Y);
659
FullRightTess(@vTriangleNodes[FTLNode], 1);
660
FullRightTess(@vTriangleNodes[FBRNode], 1);
661
FullLeftTess(@vTriangleNodes[FBRNode], 1);
662
FullLeftTess(@vTriangleNodes[FTLNode], 1);
663
tessFrameVarianceDelta := 0;
667
if Assigned(FNorth) and FNorth.HighRes then
668
FullRightTess(@vTriangleNodes[FTLNode], 1);
669
if Assigned(FSouth) and FSouth.HighRes then
670
FullRightTess(@vTriangleNodes[FBRNode], 1);
671
if Assigned(FEast) and FEast.HighRes then
672
FullLeftTess(@vTriangleNodes[FBRNode], 1);
673
if Assigned(FWest) and FWest.HighRes then
674
FullLeftTess(@vTriangleNodes[FTLNode], 1);
675
if FObserverPosition.v[2] > 0 then
676
tessFrameVarianceDelta := Round(Sqr(FObserverPosition.Z * (1 / 16)))
678
tessFrameVarianceDelta := 0;
681
tessCurrentVariance := @FTLVariance[0];
682
tessMaxVariance := FMaxTLVarianceDepth;
683
Result := RecursTessellate(@vTriangleNodes[FTLNode], 1, VertexDist(0, s),
684
VertexDist(s, 0), VertexDist(0, 0));
685
tessCurrentVariance := @FBRVariance[0];
686
tessMaxVariance := FMaxBRVarianceDepth;
688
Result := RecursTessellate(@vTriangleNodes[FBRNode], 1, VertexDist(s, 0),
689
VertexDist(0, s), VertexDist(s, s));
695
function TGLROAMPatch.SafeTesselate: boolean;
703
//ResetTessellation; <- creates gaps between tiles
707
on e: EGLROAMException do
709
// Nothing to do, just wait the next iteration
718
procedure TGLROAMPatch.RenderHighRes(vertices: TAffineVectorList;
719
vertexIndices: TIntegerList; texCoords: TTexPointList; forceROAM: Boolean);
723
// Prepare display list if needed
724
if FListHandle.Handle = 0 then
726
// either use brute-force strips or a high-res static tesselation
730
RenderROAM(vertices, vertexIndices, texCoords);
731
primitive := GL_TRIANGLES;
732
FTriangleCount := vertexIndices.Count div 3;
736
RenderAsStrips(vertices, vertexIndices, texCoords);
737
primitive := GL_TRIANGLE_STRIP;
738
FTriangleCount := vertexIndices.Count - 2 * FPatchSize;
741
vertices.Translate(VertexOffset);
742
texCoords.ScaleAndTranslate(PTexPoint(@TextureScale)^,
743
PTexPoint(@TextureOffset)^);
745
GL.VertexPointer(3, GL_FLOAT, 0, vertices.List);
746
xgl.TexCoordPointer(2, GL_FLOAT, 0, texCoords.List);
748
FListHandle.AllocateHandle;
749
GL.NewList(FListHandle.Handle, GL_COMPILE);
750
GL.DrawElements(primitive, vertexIndices.Count, GL_UNSIGNED_INT,
755
texCoords.Count := 0;
756
vertexIndices.Count := 0;
758
// perform the render
759
GL.CallList(FListHandle.Handle);
764
procedure TGLROAMPatch.RenderAccum(vertices: TAffineVectorList;
765
vertexIndices: TIntegerList; texCoords: TTexPointList;
766
autoFlushVertexCount: Integer);
768
occlusionPassed: Boolean;
771
// CLOD tiles are rendered via ROAM
772
if (FOcclusionSkip > 0) and FOcclusionQuery.IsSupported then
774
if FOcclusionQuery.Handle = 0 then
776
FOcclusionQuery.AllocateHandle;
777
FOcclusionCounter := -(ID mod (FOcclusionSkip));
779
occlusionPassed := (FOcclusionCounter <= 0) or
780
(FOcclusionQuery.PixelCount > 0);
781
Dec(FOcclusionCounter);
782
if occlusionPassed then
784
if FOcclusionCounter <= 0 then
785
Inc(FOcclusionCounter, FOcclusionSkip);
786
FOcclusionQuery.BeginQuery;
790
occlusionPassed := True;
791
FLastOcclusionTestPassed := occlusionPassed;
792
if occlusionPassed then
794
nvi := vertexIndices.Count;
796
RenderROAM(vertices, vertexIndices, texCoords);
797
nb := vertices.Count - n;
798
FTriangleCount := (vertexIndices.Count - nvi) div 3;
800
vertices.Translate(VertexOffset, n, nb);
801
texCoords.ScaleAndTranslate(PTexPoint(@TextureScale)^,
802
PTexPoint(@TextureOffset)^, n, nb);
804
if FOcclusionQuery.Active then
806
FlushAccum(vertices, vertexIndices, texCoords);
807
FOcclusionQuery.EndQuery;
809
else if vertexIndices.Count > autoFlushVertexCount then
810
FlushAccum(vertices, vertexIndices, texCoords);
818
class procedure TGLROAMPatch.FlushAccum(vertices: TAffineVectorList;
819
vertexIndices: TIntegerList; texCoords: TTexPointList);
821
if vertexIndices.Count = 0 then
824
if GL.ARB_vertex_buffer_object then
826
FVBOVertHandle.AllocateHandle;
827
FVBOVertHandle.BindBufferData(vertices.List, vertices.DataSize,
829
GL.VertexPointer(3, GL_FLOAT, 0, nil);
831
FVBOTexHandle.AllocateHandle;
832
FVBOTexHandle.BindBufferData(texCoords.List, texCoords.DataSize,
834
xgl.TexCoordPointer(2, GL_FLOAT, 0, nil);
836
GL.DrawRangeElements(GL_TRIANGLES, 0, vertices.Count - 1,
837
vertexIndices.Count, GL_UNSIGNED_INT, vertexIndices.List);
838
GL.BindBuffer(GL_ARRAY_BUFFER_ARB, 0);
839
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
841
else if GL.EXT_compiled_vertex_array and GL.EXT_draw_range_elements then
843
GL.LockArrays(0, vertices.Count);
844
GL.DrawRangeElements(GL_TRIANGLES, 0, vertices.Count - 1,
845
vertexIndices.Count, GL_UNSIGNED_INT, vertexIndices.List);
850
GL.DrawElements(GL_TRIANGLES, vertexIndices.Count, GL_UNSIGNED_INT,
854
texCoords.Count := 0;
855
vertexIndices.Count := 0;
861
renderRaster: PSmallIntRaster;
862
renderIndices: PIntegerArray;
863
renderVertices: TAffineVectorList;
864
renderTexCoords: TTexPointList;
866
procedure RecursRender(const tri: PROAMTriangleNode;
867
const left, right, apex: TROAMRenderPoint);
869
half: TROAMRenderPoint;
870
localIndices: PIntegerArray;
872
if Assigned(tri.leftChild) then
873
begin // = if node is split
874
half.Y := (left.Y + right.Y) shr 1;
875
half.X := (left.X + right.X) shr 1;
876
renderTexCoords.AddNC(@half.X);
877
half.idx := renderVertices.AddNC(@half.X, renderRaster[half.Y][half.X]);
878
RecursRender(tri.leftChild, apex, left, half);
879
RecursRender(tri.rightChild, right, apex, half);
883
localIndices := renderIndices;
884
localIndices[0] := left.idx;
885
localIndices[1] := apex.idx;
886
localIndices[2] := right.idx;
887
renderIndices := PIntegerArray(@localIndices[3]);
891
procedure TGLROAMPatch.RenderROAM(vertices: TAffineVectorList;
892
vertexIndices: TIntegerList; texCoords: TTexPointList);
894
procedure ROAMRenderPoint(var p: TROAMRenderPoint; anX, anY: Integer);
898
p.idx := vertices.Add(anX, anY, renderRaster[anY][anX]);
899
texCoords.Add(anX, anY);
903
rtl, rtr, rbl, rbr: TROAMRenderPoint;
905
renderVertices := vertices;
906
renderTexCoords := texCoords;
907
vertexIndices.AdjustCapacityToAtLeast(Sqr(FPatchSize) * 6 + 15000);
908
// this is required, the actual item count is maintained out of the list scope
909
vertexIndices.SetCountResetsMemory := False;
910
renderIndices := @vertexIndices.List[vertexIndices.Count];
912
renderRaster := FHeightData.SmallIntRaster;
914
ROAMRenderPoint(rtl, 0, 0);
915
ROAMRenderPoint(rtr, FPatchSize, 0);
916
ROAMRenderPoint(rbl, 0, FPatchSize);
917
ROAMRenderPoint(rbr, FPatchSize, FPatchSize);
919
RecursRender(@vTriangleNodes[FTLNode], rbl, rtr, rtl);
920
RecursRender(@vTriangleNodes[FBRNode], rtr, rbl, rbr);
922
vertexIndices.Count := (PtrUInt(renderIndices) - PtrUInt(vertexIndices.List))
928
procedure TGLROAMPatch.RenderAsStrips(vertices: TAffineVectorList;
929
vertexIndices: TIntegerList; texCoords: TTexPointList);
932
X, Y, baseTop, rowLength: Integer;
935
raster: PSmallIntRaster;
937
verticesList: PAffineVector;
938
texCoordsList: PTexPoint;
939
indicesList: PInteger;
941
raster := FHeightData.SmallIntRaster;
942
rowLength := FPatchSize + 1;
943
// prepare vertex data
944
vertices.Count := Sqr(rowLength);
945
verticesList := PAffineVector(vertices.List);
946
texCoords.Count := Sqr(rowLength);
947
texCoordsList := PTexPoint(texCoords.List);
948
for Y := 0 to FPatchSize do
953
for X := 0 to FPatchSize do
960
texCoordsList^ := tex;
964
// build indices list
966
vertexIndices.Count := (rowLength * 2 + 2) * FPatchSize - 1;
967
indicesList := PInteger(vertexIndices.List);
969
while Y < FPatchSize do
973
indicesList^ := baseTop + FPatchSize;
976
for X := baseTop + FPatchSize downto baseTop do
979
PIntegerArray(indicesList)[1] := rowLength + X;
982
indicesList^ := baseTop + rowLength;
983
Inc(baseTop, rowLength);
984
PIntegerArray(indicesList)[1] := baseTop + rowLength;
986
for X := baseTop to baseTop + FPatchSize do
988
indicesList^ := rowLength + X;
989
PIntegerArray(indicesList)[1] := X;
992
indicesList^ := baseTop + FPatchSize;
994
Inc(baseTop, rowLength);
997
vertexIndices.Count := vertexIndices.Count - 1;
1000
// ------------------------------------------------------------------
1001
// ------------------------------------------------------------------
1002
// ------------------------------------------------------------------
1005
// ------------------------------------------------------------------
1006
// ------------------------------------------------------------------
1007
// ------------------------------------------------------------------
1009
FVBOVertHandle := TGLVBOArrayBufferHandle.Create;
1010
FVBOTexHandle := TGLVBOArrayBufferHandle.Create;
1011
FVBOIndicesHandle := TGLVBOElementArrayHandle.Create;
1017
FVBOIndicesHandle.Free;
1019
SetROAMTrianglesCapacity(0);