GLS.FireFX.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.FireFX;
  5. (* Fire special effect *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.SysUtils,
  12. System.Types,
  13. GLS.OpenGLTokens,
  14. GLS.Scene,
  15. GLS.PipelineTransformation,
  16. GLS.XCollection,
  17. GLS.VectorGeometry,
  18. GLS.Context,
  19. GLS.VectorLists,
  20. GLS.VectorTypes,
  21. GLS.Cadencer,
  22. GLS.Color,
  23. GLS.BaseClasses,
  24. GLS.Coordinates,
  25. GLS.Manager,
  26. GLS.RenderContextInfo,
  27. GLS.State,
  28. GLS.TextureFormat;
  29. type
  30. PGLFireParticle = ^TGLFireParticle;
  31. TGLFireParticle = record
  32. Position: TGLVector;
  33. Speed: TGLVector;
  34. Alpha: Single;
  35. TimeToLive, LifeLength: Single;
  36. end;
  37. TGLFireParticleArray = array[0..MAXINT shr 6] of TGLFireParticle;
  38. PGLFireParticleArray = ^TGLFireParticleArray;
  39. TGLBFireFX = class;
  40. (* Fire special effect manager.
  41. Defines the looks and behaviour of a particle system that can be made
  42. to look fire-like. *)
  43. TGLFireFXManager = class(TGLCadenceAbleComponent)
  44. private
  45. FClients: TList;
  46. FFireParticles: PGLFireParticleArray;
  47. FFireDir, FInitialDir: TGLCoordinates;
  48. FCadencer: TGLCadencer;
  49. FMaxParticles, FParticleLife: Integer;
  50. FParticleSize, FFireDensity, FFireEvaporation: Single;
  51. FFireCrown, FParticleInterval, IntervalDelta: Single;
  52. NP: Integer;
  53. FInnerColor, FOuterColor: TGLColor;
  54. FFireBurst, FFireRadius: Single;
  55. FDisabled, FPaused, FUseInterval: Boolean;
  56. FReference: TGLBaseSceneObject;
  57. FNoZWrite: Boolean;
  58. protected
  59. procedure RegisterClient(aClient: TGLBFireFX);
  60. procedure DeRegisterClient(aClient: TGLBFireFX);
  61. procedure DeRegisterAllClients;
  62. procedure SetFireDir(const val: TGLCoordinates);
  63. procedure SetInitialDir(const val: TGLCoordinates);
  64. procedure SetCadencer(const val: TGLCadencer);
  65. function StoreParticleSize: Boolean;
  66. procedure SetInnerColor(const val: TGLcolor);
  67. procedure SetOuterColor(const val: TGLcolor);
  68. procedure SetReference(const val: TGLBaseSceneObject);
  69. procedure SetMaxParticles(const val: Integer);
  70. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  71. procedure CalcFire(deltaTime: Double; ParticleInterval, ParticleLife: Single;
  72. FireAlpha: Single);
  73. procedure AffParticle3d(Color2: TGLColorVector; const mat: TGLMatrix);
  74. public
  75. constructor Create(AOwner: TComponent); override;
  76. destructor Destroy; override;
  77. // Reinitializes the fire.
  78. procedure FireInit;
  79. (* Spawns a large quantity of particles to simulate an isotropic explosion.
  80. This method generates an isotropic explosion, i.e. there is no
  81. privilegied direction in the initial vector. *)
  82. procedure IsotropicExplosion(minInitialSpeed, maxInitialSpeed, lifeBoostFactor: Single;
  83. nbParticles: Integer = -1);
  84. (* Spawns a large quantity of particles to simulate a ring explosion.
  85. This method generates a ring explosion. The plane of the ring is described
  86. by ringVectorX/Y, which should be of unit length (but you may not
  87. make them of unit length if you want "elliptic" rings). *)
  88. procedure RingExplosion(minInitialSpeed, maxInitialSpeed, lifeBoostFactor: Single;
  89. const ringVectorX, ringVectorY: TAffineVector;
  90. nbParticles: Integer = -1);
  91. // Current Nb of particles.
  92. property ParticleCount: Integer read NP;
  93. procedure DoProgress(const progressTime: TGLProgressTimes); override;
  94. published
  95. // Adjusts the acceleration direction (abs coordinates).
  96. property FireDir: TGLCoordinates read FFireDir write SetFireDir;
  97. // Adjusts the initial direction (abs coordinates).
  98. property InitialDir: TGLCoordinates read FInitialDir write SetInitialDir;
  99. // The cadencer that will "drive" the animation of the system.
  100. property Cadencer: TGLCadencer read FCadencer write SetCadencer;
  101. // Maximum number of simultaneous particles in the system.
  102. property MaxParticles: Integer read FMaxParticles write SetMaxParticles default 256;
  103. // Size of the particle, in absolute units.
  104. property ParticleSize: Single read FParticleSize write FParticleSize stored StoreParticleSize;
  105. // Inner color of a particle.
  106. property InnerColor: TGLcolor read FInnerColor write SetInnerColor;
  107. // Outer color of a particle.
  108. property OuterColor: TGLcolor read FOuterColor write SetOuterColor; // default clrWhite;
  109. property FireDensity: Single read FFireDensity write FFireDensity;
  110. property FireEvaporation: Single read FFireEvaporation write FFireEvaporation;
  111. (* Adjust a crown (circular) radius on which particles are spawned.
  112. With a value of zero, the particles are spawned in the FireRadius
  113. cube around the origin, with a non zero value, they appear in
  114. a torus of major radius FireCrown, and minor radius FireRadius*1.73. *)
  115. property FireCrown: Single read FFireCrown write FFireCrown;
  116. // Life length of particle.
  117. property ParticleLife: Integer read FParticleLife write FParticleLife default 3;
  118. property FireBurst: Single read FFireBurst write FFireBurst;
  119. // Adjusts the random birth radius for particles (actually a birth cube).
  120. property FireRadius: Single read FFireRadius write FFireRadius;
  121. (* If true, no new particles are spawn.
  122. But current ones continue to live and die. *)
  123. property Disabled: Boolean read FDisabled write FDisabled;
  124. // When paused, the fire animation is freezed.
  125. property Paused: Boolean read FPaused write FPaused;
  126. (* Interval between particles births (in sec).
  127. The interval may not be honoured if MawParticles is reached. *)
  128. property ParticleInterval: Single read FParticleInterval write FParticleInterval;
  129. (* Enable/disable use of ParticleInterval.
  130. If true ParticleInterval is used, if False, the system will attempt
  131. to maintain a particle count of MaxParticles, by spawning new
  132. particles to replace the dead ones ASAP. *)
  133. property UseInterval: Boolean read FUseInterval write FUseInterval;
  134. // Particle's render won't write to Z-Buffer
  135. property NoZWrite: Boolean read FNoZWrite write FNoZWrite default True;
  136. (* Specifies an optional object whose position to use as reference.
  137. This property allows switching between static/shared fires (for
  138. fireplaces or static torches) and dynamic fire trails.
  139. The absolute position of the reference object is 'central' spawning
  140. point for new particles, usually, the object will be the one and only
  141. one on which the effect is applied. *)
  142. property Reference: TGLBaseSceneObject read FReference write SetReference;
  143. end;
  144. (* Fire special effect.
  145. This effect works as a client of TFireFXManager *)
  146. TGLBFireFX = class(TGLObjectPostEffect)
  147. private
  148. FManager: TGLFireFXManager;
  149. FManagerName: string; // NOT persistent, temporarily used for persistence
  150. protected
  151. procedure SetManager(const val: TGLFireFXManager);
  152. procedure WriteToFiler(writer: TWriter); override;
  153. procedure ReadFromFiler(reader: TReader); override;
  154. procedure Loaded; override;
  155. public
  156. constructor Create(aOwner: TXCollection); override;
  157. destructor Destroy; override;
  158. procedure Assign(Source: TPersistent); override;
  159. class function FriendlyName: string; override;
  160. class function FriendlyDescription: string; override;
  161. // Old - procedure Render(sceneBuffer: TGLSceneBuffer; var rci: TGLRenderContextInfo); override;
  162. procedure Render(var rci: TGLRenderContextInfo); override;
  163. published
  164. // Refers the collision manager.
  165. property Manager: TGLFireFXManager read FManager write SetManager;
  166. end;
  167. (* Returns or creates the TGLBFireFX within the given behaviours.
  168. This helper function is convenient way to access a TGLBFireFX. *)
  169. function GetOrCreateFireFX(effects: TGLEffects): TGLBFireFX; overload;
  170. (* Returns or creates the TGLBFireFX within the given object's behaviours.
  171. This helper function is convenient way to access a TGLBFireFX. *)
  172. function GetOrCreateFireFX(obj: TGLBaseSceneObject): TGLBFireFX; overload;
  173. // ------------------------------------------------------------------
  174. implementation
  175. // ------------------------------------------------------------------
  176. function GetOrCreateFireFX(effects: TGLEffects): TGLBFireFX;
  177. var
  178. i: Integer;
  179. begin
  180. i := effects.IndexOfClass(TGLBFireFX);
  181. if i >= 0 then
  182. Result := TGLBFireFX(effects[i])
  183. else
  184. Result := TGLBFireFX.Create(effects);
  185. end;
  186. function GetOrCreateFireFX(obj: TGLBaseSceneObject): TGLBFireFX;
  187. begin
  188. Result := GetOrCreateFireFX(obj.Effects);
  189. end;
  190. // ------------------
  191. // ------------------ TGLFireFXManager ------------------
  192. // ------------------
  193. constructor TGLFireFXManager.Create(AOwner: TComponent);
  194. begin
  195. inherited Create(AOwner);
  196. FClients := TList.Create;
  197. RegisterManager(Self);
  198. FFireDir := TGLCoordinates.CreateInitialized(Self, VectorMake(0, 0.5, 0), csPoint);
  199. FInitialDir := TGLCoordinates.CreateInitialized(Self, YHmgVector, csPoint);
  200. FMaxParticles := 256;
  201. FParticleSize := 1.0;
  202. FInnerColor := TGLColor.Create(Self);
  203. FInnerColor.Initialize(clrYellow);
  204. FOuterColor := TGLColor.Create(Self);
  205. FOuterColor.Initialize(clrOrange);
  206. FFireDensity := 1;
  207. FFireEvaporation := 0.86;
  208. FFireCrown := 0;
  209. FParticleLife := 3;
  210. FFireBurst := 0;
  211. FFireRadius := 1;
  212. FParticleInterval := 0.1;
  213. FDisabled := false;
  214. Fpaused := false;
  215. FUseInterval := True;
  216. FNoZWrite := True;
  217. IntervalDelta := 0;
  218. FireInit;
  219. end;
  220. destructor TGLFireFXManager.Destroy;
  221. begin
  222. DeRegisterAllClients;
  223. DeRegisterManager(Self);
  224. FreeMem(FFireParticles);
  225. FInnerColor.Free;
  226. FOuterColor.Free;
  227. FClients.Free;
  228. FFireDir.Free;
  229. FInitialDir.Free;
  230. inherited Destroy;
  231. end;
  232. procedure TGLFireFXManager.RegisterClient(aClient: TGLBFireFX);
  233. begin
  234. if Assigned(aClient) then
  235. if FClients.IndexOf(aClient) < 0 then
  236. begin
  237. FClients.Add(aClient);
  238. aClient.FManager := Self;
  239. end;
  240. end;
  241. procedure TGLFireFXManager.DeRegisterClient(aClient: TGLBFireFX);
  242. begin
  243. if Assigned(aClient) then
  244. begin
  245. aClient.FManager := nil;
  246. FClients.Remove(aClient);
  247. end;
  248. end;
  249. procedure TGLFireFXManager.DeRegisterAllClients;
  250. var
  251. i: Integer;
  252. begin
  253. // Fast deregistration
  254. for i := 0 to FClients.Count - 1 do
  255. TGLBFireFX(FClients[i]).FManager := nil;
  256. FClients.Clear;
  257. end;
  258. procedure TGLFireFXManager.SetFireDir(const val: TGLCoordinates);
  259. begin
  260. FFireDir.Assign(val);
  261. end;
  262. procedure TGLFireFXManager.SetInitialDir(const val: TGLCoordinates);
  263. begin
  264. FInitialDir.Assign(val);
  265. end;
  266. procedure TGLFireFXManager.SetCadencer(const val: TGLCadencer);
  267. begin
  268. if FCadencer <> val then
  269. begin
  270. if Assigned(FCadencer) then
  271. FCadencer.UnSubscribe(Self);
  272. FCadencer := val;
  273. if Assigned(FCadencer) then
  274. FCadencer.Subscribe(Self);
  275. end;
  276. end;
  277. function TGLFireFXManager.StoreParticleSize: Boolean;
  278. begin
  279. Result := (FParticleSize <> 1);
  280. end;
  281. procedure TGLFireFXManager.SetInnerColor(const val: TGLcolor);
  282. begin
  283. if FInnerColor <> val then
  284. begin
  285. FInnerColor.color := val.color;
  286. FireInit;
  287. end;
  288. end;
  289. procedure TGLFireFXManager.SetOuterColor(const val: TGLcolor);
  290. begin
  291. if FOuterColor <> val then
  292. begin
  293. FOuterColor.color := val.color;
  294. FireInit;
  295. end;
  296. end;
  297. procedure TGLFireFXManager.SetReference(const val: TGLBaseSceneObject);
  298. begin
  299. // nothing more yet, maybe later
  300. FReference := val;
  301. end;
  302. procedure TGLFireFXManager.SetMaxParticles(const val: Integer);
  303. begin
  304. if val <> MaxParticles then
  305. begin
  306. if val > 0 then
  307. FMaxParticles := val
  308. else
  309. FMaxParticles := 0;
  310. ReallocMem(FFireParticles, MaxParticles * Sizeof(TGLFireParticle));
  311. if NP > MaxParticles then
  312. NP := MaxParticles;
  313. end;
  314. end;
  315. procedure TGLFireFXManager.Notification(AComponent: TComponent; Operation: TOperation);
  316. begin
  317. if Operation = opRemove then
  318. begin
  319. if AComponent = FCadencer then
  320. Cadencer := nil
  321. else if AComponent = FReference then
  322. Reference := nil;
  323. end;
  324. inherited;
  325. end;
  326. procedure TGLFireFXManager.DoProgress(const progressTime: TGLProgressTimes);
  327. var
  328. i: Integer;
  329. begin
  330. // Progress the particles
  331. if (not FPaused) and (FParticleInterval > 0) then
  332. CalcFire(progressTime.deltaTime * (1.0 + Abs(FFireBurst)),
  333. FParticleInterval, FParticleLife, FFireDensity);
  334. // Invalidate all clients
  335. for i := 0 to FClients.Count - 1 do
  336. TGLBFireFX(FClients[i]).OwnerBaseSceneObject.NotifyChange(TGLBFireFX(FClients[i]));
  337. end;
  338. procedure TGLFireFXManager.FireInit;
  339. begin
  340. IntervalDelta := 0;
  341. NP := 0;
  342. ReallocMem(FFireParticles, FMaxParticles * Sizeof(TGLFireParticle));
  343. end;
  344. procedure TGLFireFXManager.IsotropicExplosion(minInitialSpeed, maxInitialSpeed, lifeBoostFactor: Single;
  345. nbParticles: Integer = -1);
  346. var
  347. n: Integer;
  348. tmp, refPos: TGLVector;
  349. begin
  350. if nbParticles < 0 then
  351. n := MaxInt
  352. else
  353. n := nbParticles;
  354. if Assigned(Reference) then
  355. refPos := Reference.AbsolutePosition
  356. else
  357. refPos := NullHmgPoint;
  358. while (NP < MaxParticles) and (n > 0) do
  359. begin
  360. // okay, ain't exactly "isotropic"...
  361. SetVector(tmp, Random - 0.5, Random - 0.5, Random - 0.5, 0);
  362. NormalizeVector(tmp);
  363. ScaleVector(tmp, minInitialSpeed + Random * (maxInitialSpeed - minInitialSpeed));
  364. with FFireParticles^[NP] do
  365. begin
  366. Position := VectorAdd(refPos, VectorMake((2 * Random - 1) * FireRadius, (2 * Random - 1) * FireRadius, (2 * Random - 1) * FireRadius));
  367. Speed := tmp;
  368. TimeToLive := ParticleLife * (Random * 0.5 + 0.5) * lifeBoostFactor;
  369. LifeLength := TimeToLive;
  370. Alpha := FireDensity;
  371. end;
  372. Inc(NP);
  373. Dec(n);
  374. end;
  375. end;
  376. procedure TGLFireFXManager.RingExplosion(minInitialSpeed, maxInitialSpeed, lifeBoostFactor: Single;
  377. const ringVectorX, ringVectorY: TAffineVector;
  378. nbParticles: Integer = -1);
  379. var
  380. n: Integer;
  381. tmp, refPos: TGLVector;
  382. fx, fy, d: Single;
  383. begin
  384. if nbParticles < 0 then
  385. n := MaxInt
  386. else
  387. n := nbParticles;
  388. if Assigned(Reference) then
  389. refPos := Reference.AbsolutePosition
  390. else
  391. refPos := NullHmgPoint;
  392. while (NP < MaxParticles) and (n > 0) do
  393. begin
  394. // okay, ain't exactly and "isotropic" ring...
  395. fx := Random - 0.5;
  396. fy := Random - 0.5;
  397. d := RSqrt(Sqr(fx) + Sqr(fy));
  398. PAffineVector(@tmp)^ := VectorCombine(ringVectorX, ringVectorY, fx * d, fy * d);
  399. tmp.W := 1;
  400. ScaleVector(tmp, minInitialSpeed + Random * (maxInitialSpeed - minInitialSpeed));
  401. with FFireParticles^[NP] do
  402. begin
  403. Position := VectorAdd(refPos, VectorMake((2 * Random - 1) * FireRadius, (2 * Random - 1) * FireRadius, (2 * Random - 1) * FireRadius));
  404. Speed := tmp;
  405. TimeToLive := ParticleLife * (Random * 0.5 + 0.5) * lifeBoostFactor;
  406. LifeLength := TimeToLive;
  407. Alpha := FireDensity;
  408. end;
  409. Inc(NP);
  410. Dec(n);
  411. end;
  412. end;
  413. procedure TGLFireFXManager.CalcFire(deltaTime: Double;
  414. particleInterval, particleLife: Single; fireAlpha: Single);
  415. var
  416. N, I: Integer;
  417. Fdelta: Single;
  418. tmp, refPos: TGLVector;
  419. begin
  420. // Process live stuff
  421. N := 0;
  422. I := 0;
  423. while N < NP do
  424. begin
  425. FFireParticles^[I].TimeToLive := FFireParticles^[I].TimeToLive - deltaTime;
  426. if (FFireParticles^[I].TimeToLive <= 0) then
  427. begin
  428. //Get the prev element
  429. Dec(NP);
  430. FFireParticles^[I] := FFireParticles^[NP];
  431. end
  432. else
  433. begin
  434. //animate it
  435. with FFireParticles^[I] do
  436. begin
  437. Speed := VectorCombine(Speed, FireDir.AsVector, 1, deltaTime);
  438. Position := VectorCombine(Position, Speed, 1, deltaTime);
  439. end;
  440. Inc(N);
  441. Inc(I);
  442. end;
  443. end;
  444. // Spawn new particles
  445. if FDisabled then
  446. Exit;
  447. if Assigned(Reference) then
  448. refPos := Reference.AbsolutePosition
  449. else
  450. refPos := NullHmgPoint;
  451. IntervalDelta := IntervalDelta + deltaTime / ParticleInterval;
  452. if (not UseInterval) or (IntervalDelta > 1) then
  453. begin
  454. fDelta := Frac(IntervalDelta);
  455. while (NP < MaxParticles) do
  456. begin
  457. SetVector(tmp, (2 * Random - 1) * FireRadius, (2 * Random - 1) * FireRadius,
  458. FireCrown + (2 * Random - 1) * FireRadius);
  459. RotateVectorAroundY(PAffineVector(@tmp)^, Random * 2 * PI);
  460. AddVector(tmp, refPos);
  461. with FFireParticles^[NP] do
  462. begin
  463. Position := tmp;
  464. Speed := InitialDir.AsVector;
  465. TimeToLive := ParticleLife * (Random * 0.5 + 0.5);
  466. LifeLength := TimeToLive;
  467. Alpha := FireAlpha;
  468. end;
  469. Inc(NP);
  470. if UseInterval then
  471. Break;
  472. end;
  473. IntervalDelta := fDelta;
  474. end;
  475. end;
  476. procedure TGLFireFXManager.AffParticle3d(Color2: TGLColorVector; const mat: TGLMatrix);
  477. var
  478. vx, vy: TGLVector;
  479. i: Integer;
  480. begin
  481. for i := 0 to 2 do
  482. begin
  483. vx.V[i] := mat.V[i].X * FParticleSize;
  484. vy.V[i] := mat.V[i].Y * FParticleSize;
  485. end;
  486. begin
  487. gl.Begin_(GL_TRIANGLE_FAN);
  488. gl.Vertex3fv(@NullVector);
  489. gl.Color4f(Color2.X, Color2.Y, Color2.Z, 0.0);
  490. gl.Vertex3f(-vx.X, -vx.Y, -vx.Z);
  491. // those things should be composited in the model view matrix
  492. gl.Vertex3f(-0.5 * vx.X + FFireEvaporation * vy.X,
  493. -0.5 * vx.Y + FFireEvaporation * vy.Y,
  494. -0.5 * vx.Z + FFireEvaporation * vy.Z);
  495. gl.Vertex3f(+0.5 * vx.X + FFireEvaporation * vy.X,
  496. +0.5 * vx.Y + FFireEvaporation * vy.Y,
  497. +0.5 * vx.Z + FFireEvaporation * vy.Z);
  498. gl.Vertex3f(+vx.X, +vx.Y, +vx.Z);
  499. gl.Vertex3f(+0.5 * vx.X - FFireEvaporation * vy.X,
  500. +0.5 * vx.Y - FFireEvaporation * vy.Y,
  501. +0.5 * vx.Z - FFireEvaporation * vy.Z);
  502. gl.Vertex3f(-0.5 * vx.X - FFireEvaporation * vy.X,
  503. -0.5 * vx.Y - FFireEvaporation * vy.Y,
  504. -0.5 * vx.Z - FFireEvaporation * vy.Z);
  505. gl.Vertex3f(-vx.X, -vx.Y, -vx.Z);
  506. gl.End_;
  507. end;
  508. end;
  509. // ------------------
  510. // ------------------ TGLBFireFX ------------------
  511. // ------------------
  512. constructor TGLBFireFX.Create(aOwner: TXCollection);
  513. begin
  514. inherited Create(aOwner);
  515. end;
  516. destructor TGLBFireFX.Destroy;
  517. begin
  518. Manager := nil;
  519. inherited Destroy;
  520. end;
  521. class function TGLBFireFX.FriendlyName: string;
  522. begin
  523. Result := 'FireFX';
  524. end;
  525. class function TGLBFireFX.FriendlyDescription: string;
  526. begin
  527. Result := 'Fire FX';
  528. end;
  529. procedure TGLBFireFX.WriteToFiler(writer: TWriter);
  530. begin
  531. with writer do
  532. begin
  533. // ArchiveVersion 1, added inherited call
  534. WriteInteger(1);
  535. inherited;
  536. if Assigned(FManager) then
  537. WriteString(FManager.GetNamePath)
  538. else
  539. WriteString('');
  540. end;
  541. end;
  542. procedure TGLBFireFX.ReadFromFiler(reader: TReader);
  543. var
  544. archiveVersion : Integer;
  545. begin
  546. with reader do
  547. begin
  548. archiveVersion := ReadInteger;
  549. Assert(archiveVersion in [0..1]);
  550. if archiveVersion >= 1 then
  551. inherited;
  552. FManagerName := ReadString;
  553. Manager := nil;
  554. end;
  555. end;
  556. procedure TGLBFireFX.Loaded;
  557. var
  558. mng: TComponent;
  559. begin
  560. inherited;
  561. if FManagerName <> '' then
  562. begin
  563. mng := FindManager(TGLFireFXManager, FManagerName);
  564. if Assigned(mng) then
  565. Manager := TGLFireFXManager(mng);
  566. FManagerName := '';
  567. end;
  568. end;
  569. procedure TGLBFireFX.Assign(Source: TPersistent);
  570. begin
  571. if Source is TGLBFireFX then
  572. begin
  573. Manager := TGLBFireFX(Source).Manager;
  574. end;
  575. inherited Assign(Source);
  576. end;
  577. procedure TGLBFireFX.SetManager(const val: TGLFireFXManager);
  578. begin
  579. if val <> FManager then
  580. begin
  581. if Assigned(FManager) then
  582. FManager.DeRegisterClient(Self);
  583. if Assigned(val) then
  584. val.RegisterClient(Self);
  585. end;
  586. end;
  587. procedure TGLBFireFX.Render(var rci: TGLRenderContextInfo);
  588. var
  589. n: Integer;
  590. i: Integer;
  591. innerColor: TGLVector;
  592. lastTr: TAffineVector;
  593. distList: TGLSingleList;
  594. objList: TList;
  595. fp: PGLFireParticle;
  596. begin
  597. if Manager = nil then
  598. Exit;
  599. rci.PipelineTransformation.Push;
  600. // revert to the base model matrix in the case of a referenced fire
  601. if Assigned(Manager.Reference) then
  602. rci.PipelineTransformation.SetModelMatrix(IdentityHmgMatrix);
  603. rci.GLStates.CurrentProgram := 0;
  604. rci.GLStates.Disable(stCullFace);
  605. rci.GLStates.ActiveTextureEnabled[ttTexture2D] := False;
  606. rci.GLStates.Disable(stLighting);
  607. rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
  608. rci.GLStates.Enable(stBlend);
  609. rci.GLStates.Disable(stAlphaTest);
  610. rci.GLStates.Enable(stDepthTest);
  611. rci.GLStates.DepthFunc := cfLEqual;
  612. rci.GLStates.DepthWriteMask := not Manager.NoZWrite;
  613. n := Manager.NP;
  614. if n > 1 then
  615. begin
  616. distList := TGLSingleList.Create;
  617. objList := TList.Create;
  618. for i := 0 to n - 1 do
  619. begin
  620. fp := @(Manager.FFireParticles[i]);
  621. distList.Add(VectorDotProduct(rci.cameraDirection, fp^.Position));
  622. objList.Add(fp);
  623. end;
  624. QuickSortLists(0, N - 1, distList, objList);
  625. lastTr := NullVector;
  626. SetVector(innerColor, Manager.FInnerColor.Color);
  627. for i := n - 1 downto 0 do
  628. begin
  629. fp := PGLFireParticle(objList[i]);
  630. gl.Translatef(fp^.Position.X - lastTr.X,
  631. fp^.Position.Y - lastTr.Y,
  632. fp^.Position.Z - lastTr.Z);
  633. SetVector(lastTr, fp^.Position);
  634. innerColor.W := fp^.Alpha * fp^.TimeToLive / Sqr(fp^.LifeLength);
  635. gl.Color4fv(@innerColor);
  636. Manager.AffParticle3d(Manager.FOuterColor.Color, rci.PipelineTransformation.ViewMatrix^);
  637. end;
  638. objList.Free;
  639. distList.Free;
  640. end;
  641. rci.PipelineTransformation.Pop;
  642. end;
  643. // ------------------------------------------------------------------
  644. initialization
  645. // ------------------------------------------------------------------
  646. // class registrations
  647. RegisterXCollectionItemClass(TGLBFireFX);
  648. finalization
  649. UnregisterXCollectionItemClass(TGLBFireFX);
  650. end.