GLS.FireFX.pas 21 KB

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