2
// This unit is part of the GLScene Engine https://github.com/glscene
5
A plane simulating animated water
8
10/11/12 - PW - Added CPP compatibility: changed vector arrays to records
9
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
10
30/03/07 - DaStr - Added $I GLScene.inc
11
22/09/04 - R.Cao - Added AxisAlignedDimensionsUnscaled to fix visibility culling
12
02/04/03 - EG - More optimizations, mask support
13
01/04/03 - EG - Cleanup and optimizations
14
14/11/03 - Mrqzzz - Tried "CreateRippleAtWorldPos" to work at any position/rotation, but need expert's help.. :(
15
13/11/03 - Mrqzzz - Tried to add timing indipendence (quite not precise yet)
16
12/11/03 - Mrqzzz - Added some properties & small optims added
17
01/01/03 - Sternas Stefanos - Original code
20
The Original Code is part of Cosmos4D
21
http://users.hol.gr/~sternas/
32
GLVectorGeometry, GLScene, OpenGLTokens, GLVectorLists,
33
GLCrossPlatform, GLPersistentClasses, GLBaseClasses,
34
GLContext, GLRenderContextInfo, GLVectorTypes;
38
// TGLWaterPlaneOption
40
TGLWaterPlaneOption = (wpoTextured);
41
TGLWaterPlaneOptions = set of TGLWaterPlaneOption;
44
cDefaultWaterPlaneOptions = [wpoTextured];
50
TGLWaterPlane = class (TGLSceneObject)
53
FLocks : packed array of ByteBool;
54
FPositions, FVelocity : packed array of Single;
55
FPlaneQuadIndices : TPersistentObjectList;
56
FPlaneQuadTexCoords : TTexPointList;
57
FPlaneQuadVertices : TAffineVectorList;
58
FPlaneQuadNormals : TAffineVectorList;
60
FRainTimeInterval : Integer;
64
FResolution : Integer;
65
FSimulationFrequency, FTimeToNextUpdate : Single;
66
FTimeToNextRainDrop : Single;
67
FMaximumCatchupIterations : Integer;
68
FLastIterationStepTime : Single;
70
FOptions : TGLWaterPlaneOptions;
74
procedure SetElastic(const value : Single);
75
procedure SetResolution(const value : Integer);
76
procedure SetRainTimeInterval(const val : Integer);
77
procedure SetViscosity(const val : Single);
78
procedure SetRainForce(const val : Single);
79
procedure SetSimulationFrequency(const val : Single);
80
procedure SetMask(val : TGLPicture);
81
procedure SetOptions(const val : TGLWaterPlaneOptions);
83
procedure DoMaskChanged(Sender : TObject);
84
procedure InitResolution;
86
procedure IterComputeVelocity;
87
procedure IterComputePositions;
88
procedure IterComputeNormals;
93
constructor Create(AOwner : TComponent); override;
94
destructor Destroy; override;
96
procedure DoProgress(const progressTime : TProgressTimes); override;
97
procedure BuildList(var rci : TGLRenderContextInfo); override;
98
procedure Assign(Source: TPersistent); override;
99
function AxisAlignedDimensionsUnscaled : TVector; override;
102
procedure CreateRippleAtGridPos(X,Y:integer);
103
procedure CreateRippleAtWorldPos(const x, y, z : Single); overload;
104
procedure CreateRippleAtWorldPos(const pos : TVector); overload;
105
procedure CreateRippleRandom;
108
{ CPU time (in seconds) taken by the last iteration step. }
109
property LastIterationStepTime : Single read FLastIterationStepTime;
114
property Active : Boolean read FActive write FActive default True;
116
{ Delay between raindrops in milliseconds (0 = no rain) }
117
property RainTimeInterval : Integer read FRainTimeInterval write SetRainTimeInterval default 500;
118
property RainForce : Single read FRainForce write SetRainForce;
120
property Viscosity : Single read FViscosity write SetViscosity ;
121
property Elastic : Single read FElastic write SetElastic;
122
property Resolution : Integer read FResolution write SetResolution default 64;
123
property Options : TGLWaterPlaneOptions read FOptions write SetOptions default cDefaultWaterPlaneOptions;
125
{ A picture whose pixels determine what part of the waterplane is active.
126
Pixels with a green/gray component beyond 128 are active, the others
127
are not (in short, white = active, black = inactive).
128
The picture will automatically be stretched to match the resolution. }
129
property Mask : TGLPicture read FMask write SetMask;
131
{ Maximum frequency (in Hz) at which simulation iterations happen. }
132
property SimulationFrequency : Single read FSimulationFrequency write SetSimulationFrequency;
133
{ Maximum number of simulation iterations during catchups.
134
Catchups happen when for a reason or another, the DoProgress doesn't
135
happen as fast SimulationFrequency. }
136
property MaximumCatchupIterations : Integer read FMaximumCatchupIterations write FMaximumCatchupIterations default 1;
139
//-------------------------------------------------------------
140
//-------------------------------------------------------------
141
//-------------------------------------------------------------
143
//-------------------------------------------------------------
144
//-------------------------------------------------------------
145
//-------------------------------------------------------------
149
constructor TGLWaterPlane.Create(AOwner : TComponent);
151
inherited Create(AOwner);
152
ObjectStyle:=ObjectStyle+[osDirectDraw];
156
FRainTimeInterval:=500;
159
FSimulationFrequency:=100; // 100 Hz
160
FMaximumCatchupIterations:=1;
161
FOptions:=cDefaultWaterPlaneOptions;
163
FPlaneQuadIndices:=TPersistentObjectList.Create;
164
FPlaneQuadTexCoords:=TTexPointList.Create;
165
FPlaneQuadVertices:=TAffineVectorList.Create;
166
FPlaneQuadNormals:=TAffineVectorList.Create;
167
FMask:=TGLPicture.Create;
168
FMask.OnChange:=DoMaskChanged;
175
destructor TGLWaterPlane.Destroy;
178
FPlaneQuadNormals.Free;
179
FPlaneQuadVertices.Free;
180
FPlaneQuadTexCoords.Free;
181
FPlaneQuadIndices.CleanFree;
187
procedure TGLWaterPlane.DoProgress(const progressTime : TProgressTimes);
192
if Active and Visible then begin
194
if FRainTimeInterval>0 then begin
195
FTimeToNextRainDrop:=FTimeToNextRainDrop-progressTime.deltaTime;
196
i:=FMaximumCatchupIterations;
197
while FTimeToNextRainDrop<=0 do begin
199
FTimeToNextRainDrop:=FTimeToNextRainDrop+FRainTimeInterval*0.001;
202
if FTimeToNextRainDrop<0 then FTimeToNextRainDrop:=FRainTimeInterval*0.001;
207
// iterate simulation
208
FTimeToNextUpdate:=FTimeToNextUpdate-progressTime.deltaTime;
209
if FTimeToNextUpdate<=0 then begin
210
i:=FMaximumCatchupIterations;
211
while FTimeToNextUpdate<=0 do begin
213
FTimeToNextUpdate:=FTimeToNextUpdate+1/FSimulationFrequency;
216
if FTimeToNextUpdate<0 then FTimeToNextUpdate:=1/FSimulationFrequency;
225
// CreateRippleAtGridPos
227
procedure TGLWaterPlane.CreateRippleAtGridPos(x, y : Integer);
229
if (x>0) and (y>0) and (x<Resolution-1) and (y<Resolution-1) then
230
FVelocity[x+y*Resolution]:=FRainForce;
233
// CreateRippleAtWorldPos
235
procedure TGLWaterPlane.CreateRippleAtWorldPos(const x, y, z : Single);
239
vv:=AbsoluteToLocal(PointMake(x, y, z));
240
CreateRippleAtGridPos(Round((vv.V[0]+0.5)*Resolution),
241
Round((vv.V[2]+0.5)*Resolution));
244
// CreateRippleAtWorldPos
246
procedure TGLWaterPlane.CreateRippleAtWorldPos(const pos : TVector);
250
vv:=AbsoluteToLocal(PointMake(pos));
251
CreateRippleAtGridPos(Round((vv.V[0]+0.5)*Resolution),
252
Round((vv.V[2]+0.5)*Resolution));
257
procedure TGLWaterPlane.CreateRippleRandom;
259
CreateRippleAtGridPos(Random(Resolution-3)+2, Random(Resolution-3)+2);
264
procedure TGLWaterPlane.InitResolution;
271
resSqr:=FResolution*FResolution;
272
FPlaneQuadIndices.Capacity:=resSqr*2;
273
FPlaneQuadTexCoords.Clear;
274
FPlaneQuadTexCoords.Capacity:=resSqr;
275
FPlaneQuadVertices.Clear;
276
FPlaneQuadVertices.Capacity:=resSqr;
278
invResol:=1/Resolution;
279
for j:=0 to Resolution-1 do begin
280
for i:=0 to Resolution-1 do begin
281
FPlaneQuadTexCoords.Add(i*invResol, j*invResol);
282
FPlaneQuadVertices.Add((i-Resolution*0.5)*invResol,
284
(j-Resolution*0.5)*invResol);
288
FPlaneQuadNormals.Count:=resSqr;
292
for i:=0 to FPlaneQuadNormals.Count-1 do
293
FPlaneQuadNormals.List[i]:=v;
295
SetLength(FPositions, resSqr);
296
SetLength(FVelocity, resSqr);
297
SetLength(FLocks, resSqr);
307
procedure TGLWaterPlane.Reset;
309
i, j, ij, resSqr : Integer;
311
scanLine : PIntegerArray;
315
resSqr:=FResolution*FResolution;
316
for i:=0 to resSqr-1 do begin
321
if FMask.Width>0 then begin
322
maskBmp:=TGLBitmap.Create;
324
maskBmp.PixelFormat:=glpf32bit;
325
maskBmp.Width:=Resolution;
326
maskBmp.Height:=Resolution;
327
maskBmp.Canvas.StretchDraw(Rect(0, 0, Resolution, Resolution), FMask.Graphic);
328
for j:=0 to Resolution-1 do begin
329
scanLine:=BitmapScanLine(maskBmp, Resolution-1-j); //maskBmp.ScanLine[Resolution-1-j];
330
for i:=0 to Resolution-1 do
331
FLocks[i+j*Resolution]:=(((scanLine[i] shr 8) and $FF)<128);
338
FPlaneQuadIndices.Clean;
339
for j:=0 to Resolution-2 do begin
340
il:=TIntegerList.Create;
341
for i:=0 to Resolution-1 do begin
343
if (il.Count and 2)<>0 then
346
locked:=FLocks[ij] and FLocks[ij+Resolution];
347
if locked and (i<Resolution-1) then
348
locked:=FLocks[ij+1] and FLocks[ij+Resolution+1];
351
il.Add(ij, ij+Resolution)
352
else if il.Count>0 then begin
353
FPlaneQuadIndices.Add(il);
354
il:=TIntegerList.Create;
358
FPlaneQuadIndices.Add(il)
363
// IterComputeVelocity
365
procedure TGLWaterPlane.IterComputeVelocity;
369
posList, velList : PSingleArray;
370
lockList : PByteArray;
375
posList:=@FPositions[0];
376
velList:=@FVelocity[0];
377
lockList:=@FLocks[0];
378
for i:=1 to Resolution-2 do begin
380
for j:=1 to Resolution-2 do begin
382
if lockList[ij]<>0 then continue;
383
velList[ij]:= velList[ij]
385
-f1*( 4*( posList[ij-1] +posList[ij+1]
386
+posList[ij-Resolution]+posList[ij+Resolution])
387
+posList[ij-1-Resolution]+posList[ij+1-Resolution]
388
+posList[ij-1+Resolution]+posList[ij+1+Resolution]));
393
// IterComputePositions
395
procedure TGLWaterPlane.IterComputePositions;
397
cVelocityIntegrationCoeff : Single = 0.02;
398
cHeightFactor : Single = 1e-4;
403
posList, velList : PSingleArray;
404
lockList : PByteArray;
406
// Calculate the new ripple positions and update vertex coordinates
407
coeff:=cVelocityIntegrationCoeff*Resolution;
408
f:=cHeightFactor/Resolution;
409
posList:=@FPositions[0];
410
velList:=@FVelocity[0];
411
lockList:=@FLocks[0];
412
for ij:=0 to Resolution*Resolution-1 do begin
413
if lockList[ij]=0 then begin
414
posList[ij]:=posList[ij]-coeff*velList[ij];
415
velList[ij]:=velList[ij]*FViscosity;
416
FPlaneQuadVertices.List[ij].V[1]:=posList[ij]*f;
423
procedure TGLWaterPlane.IterComputeNormals;
427
posList : PSingleArray;
428
normList : PAffineVectorArray;
430
// Calculate the new vertex normals (not normalized, the hardware will handle that)
431
posList:=@FPositions[0];
432
normList:=FPlaneQuadNormals.List;
433
for i:=1 to Resolution-2 do begin
435
for j:=1 to Resolution-2 do begin
438
pv.V[0]:=posList[ij+1]-posList[ij-1];
439
pv.V[2]:=posList[ij+Resolution]-posList[ij-Resolution];
446
procedure TGLWaterPlane.Iterate;
450
if Visible then begin
451
t:=StartPrecisionTimer;
454
IterComputePositions;
457
FLastIterationStepTime:=StopPrecisionTimer(t);
463
procedure TGLWaterPlane.BuildList(var rci : TGLRenderContextInfo);
468
GL.PushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
470
GL.EnableClientState(GL_VERTEX_ARRAY);
471
GL.VertexPointer(3, GL_FLOAT, 0, FPlaneQuadVertices.List);
472
GL.EnableClientState(GL_NORMAL_ARRAY);
473
GL.NormalPointer(GL_FLOAT, 0, FPlaneQuadNormals.List);
474
if wpoTextured in Options then begin
475
GL.EnableClientState(GL_TEXTURE_COORD_ARRAY);
476
GL.TexCoordPointer(2, GL_FLOAT, 0, FPlaneQuadTexCoords.List);
477
end else GL.DisableClientState(GL_TEXTURE_COORD_ARRAY);
479
if GL.EXT_compiled_vertex_array then
480
GL.LockArrays(0, FPlaneQuadVertices.Count);
482
for i:=0 to FPlaneQuadIndices.Count-1 do begin
483
il:=TIntegerList(FPlaneQuadIndices[i]);
484
GL.DrawElements(GL_QUAD_STRIP, il.Count, GL_UNSIGNED_INT, il.List);
487
if GL.EXT_compiled_vertex_array then
495
procedure TGLWaterPlane.Assign(Source: TPersistent);
497
if Assigned(Source) and (Source is TGLWaterPlane) then begin
498
Active:=TGLWaterPlane(Source).Active;
499
RainTimeInterval:=TGLWaterPlane(Source).RainTimeInterval;
500
RainForce:=TGLWaterPlane(Source).RainForce;
501
Viscosity:=TGLWaterPlane(Source).Viscosity;
503
inherited Assign(Source);
506
// AxisAlignedDimensionsUnscaled
508
function TGLWaterPlane.AxisAlignedDimensionsUnscaled : TVector;
510
Result.V[0]:=0.5*Abs(Resolution);
512
Result.V[2]:=0.5*Abs(FResolution);
518
procedure TGLWaterPlane.SetElastic(const Value: single);
525
procedure TGLWaterPlane.SetResolution(const value : Integer);
527
if value<>FResolution then begin
529
if FResolution<16 then FResolution:=16;
534
// SetRainTimeInterval
536
procedure TGLWaterPlane.SetRainTimeInterval(Const val:integer);
538
if (val>=0) and (Val<=1000000) then
539
fRainTimeInterval:=val;
544
Procedure TGLWaterPlane.SetViscosity(const val : Single);
546
if (val>=0) and (val<=1) then
552
procedure TGLWaterPlane.SetRainForce(const val : Single);
554
if (val>=0) and (val<=1000000) then
558
// SetSimulationFrequency
560
procedure TGLWaterPlane.SetSimulationFrequency(const val : Single);
562
if FSimulationFrequency<>val then begin
563
FSimulationFrequency:=val;
564
if FSimulationFrequency<1 then FSimulationFrequency:=1;
565
FTimeToNextUpdate:=0;
571
procedure TGLWaterPlane.SetMask(val : TGLPicture);
578
procedure TGLWaterPlane.DoMaskChanged(Sender : TObject);
586
procedure TGLWaterPlane.SetOptions(const val : TGLWaterPlaneOptions);
588
if FOptions<>val then begin
594
//-------------------------------------------------------------
595
//-------------------------------------------------------------
596
//-------------------------------------------------------------
599
//-------------------------------------------------------------
600
//-------------------------------------------------------------
601
//-------------------------------------------------------------
603
RegisterClasses([TGLWaterPlane]);