GLS.WaterPlane.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.WaterPlane;
  5. (*
  6. A plane simulating animated water
  7. The Original Code is part of Cosmos4D
  8. http://users.hol.gr/~sternas/
  9. *)
  10. interface
  11. {$I Stage.Defines.inc}
  12. uses
  13. Winapi.OpenGL,
  14. System.Classes,
  15. Vcl.Graphics,
  16. Stage.OpenGLTokens,
  17. Stage.VectorGeometry,
  18. GLS.Scene,
  19. GLS.VectorLists,
  20. GLS.PersistentClasses,
  21. GLS.BaseClasses,
  22. GLS.Context,
  23. GLS.RenderContextInfo,
  24. Stage.VectorTypes,
  25. Stage.Utils;
  26. type
  27. TGLWaterPlaneOption = (wpoTextured);
  28. TGLWaterPlaneOptions = set of TGLWaterPlaneOption;
  29. const
  30. cDefaultWaterPlaneOptions = [wpoTextured];
  31. type
  32. TGLWaterPlane = class(TGLSceneObject)
  33. private
  34. FLocks: packed array of ByteBool;
  35. FPositions, FVelocity: packed array of Single;
  36. FPlaneQuadIndices: TGLPersistentObjectList;
  37. FPlaneQuadTexCoords: TGLTexPointList;
  38. FPlaneQuadVertices: TGLAffineVectorList;
  39. FPlaneQuadNormals: TGLAffineVectorList;
  40. FActive: Boolean;
  41. FRainTimeInterval: Integer;
  42. FRainForce: Single;
  43. FViscosity: Single;
  44. FElastic: Single;
  45. FResolution: Integer;
  46. FSimulationFrequency, FTimeToNextUpdate: Single;
  47. FTimeToNextRainDrop: Single;
  48. FMaximumCatchupIterations: Integer;
  49. FLastIterationStepTime: Single;
  50. FMask: TPicture;
  51. FOptions: TGLWaterPlaneOptions;
  52. protected
  53. procedure SetElastic(const value: Single);
  54. procedure SetResolution(const value: Integer);
  55. procedure SetRainTimeInterval(const val: Integer);
  56. procedure SetViscosity(const val: Single);
  57. procedure SetRainForce(const val: Single);
  58. procedure SetSimulationFrequency(const val: Single);
  59. procedure SetMask(val: TPicture);
  60. procedure SetOptions(const val: TGLWaterPlaneOptions);
  61. procedure DoMaskChanged(Sender: TObject);
  62. procedure InitResolution;
  63. procedure IterComputeVelocity;
  64. procedure IterComputePositions;
  65. procedure IterComputeNormals;
  66. procedure Iterate;
  67. public
  68. constructor Create(AOwner: TComponent); override;
  69. destructor Destroy; override;
  70. procedure DoProgress(const progressTime: TGLProgressTimes); override;
  71. procedure BuildList(var rci: TGLRenderContextInfo); override;
  72. procedure Assign(Source: TPersistent); override;
  73. function AxisAlignedDimensionsUnscaled: TGLVector; override;
  74. procedure CreateRippleAtGridPos(X, Y: Integer);
  75. procedure CreateRippleAtWorldPos(const X, Y, z: Single); overload;
  76. procedure CreateRippleAtWorldPos(const pos: TGLVector); overload;
  77. procedure CreateRippleRandom;
  78. procedure Reset;
  79. // CPU time (in seconds) taken by the last iteration step.
  80. property LastIterationStepTime: Single read FLastIterationStepTime;
  81. published
  82. property Active: Boolean read FActive write FActive default True;
  83. // Delay between raindrops in milliseconds (0 = no rain)
  84. property RainTimeInterval: Integer read FRainTimeInterval
  85. write SetRainTimeInterval default 500;
  86. property RainForce: Single read FRainForce write SetRainForce;
  87. property Viscosity: Single read FViscosity write SetViscosity;
  88. property Elastic: Single read FElastic write SetElastic;
  89. property Resolution: Integer read FResolution write SetResolution
  90. default 64;
  91. property Options: TGLWaterPlaneOptions read FOptions write SetOptions
  92. default cDefaultWaterPlaneOptions;
  93. (* A picture whose pixels determine what part of the waterplane is active.
  94. Pixels with a green/gray component beyond 128 are active, the others
  95. are not (in short, white = active, black = inactive).
  96. The picture will automatically be stretched to match the resolution. *)
  97. property Mask: TPicture read FMask write SetMask;
  98. // Maximum frequency (in Hz) at which simulation iterations happen.
  99. property SimulationFrequency: Single read FSimulationFrequency
  100. write SetSimulationFrequency;
  101. (* Maximum number of simulation iterations during catchups.
  102. Catchups happen when for a reason or another, the DoProgress doesn't
  103. happen as fast SimulationFrequency. *)
  104. property MaximumCatchupIterations: Integer read FMaximumCatchupIterations
  105. write FMaximumCatchupIterations default 1;
  106. end;
  107. //-------------------------------------------------------------
  108. implementation
  109. //-------------------------------------------------------------
  110. constructor TGLWaterPlane.Create(AOwner: TComponent);
  111. begin
  112. inherited Create(AOwner);
  113. ObjectStyle := ObjectStyle + [osDirectDraw];
  114. FElastic := 10;
  115. FActive := True;
  116. FRainTimeInterval := 500;
  117. FRainForce := 5000;
  118. FViscosity := 0.99;
  119. FSimulationFrequency := 100; // 100 Hz
  120. FMaximumCatchupIterations := 1;
  121. FOptions := cDefaultWaterPlaneOptions;
  122. FPlaneQuadIndices := TGLPersistentObjectList.Create;
  123. FPlaneQuadTexCoords := TGLTexPointList.Create;
  124. FPlaneQuadVertices := TGLAffineVectorList.Create;
  125. FPlaneQuadNormals := TGLAffineVectorList.Create;
  126. FMask := TPicture.Create;
  127. FMask.OnChange := DoMaskChanged;
  128. SetResolution(64);
  129. end;
  130. destructor TGLWaterPlane.Destroy;
  131. begin
  132. FMask.Free;
  133. FPlaneQuadNormals.Free;
  134. FPlaneQuadVertices.Free;
  135. FPlaneQuadTexCoords.Free;
  136. FPlaneQuadIndices.CleanFree;
  137. inherited;
  138. end;
  139. procedure TGLWaterPlane.DoProgress(const progressTime: TGLProgressTimes);
  140. var
  141. i: Integer;
  142. begin
  143. inherited;
  144. if Active and Visible then
  145. begin
  146. // new raindrops
  147. if FRainTimeInterval > 0 then
  148. begin
  149. FTimeToNextRainDrop := FTimeToNextRainDrop - progressTime.deltaTime;
  150. i := FMaximumCatchupIterations;
  151. while FTimeToNextRainDrop <= 0 do
  152. begin
  153. CreateRippleRandom;
  154. FTimeToNextRainDrop := FTimeToNextRainDrop + FRainTimeInterval * 0.001;
  155. Dec(i);
  156. if i < 0 then
  157. begin
  158. if FTimeToNextRainDrop < 0 then
  159. FTimeToNextRainDrop := FRainTimeInterval * 0.001;
  160. Break;
  161. end;
  162. end;
  163. end;
  164. // iterate simulation
  165. FTimeToNextUpdate := FTimeToNextUpdate - progressTime.deltaTime;
  166. if FTimeToNextUpdate <= 0 then
  167. begin
  168. i := FMaximumCatchupIterations;
  169. while FTimeToNextUpdate <= 0 do
  170. begin
  171. Iterate;
  172. FTimeToNextUpdate := FTimeToNextUpdate + 1 / FSimulationFrequency;
  173. Dec(i);
  174. if i < 0 then
  175. begin
  176. if FTimeToNextUpdate < 0 then
  177. FTimeToNextUpdate := 1 / FSimulationFrequency;
  178. Break;
  179. end;
  180. end;
  181. StructureChanged;
  182. end;
  183. end;
  184. end;
  185. procedure TGLWaterPlane.CreateRippleAtGridPos(X, Y: Integer);
  186. begin
  187. if (X > 0) and (Y > 0) and (X < Resolution - 1) and (Y < Resolution - 1) then
  188. FVelocity[X + Y * Resolution] := FRainForce;
  189. end;
  190. procedure TGLWaterPlane.CreateRippleAtWorldPos(const X, Y, z: Single);
  191. var
  192. vv: TGLVector;
  193. begin
  194. vv := AbsoluteToLocal(PointMake(X, Y, z));
  195. CreateRippleAtGridPos(Round((vv.X + 0.5) * Resolution),
  196. Round((vv.z + 0.5) * Resolution));
  197. end;
  198. procedure TGLWaterPlane.CreateRippleAtWorldPos(const pos: TGLVector);
  199. var
  200. vv: TGLVector;
  201. begin
  202. vv := AbsoluteToLocal(PointMake(pos));
  203. CreateRippleAtGridPos(Round((vv.X + 0.5) * Resolution),
  204. Round((vv.z + 0.5) * Resolution));
  205. end;
  206. procedure TGLWaterPlane.CreateRippleRandom;
  207. begin
  208. CreateRippleAtGridPos(Random(Resolution - 3) + 2, Random(Resolution - 3) + 2);
  209. end;
  210. procedure TGLWaterPlane.InitResolution;
  211. var
  212. i, j: Integer;
  213. v: TAffineVector;
  214. resSqr: Integer;
  215. invResol: Single;
  216. begin
  217. resSqr := FResolution * FResolution;
  218. FPlaneQuadIndices.Capacity := resSqr * 2;
  219. FPlaneQuadTexCoords.Clear;
  220. FPlaneQuadTexCoords.Capacity := resSqr;
  221. FPlaneQuadVertices.Clear;
  222. FPlaneQuadVertices.Capacity := resSqr;
  223. invResol := 1 / Resolution;
  224. for j := 0 to Resolution - 1 do
  225. begin
  226. for i := 0 to Resolution - 1 do
  227. begin
  228. FPlaneQuadTexCoords.Add(i * invResol, j * invResol);
  229. FPlaneQuadVertices.Add((i - Resolution * 0.5) * invResol, 0,
  230. (j - Resolution * 0.5) * invResol);
  231. end;
  232. end;
  233. FPlaneQuadNormals.Count := resSqr;
  234. v.X := 0;
  235. v.Y := 2048;
  236. v.z := 0;
  237. for i := 0 to FPlaneQuadNormals.Count - 1 do
  238. FPlaneQuadNormals.List[i] := v;
  239. SetLength(FPositions, resSqr);
  240. SetLength(FVelocity, resSqr);
  241. SetLength(FLocks, resSqr);
  242. Reset;
  243. Iterate;
  244. StructureChanged;
  245. end;
  246. procedure TGLWaterPlane.Reset;
  247. var
  248. i, j, ij, resSqr: Integer;
  249. maskBmp: TBitmap;
  250. scanLine: PIntegerArray;
  251. il: TGLIntegerList;
  252. locked: Boolean;
  253. begin
  254. resSqr := FResolution * FResolution;
  255. for i := 0 to resSqr - 1 do
  256. begin
  257. FPositions[i] := 0;
  258. FVelocity[i] := 0;
  259. FLocks[i] := False;
  260. end;
  261. if FMask.Width > 0 then
  262. begin
  263. maskBmp := TBitmap.Create;
  264. try
  265. maskBmp.PixelFormat := pf32bit;
  266. maskBmp.Width := Resolution;
  267. maskBmp.Height := Resolution;
  268. maskBmp.Canvas.StretchDraw(Rect(0, 0, Resolution, Resolution),
  269. FMask.Graphic);
  270. for j := 0 to Resolution - 1 do
  271. begin
  272. scanLine := maskBmp.scanLine[Resolution - 1 - j];
  273. for i := 0 to Resolution - 1 do
  274. FLocks[i + j * Resolution] := (((scanLine[i] shr 8) and $FF) < 128);
  275. end;
  276. finally
  277. maskBmp.Free;
  278. end;
  279. end;
  280. FPlaneQuadIndices.Clean;
  281. for j := 0 to Resolution - 2 do
  282. begin
  283. il := TGLIntegerList.Create;
  284. for i := 0 to Resolution - 1 do
  285. begin
  286. ij := i + j * Resolution;
  287. if (il.Count and 2) <> 0 then
  288. locked := False
  289. else
  290. begin
  291. locked := FLocks[ij] and FLocks[ij + Resolution];
  292. if locked and (i < Resolution - 1) then
  293. locked := FLocks[ij + 1] and FLocks[ij + Resolution + 1];
  294. end;
  295. if not locked then
  296. il.Add(ij, ij + Resolution)
  297. else if il.Count > 0 then
  298. begin
  299. FPlaneQuadIndices.Add(il);
  300. il := TGLIntegerList.Create;
  301. end;
  302. end;
  303. if il.Count > 0 then
  304. FPlaneQuadIndices.Add(il)
  305. else
  306. il.Free;
  307. end;
  308. end;
  309. procedure TGLWaterPlane.IterComputeVelocity;
  310. var
  311. i, j, ij: Integer;
  312. f1, f2: Single;
  313. posList, velList: PSingleArray;
  314. lockList: PByteArray;
  315. begin
  316. f1 := 0.05;
  317. f2 := 0.01 * FElastic;
  318. posList := @FPositions[0];
  319. velList := @FVelocity[0];
  320. lockList := @FLocks[0];
  321. for i := 1 to Resolution - 2 do
  322. begin
  323. ij := i * Resolution;
  324. for j := 1 to Resolution - 2 do
  325. begin
  326. Inc(ij);
  327. if lockList[ij] <> 0 then
  328. continue;
  329. velList[ij] := velList[ij] + f2 *
  330. (posList[ij] - f1 * (4 * (posList[ij - 1] + posList[ij + 1] +
  331. posList[ij - Resolution] + posList[ij + Resolution]) +
  332. posList[ij - 1 - Resolution] + posList[ij + 1 - Resolution] +
  333. posList[ij - 1 + Resolution] + posList[ij + 1 + Resolution]));
  334. end;
  335. end;
  336. end;
  337. procedure TGLWaterPlane.IterComputePositions;
  338. const
  339. cVelocityIntegrationCoeff: Single = 0.02;
  340. cHeightFactor: Single = 1E-4;
  341. var
  342. ij: Integer;
  343. f: Single;
  344. coeff: Single;
  345. posList, velList: PSingleArray;
  346. lockList: PByteArray;
  347. begin
  348. // Calculate the new ripple positions and update vertex coordinates
  349. coeff := cVelocityIntegrationCoeff * Resolution;
  350. f := cHeightFactor / Resolution;
  351. posList := @FPositions[0];
  352. velList := @FVelocity[0];
  353. lockList := @FLocks[0];
  354. for ij := 0 to Resolution * Resolution - 1 do
  355. begin
  356. if lockList[ij] = 0 then
  357. begin
  358. posList[ij] := posList[ij] - coeff * velList[ij];
  359. velList[ij] := velList[ij] * FViscosity;
  360. FPlaneQuadVertices.List[ij].Y := posList[ij] * f;
  361. end;
  362. end;
  363. end;
  364. procedure TGLWaterPlane.IterComputeNormals;
  365. var
  366. i, j, ij: Integer;
  367. pv: PAffineVector;
  368. posList: PSingleArray;
  369. normList: PAffineVectorArray;
  370. begin
  371. // Calculate the new vertex normals (not normalized, the hardware will handle that)
  372. posList := @FPositions[0];
  373. normList := FPlaneQuadNormals.List;
  374. for i := 1 to Resolution - 2 do
  375. begin
  376. ij := i * Resolution;
  377. for j := 1 to Resolution - 2 do
  378. begin
  379. Inc(ij);
  380. pv := @normList[ij];
  381. pv.X := posList[ij + 1] - posList[ij - 1];
  382. pv.z := posList[ij + Resolution] - posList[ij - Resolution];
  383. end;
  384. end;
  385. end;
  386. procedure TGLWaterPlane.Iterate;
  387. var
  388. t: Int64;
  389. begin
  390. if Visible then
  391. begin
  392. t := StartPrecisionTimer;
  393. IterComputeVelocity;
  394. IterComputePositions;
  395. IterComputeNormals;
  396. FLastIterationStepTime := StopPrecisionTimer(t);
  397. end;
  398. end;
  399. procedure TGLWaterPlane.BuildList(var rci: TGLRenderContextInfo);
  400. var
  401. i: Integer;
  402. il: TGLIntegerList;
  403. begin
  404. gl.PushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
  405. gl.EnableClientState(GL_VERTEX_ARRAY);
  406. gl.VertexPointer(3, GL_FLOAT, 0, FPlaneQuadVertices.List);
  407. gl.EnableClientState(GL_NORMAL_ARRAY);
  408. gl.NormalPointer(GL_FLOAT, 0, FPlaneQuadNormals.List);
  409. if wpoTextured in Options then
  410. begin
  411. gl.EnableClientState(GL_TEXTURE_COORD_ARRAY);
  412. gl.TexCoordPointer(2, GL_FLOAT, 0, FPlaneQuadTexCoords.List);
  413. end
  414. else
  415. gl.DisableClientState(GL_TEXTURE_COORD_ARRAY);
  416. if gl.EXT_compiled_vertex_array then
  417. gl.LockArrays(0, FPlaneQuadVertices.Count);
  418. for i := 0 to FPlaneQuadIndices.Count - 1 do
  419. begin
  420. il := TGLIntegerList(FPlaneQuadIndices[i]);
  421. gl.DrawElements(GL_QUAD_STRIP, il.Count, GL_UNSIGNED_INT, il.List);
  422. end;
  423. if gl.EXT_compiled_vertex_array then
  424. gl.UnLockArrays;
  425. gl.PopClientAttrib;
  426. end;
  427. procedure TGLWaterPlane.Assign(Source: TPersistent);
  428. begin
  429. if Assigned(Source) and (Source is TGLWaterPlane) then
  430. begin
  431. Active := TGLWaterPlane(Source).Active;
  432. RainTimeInterval := TGLWaterPlane(Source).RainTimeInterval;
  433. RainForce := TGLWaterPlane(Source).RainForce;
  434. Viscosity := TGLWaterPlane(Source).Viscosity;
  435. end;
  436. inherited Assign(Source);
  437. end;
  438. function TGLWaterPlane.AxisAlignedDimensionsUnscaled: TGLVector;
  439. begin
  440. Result.X := 0.5 * Abs(Resolution);
  441. Result.Y := 0;
  442. Result.z := 0.5 * Abs(FResolution);
  443. end;
  444. procedure TGLWaterPlane.SetElastic(const value: Single);
  445. begin
  446. FElastic := value;
  447. end;
  448. procedure TGLWaterPlane.SetResolution(const value: Integer);
  449. begin
  450. if value <> FResolution then
  451. begin
  452. FResolution := value;
  453. if FResolution < 16 then
  454. FResolution := 16;
  455. InitResolution;
  456. end;
  457. end;
  458. procedure TGLWaterPlane.SetRainTimeInterval(Const val: Integer);
  459. begin
  460. if (val >= 0) and (val <= 1000000) then
  461. FRainTimeInterval := val;
  462. end;
  463. Procedure TGLWaterPlane.SetViscosity(const val: Single);
  464. begin
  465. if (val >= 0) and (val <= 1) then
  466. FViscosity := val;
  467. end;
  468. procedure TGLWaterPlane.SetRainForce(const val: Single);
  469. begin
  470. if (val >= 0) and (val <= 1000000) then
  471. FRainForce := val;
  472. end;
  473. procedure TGLWaterPlane.SetSimulationFrequency(const val: Single);
  474. begin
  475. if FSimulationFrequency <> val then
  476. begin
  477. FSimulationFrequency := val;
  478. if FSimulationFrequency < 1 then
  479. FSimulationFrequency := 1;
  480. FTimeToNextUpdate := 0;
  481. end;
  482. end;
  483. procedure TGLWaterPlane.SetMask(val: TPicture);
  484. begin
  485. FMask.Assign(val);
  486. end;
  487. procedure TGLWaterPlane.DoMaskChanged(Sender: TObject);
  488. begin
  489. Reset;
  490. StructureChanged;
  491. end;
  492. procedure TGLWaterPlane.SetOptions(const val: TGLWaterPlaneOptions);
  493. begin
  494. if FOptions <> val then
  495. begin
  496. FOptions := val;
  497. StructureChanged;
  498. end;
  499. end;
  500. //-------------------------------------------------------------
  501. initialization
  502. //-------------------------------------------------------------
  503. RegisterClasses([TGLWaterPlane]);
  504. end.