GXS.WaterPlane.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.WaterPlane;
  5. (* A plane simulating animated water *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. Stage.OpenGL4, // GL_EXT_compiled_vertex_array
  12. System.Classes,
  13. FMX.Objects,
  14. FMX.Types,
  15. FMX.Graphics,
  16. Stage.VectorGeometry,
  17. Stage.VectorTypes,
  18. Stage.Utils,
  19. GXS.VectorLists,
  20. GXS.PersistentClasses,
  21. GXS.BaseClasses,
  22. GXS.Scene,
  23. GXS.ImageUtils,
  24. GXS.Context,
  25. GXS.RenderContextInfo;
  26. type
  27. TgxWaterPlaneOption = (wpoTextured);
  28. TgxWaterPlaneOptions = set of TgxWaterPlaneOption;
  29. const
  30. cDefaultWaterPlaneOptions = [wpoTextured];
  31. type
  32. TgxWaterPlane = class(TgxSceneObject)
  33. private
  34. FLocks: packed array of ByteBool;
  35. FPositions, FVelocity: packed array of Single;
  36. FPlaneQuadIndices: TgxPersistentObjectList;
  37. FPlaneQuadTexCoords: TgxTexPointList;
  38. FPlaneQuadVertices: TgxAffineVectorList;
  39. FPlaneQuadNormals: TgxAffineVectorList;
  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: TImage;
  51. FOptions: TgxWaterPlaneOptions;
  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: TImage);
  60. procedure SetOptions(const val: TgxWaterPlaneOptions);
  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: TgxProgressTimes); override;
  71. procedure BuildList(var rci: TgxRenderContextInfo); override;
  72. procedure Assign(Source: TPersistent); override;
  73. function AxisAlignedDimensionsUnscaled: TVector4f; override;
  74. procedure CreateRippleAtGridPos(X, Y: Integer);
  75. procedure CreateRippleAtWorldPos(const X, Y, z: Single); overload;
  76. procedure CreateRippleAtWorldPos(const pos: TVector4f); 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: TgxWaterPlaneOptions 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: TImage 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 TgxWaterPlane.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 := TgxPersistentObjectList.Create;
  123. FPlaneQuadTexCoords := TgxTexPointList.Create;
  124. FPlaneQuadVertices := TgxAffineVectorList.Create;
  125. FPlaneQuadNormals := TgxAffineVectorList.Create;
  126. FMask := TImage.Create(AOwner);
  127. FMask.Bitmap.OnChange := DoMaskChanged;
  128. SetResolution(64);
  129. end;
  130. destructor TgxWaterPlane.Destroy;
  131. begin
  132. FMask.Free;
  133. FPlaneQuadNormals.Free;
  134. FPlaneQuadVertices.Free;
  135. FPlaneQuadTexCoords.Free;
  136. FPlaneQuadIndices.CleanFree;
  137. inherited;
  138. end;
  139. procedure TgxWaterPlane.DoProgress(const progressTime: TgxProgressTimes);
  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 TgxWaterPlane.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 TgxWaterPlane.CreateRippleAtWorldPos(const X, Y, z: Single);
  191. var
  192. vv: TVector4f;
  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 TgxWaterPlane.CreateRippleAtWorldPos(const pos: TVector4f);
  199. var
  200. vv: TVector4f;
  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 TgxWaterPlane.CreateRippleRandom;
  207. begin
  208. CreateRippleAtGridPos(Random(Resolution - 3) + 2, Random(Resolution - 3) + 2);
  209. end;
  210. procedure TgxWaterPlane.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 TgxWaterPlane.Reset;
  247. var
  248. i, j, ij, resSqr: Integer;
  249. maskBmp: TBitmap;
  250. scanLine: PIntegerArray;
  251. il: TgxIntegerList;
  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. { TODO : E2129 Cannot assign to a read-only property }
  266. (* maskBmp.PixelFormat:= TPixelFormat.RGBA32F; //in VCL glpf32bit; *)
  267. maskBmp.Width := Resolution;
  268. maskBmp.Height := Resolution;
  269. { TODO : E2003 Undeclared identifier: 'StretchDraw' }
  270. (* maskBmp.Canvas.StretchDraw(Rect(0, 0, Resolution, Resolution), FMask.Graphic); *)
  271. for j := 0 to Resolution - 1 do
  272. begin
  273. scanLine := BitmapScanLine(maskBmp, Resolution - 1 - j);
  274. // maskBmp.ScanLine[Resolution-1-j];
  275. for i := 0 to Resolution - 1 do
  276. FLocks[i + j * Resolution] := (((scanLine[i] shr 8) and $FF) < 128);
  277. end;
  278. finally
  279. maskBmp.Free;
  280. end;
  281. end;
  282. FPlaneQuadIndices.Clean;
  283. for j := 0 to Resolution - 2 do
  284. begin
  285. il := TgxIntegerList.Create;
  286. for i := 0 to Resolution - 1 do
  287. begin
  288. ij := i + j * Resolution;
  289. if (il.Count and 2) <> 0 then
  290. locked := False
  291. else
  292. begin
  293. locked := FLocks[ij] and FLocks[ij + Resolution];
  294. if locked and (i < Resolution - 1) then
  295. locked := FLocks[ij + 1] and FLocks[ij + Resolution + 1];
  296. end;
  297. if not locked then
  298. il.Add(ij, ij + Resolution)
  299. else if il.Count > 0 then
  300. begin
  301. FPlaneQuadIndices.Add(il);
  302. il := TgxIntegerList.Create;
  303. end;
  304. end;
  305. if il.Count > 0 then
  306. FPlaneQuadIndices.Add(il)
  307. else
  308. il.Free;
  309. end;
  310. end;
  311. procedure TgxWaterPlane.IterComputeVelocity;
  312. var
  313. i, j, ij: Integer;
  314. f1, f2: Single;
  315. posList, velList: PSingleArray;
  316. lockList: PByteArray;
  317. begin
  318. f1 := 0.05;
  319. f2 := 0.01 * FElastic;
  320. posList := @FPositions[0];
  321. velList := @FVelocity[0];
  322. lockList := @FLocks[0];
  323. for i := 1 to Resolution - 2 do
  324. begin
  325. ij := i * Resolution;
  326. for j := 1 to Resolution - 2 do
  327. begin
  328. Inc(ij);
  329. if lockList[ij] <> 0 then
  330. continue;
  331. velList[ij] := velList[ij] + f2 *
  332. (posList[ij] - f1 * (4 * (posList[ij - 1] + posList[ij + 1] +
  333. posList[ij - Resolution] + posList[ij + Resolution]) +
  334. posList[ij - 1 - Resolution] + posList[ij + 1 - Resolution] +
  335. posList[ij - 1 + Resolution] + posList[ij + 1 + Resolution]));
  336. end;
  337. end;
  338. end;
  339. procedure TgxWaterPlane.IterComputePositions;
  340. const
  341. cVelocityIntegrationCoeff: Single = 0.02;
  342. cHeightFactor: Single = 1E-4;
  343. var
  344. ij: Integer;
  345. f: Single;
  346. coeff: Single;
  347. posList, velList: PSingleArray;
  348. lockList: PByteArray;
  349. begin
  350. // Calculate the new ripple positions and update vertex coordinates
  351. coeff := cVelocityIntegrationCoeff * Resolution;
  352. f := cHeightFactor / Resolution;
  353. posList := @FPositions[0];
  354. velList := @FVelocity[0];
  355. lockList := @FLocks[0];
  356. for ij := 0 to Resolution * Resolution - 1 do
  357. begin
  358. if lockList[ij] = 0 then
  359. begin
  360. posList[ij] := posList[ij] - coeff * velList[ij];
  361. velList[ij] := velList[ij] * FViscosity;
  362. FPlaneQuadVertices.List[ij].Y := posList[ij] * f;
  363. end;
  364. end;
  365. end;
  366. procedure TgxWaterPlane.IterComputeNormals;
  367. var
  368. i, j, ij: Integer;
  369. pv: PAffineVector;
  370. posList: PSingleArray;
  371. normList: PAffineVectorArray;
  372. begin
  373. // Calculate the new vertex normals (not normalized, the hardware will handle that)
  374. posList := @FPositions[0];
  375. normList := FPlaneQuadNormals.List;
  376. for i := 1 to Resolution - 2 do
  377. begin
  378. ij := i * Resolution;
  379. for j := 1 to Resolution - 2 do
  380. begin
  381. Inc(ij);
  382. pv := @normList[ij];
  383. pv.X := posList[ij + 1] - posList[ij - 1];
  384. pv.z := posList[ij + Resolution] - posList[ij - Resolution];
  385. end;
  386. end;
  387. end;
  388. procedure TgxWaterPlane.Iterate;
  389. var
  390. t: Int64;
  391. begin
  392. if Visible then
  393. begin
  394. t := StartPrecisionTimer;
  395. IterComputeVelocity;
  396. IterComputePositions;
  397. IterComputeNormals;
  398. FLastIterationStepTime := StopPrecisionTimer(t);
  399. end;
  400. end;
  401. procedure TgxWaterPlane.BuildList(var rci: TgxRenderContextInfo);
  402. var
  403. i: Integer;
  404. il: TgxIntegerList;
  405. begin
  406. glPushClientAttribDefaultEXT(GL_CLIENT_VERTEX_ARRAY_BIT);
  407. glEnableClientState(GL_VERTEX_ARRAY);
  408. glVertexPointer(3, GL_FLOAT, 0, FPlaneQuadVertices.List);
  409. glEnableClientState(GL_NORMAL_ARRAY);
  410. glNormalPointer(GL_FLOAT, 0, FPlaneQuadNormals.List);
  411. if wpoTextured in Options then
  412. begin
  413. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  414. glTexCoordPointer(2, GL_FLOAT, 0, FPlaneQuadTexCoords.List);
  415. end
  416. else
  417. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  418. if GL_EXT_compiled_vertex_array then
  419. glLockArraysEXT(0, FPlaneQuadVertices.Count);
  420. for i := 0 to FPlaneQuadIndices.Count - 1 do
  421. begin
  422. il := TgxIntegerList(FPlaneQuadIndices[i]);
  423. glDrawElements(GL_QUAD_STRIP, il.Count, GL_UNSIGNED_INT, il.List);
  424. end;
  425. if GL_EXT_compiled_vertex_array then
  426. glUnlockArraysEXT;
  427. glPopClientAttrib;
  428. end;
  429. procedure TgxWaterPlane.Assign(Source: TPersistent);
  430. begin
  431. if Assigned(Source) and (Source is TgxWaterPlane) then
  432. begin
  433. Active := TgxWaterPlane(Source).Active;
  434. RainTimeInterval := TgxWaterPlane(Source).RainTimeInterval;
  435. RainForce := TgxWaterPlane(Source).RainForce;
  436. Viscosity := TgxWaterPlane(Source).Viscosity;
  437. end;
  438. inherited Assign(Source);
  439. end;
  440. function TgxWaterPlane.AxisAlignedDimensionsUnscaled: TVector4f;
  441. begin
  442. Result.X := 0.5 * Abs(Resolution);
  443. Result.Y := 0;
  444. Result.z := 0.5 * Abs(FResolution);
  445. end;
  446. procedure TgxWaterPlane.SetElastic(const value: Single);
  447. begin
  448. FElastic := value;
  449. end;
  450. procedure TgxWaterPlane.SetResolution(const value: Integer);
  451. begin
  452. if value <> FResolution then
  453. begin
  454. FResolution := value;
  455. if FResolution < 16 then
  456. FResolution := 16;
  457. InitResolution;
  458. end;
  459. end;
  460. procedure TgxWaterPlane.SetRainTimeInterval(Const val: Integer);
  461. begin
  462. if (val >= 0) and (val <= 1000000) then
  463. FRainTimeInterval := val;
  464. end;
  465. Procedure TgxWaterPlane.SetViscosity(const val: Single);
  466. begin
  467. if (val >= 0) and (val <= 1) then
  468. FViscosity := val;
  469. end;
  470. procedure TgxWaterPlane.SetRainForce(const val: Single);
  471. begin
  472. if (val >= 0) and (val <= 1000000) then
  473. FRainForce := val;
  474. end;
  475. procedure TgxWaterPlane.SetSimulationFrequency(const val: Single);
  476. begin
  477. if FSimulationFrequency <> val then
  478. begin
  479. FSimulationFrequency := val;
  480. if FSimulationFrequency < 1 then
  481. FSimulationFrequency := 1;
  482. FTimeToNextUpdate := 0;
  483. end;
  484. end;
  485. procedure TgxWaterPlane.SetMask(val: TImage);
  486. begin
  487. FMask.Assign(val);
  488. end;
  489. procedure TgxWaterPlane.DoMaskChanged(Sender: TObject);
  490. begin
  491. Reset;
  492. StructureChanged;
  493. end;
  494. procedure TgxWaterPlane.SetOptions(const val: TgxWaterPlaneOptions);
  495. begin
  496. if FOptions <> val then
  497. begin
  498. FOptions := val;
  499. StructureChanged;
  500. end;
  501. end;
  502. // -------------------------------------------------------------
  503. initialization
  504. // -------------------------------------------------------------
  505. RegisterClasses([TgxWaterPlane]);
  506. end.