2
0

GXS.FireFX.pas 21 KB

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