LZScene

Форк
0
/
GLParticleFX.pas 
3534 строки · 101.6 Кб
1
//
2
// This unit is part of the GLScene Engine https://github.com/glscene
3
//
4
{
5
   Base classes for scene-wide blended particles FX.
6

7
   These provide a mechanism to render heterogenous particles systems with per
8
   particle depth-sorting (allowing correct rendering of interwoven separate
9
   fire and smoke particle systems for instance).
10

11
    History :  
12
       21/01/01 - DanB - Added "inherited" call to TGLParticleFXEffect.WriteToFiler
13
       23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
14
       22/04/10 - Yar - Fixes after GLState revision
15
       05/03/10 - DanB - More state added to TGLStateCache
16
       22/01/10 - Yar  - Added bmp32.Blank:=false for memory allocation
17
                            and fix RegisterAsOpenGLTexture
18
       12/10/08 - DanB - fix to TGLLifeColoredPFXManager.ComputeRotateAngle (it used lck.FDoScale
19
                            instead of lck.FDoRotate), made more use of RCI instead of accessing via global variables.
20
                            Changed order of transformations when rendering particles, now does rotation+scaling before translation.
21
                            Disabled FRotationCenter,
22
       15/02/08 - Mrqzzz - Fixed SizeScale in Lifetimes of TGLBaseSpritePFXManager (was ignored)
23
       06/06/07 - DaStr - Added GLColor to uses (BugtrackerID = 1732211)
24
       02/04/07 - DaStr - TPFXLifeColors now inherits from TOwnedCollection
25
                             (thanks Burkhard Carstens)
26
       30/03/07 - DaStr - Added $I GLScene.inc
27
       14/03/07 - DaStr - Added explicit pointer dereferencing
28
                             (thanks Burkhard Carstens) (Bugtracker ID = 1678644)
29
       24/01/07 - DaStr - TGLSourcePFXEffect.Burst and TGLBaseSpritePFXManager.RenderParticle bugfixed
30
                             TGLLifeColoredPFXManager.RotateVertexBuf bugfixed (all based on old code)
31
       28/10/06 - LC - Fixed access violation in TGLParticleFXRenderer. Bugtracker ID=1585907 (thanks Da Stranger)
32
       19/10/06 - LC - Fixed memory leak in TGLParticleFXManager. Bugtracker ID=1551866 (thanks Dave Gravel)
33
       08/10/05 - Mathx - Fixed access violation when a PFXManager was removed from
34
                             form but a particleFX still had a reference to it (added
35
                             the FUsers property). Butracker ID=783625.
36
       17/02/05 - EG - Restored correct PFXSource.Burst relative/absolute behaviour,
37
                          EffectsScale support not added back (no clue what it does... Mrqz?)
38
       23/11/04 - SG - Fixed memory leak in TGLLifeColoredPFXManager (kenguru)
39
       03/10/04 - Mrqzzz - added property TGLParticleFXEffect.DisabledIfOwnerInvisible. Fixed PositionDispersionRange to honour VelocityMode=svmRelative
40
       25/09/04 - Graham Kennedy - Fixed restore of currentTexturingMode
41
       09/09/04 - Mrqzzz - added property TGLParticleFXEffect.EffectScale allowing different scaling of effect with same manager. TGLParticleFXEffect.ArchiveVersion updated to 4
42
       02/08/04 - LR, YHC - BCB corrections: use record instead array
43
                               Replace direct access of some properties by
44
                               a getter and a setter.
45
                               fixed undefined TPFXRegion error in BCB
46
       29/08/04 - Mrqzzz - fixed particles initial position when VelocityMode=svmRelative
47
       28/08/04 - Mrqzzz - fixed particles direction when VelocityMode=svmRelative
48
       09/07/04 - Mrqzzz - small fixup (TGLSourcePFXEffect.WriteToFiler Archive V.4)
49
       08/07/04 - Eugene Kryukov - Added rotation for particles, RotateAngle in
50
                                      LifeColor. And added AbsoluteRotation for TGLDynamicPFXManager
51
       25/04/04 - EG - Added friction, Life sizes, multiple sprites per texture
52
                          and sprites sharing
53
       24/04/04 - Mrqzzz - Added property "enabled" to TGLSourcePFXEffect
54
       15/04/04 - EG - AspectRatio and Rotation added to sprite PFX,
55
                          improved texturing mode switches
56
       26/05/03 - EG - Improved TGLParticleFXRenderer.BuildList
57
       05/11/02 - EG - Enable per-manager blending mode control
58
       27/01/02 - EG - Added TGLLifeColoredPFXManager, TGLBaseSpritePFXManager
59
                          and TGLPointLightPFXManager.
60
       23/01/02 - EG - Added ZWrite and BlendingMode to the PFX renderer,
61
                          minor sort and render optims
62
       22/01/02 - EG - Another RenderParticle color lerp fix (GliGli)
63
       20/01/02 - EG - Several optimization (35% faster on Volcano bench)
64
       18/01/02 - EG - RenderParticle color lerp fix (GliGli)
65
       08/09/01 - EG - Creation (GLParticleFX.omm)
66
    
67
}
68
unit GLParticleFX;
69

70
interface
71

72
{$I GLScene.inc}
73

74
uses
75
  Classes, SysUtils,
76
  GLScene,  OpenGLTokens,  GLCrossPlatform,  GLState, GLVectorTypes,
77
  GLPersistentClasses,  GLVectorGeometry,  GLXCollection,  GLMaterial,
78
  GLCadencer, GLVectorLists,  GLGraphics,  GLContext,  GLColor,  GLBaseClasses,
79
  GLCoordinates,  GLRenderContextInfo,  GLManager,  GLTextureFormat, GLRandomGenerator;
80

81
const
82
  cPFXNbRegions = 128; // number of distance regions
83
  cPFXGranularity = 128; // granularity of particles per region
84

85
type
86

87
  TGLParticleList = class;
88
  TGLParticleFXManager = class;
89
  TGLParticleFXEffect = class;
90

91
  // TGLParticle
92
  //
93
  { Base class for particles.
94
     The class implements properties for position, velocity and time, whatever
95
     you need in excess of that will have to be placed in subclasses (this
96
     class should remain as compact as possible). }
97
  TGLParticle = class(TPersistentObject)
98
  private
99
     
100
    FID, FTag: Integer;
101
    FManager: TGLParticleFXManager; // NOT persistent
102
    FPosition: TAffineVector;
103
    FVelocity: TAffineVector;
104
    FRotation: Single;
105
    FCreationTime: Double;
106
    FEffectScale: Single;
107
    function GetPosition(const Index: Integer): Single;
108
    procedure WritePosition(const Index: Integer; const aValue: Single);
109
    function GetVelocity(const Index: Integer): Single;
110
    procedure WriteVelocity(const Index: Integer; const aValue: Single);
111

112
  protected
113
     
114

115
  public
116
     
117
    constructor Create; override;
118
    destructor Destroy; override;
119
    procedure WriteToFiler(writer: TVirtualWriter); override;
120
    procedure ReadFromFiler(reader: TVirtualReader); override;
121

122
    property Manager: TGLParticleFXManager read FManager write FManager;
123

124
    { Particle's ID, given at birth.
125
       ID is a value unique per manager. }
126
    property ID: Integer read FID;
127
    { Particle's absolute position.
128
       Note that this property is read-accessed directly at rendering time
129
       in the innards of the depth-sorting code. }
130
    property Position: TAffineVector read FPosition write FPosition;
131
    { Particle's velocity.
132
       This velocity is indicative and is NOT automatically applied
133
       to the position during progression events by this class (subclasses
134
       may implement that). }
135
    property Velocity: TAffineVector read FVelocity write FVelocity;
136
    { Time at which particle was created }
137
    property CreationTime: Double read FCreationTime write FCreationTime;
138

139
    property PosX : Single index 0 read GetPosition write WritePosition;
140
    property PosY : Single index 1 read GetPosition write WritePosition;
141
    property PosZ : Single index 2 read GetPosition write WritePosition;
142
    property VelX : Single index 0 read GetVelocity write WriteVelocity;
143
    property VelY : Single index 1 read GetVelocity write WriteVelocity;
144
    property VelZ : Single index 2 read GetVelocity write WriteVelocity;
145

146
    property Tag: Integer read FTag write FTag;
147
  end;
148

149
  TGLParticleClass = class of TGLParticle;
150
  TGLParticleArray = array[0..MaxInt shr 4] of TGLParticle;
151
  PGLParticleArray = ^TGLParticleArray;
152

153
  // TGLParticleList
154
  //
155
  { List of particles.
156
     This list is managed with particles and performance in mind, make sure to
157
     check methods doc. }
158
  TGLParticleList = class(TPersistentObject)
159
  private
160
     
161
    FOwner: TGLParticleFXManager; // NOT persistent
162
    FItemList: TPersistentObjectList;
163
    FDirectList: PGLParticleArray; // NOT persistent
164

165
  protected
166
     
167
    function GetItems(index: Integer): TGLParticle;
168
    procedure SetItems(index: Integer; val: TGLParticle);
169
    procedure AfterItemCreated(Sender: TObject);
170

171
  public
172
     
173
    constructor Create; override;
174
    destructor Destroy; override;
175
    procedure WriteToFiler(writer: TVirtualWriter); override;
176
    procedure ReadFromFiler(reader: TVirtualReader); override;
177

178
    { Refers owner manager }
179
    property Owner: TGLParticleFXManager read FOwner write FOwner;
180
    property Items[index: Integer]: TGLParticle read GetItems write SetItems; default;
181

182
    function ItemCount: Integer;
183
    { Adds a particle to the list.
184
       Particle owneship is defined blindly, if the particle was previously
185
       in another list, it won't be automatically removed from that list. }
186
    function AddItem(aItem: TGLParticle): Integer;
187
    { Removes and frees a particular item for the list.
188
       If the item is not part of the list, nothing is done. 
189
       If found in the list, the item's "slot" is set to nil and item is
190
       freed (after setting its ownership to nil). The nils can be removed
191
       with a call to Pack. }
192
    procedure RemoveAndFreeItem(aItem: TGLParticle);
193
    function IndexOfItem(aItem: TGLParticle): Integer;
194
    { Packs the list by removing all "nil" items.
195
       Note: this functions is orders of magnitude faster than the TList
196
       version. }
197
    procedure Pack;
198

199
    property List: PGLParticleArray read FDirectList;
200
  end;
201

202
  TGLParticleFXRenderer = class;
203
  TPFXCreateParticleEvent = procedure(Sender: TObject; aParticle: TGLParticle) of object;
204

205
  // TGLParticleFXManager
206
  //
207
  { Base class for particle FX managers.
208
     Managers take care of life and death of particles for a particular
209
     particles FX system. You can have multiple scene-wide particle
210
     FX managers in a scene, handled by the same ParticleFxRenderer.
211
     Before subclassing, make sure you understood how the Initialize/Finalize
212
     Rendering, Begin/End Particles and RenderParticles methods (and also
213
     understood that rendering of manager's particles may be interwoven). }
214
  TGLParticleFXManager = class(TGLCadencedComponent)
215
  private
216
     
217
    FBlendingMode: TBlendingMode;
218
    FRenderer: TGLParticleFXRenderer;
219
    FParticles: TGLParticleList;
220
    FNextID: Integer;
221
    FOnCreateParticle: TPFXCreateParticleEvent;
222
    FAutoFreeWhenEmpty: Boolean;
223

224
    FUsers: TList; //list of objects that use this manager
225

226
  protected
227
     
228
    procedure SetRenderer(const val: TGLParticleFXRenderer);
229
    procedure SetParticles(const aParticles: TGLParticleList);
230

231
    { Texturing mode for the particles.
232
       Subclasses should return GL_TEXTURE_1D, 2D or 3D depending on their
233
       needs, and zero if they don't use texturing. This method is used
234
       to reduce the number of texturing activations/deactivations. }
235
    function TexturingMode: Cardinal; virtual; abstract;
236

237
    { Invoked when the particles of the manager will be rendered.
238
       This method is fired with the "base" OpenGL states and matrices
239
       that will be used throughout the whole rendering, per-frame
240
       initialization should take place here. 
241
       OpenGL states/matrices should not be altered in any way here. }
242
    procedure InitializeRendering(var rci: TGLRenderContextInfo); dynamic; abstract;
243
    { Triggered just before rendering a set of particles.
244
       The current OpenGL state should be assumed to be the "base" one as
245
       was found during InitializeRendering. Manager-specific states should
246
       be established here. 
247
       Multiple BeginParticles can occur during a render (but all will be
248
       between InitializeRendering and Finalizerendering, and at least one
249
       particle will be rendered before EndParticles is invoked). }
250
    procedure BeginParticles(var rci: TGLRenderContextInfo); virtual; abstract;
251
    { Request to render a particular particle.
252
       Due to the nature of the rendering, no particular order should be
253
       assumed. If possible, no OpenGL state changes should be made in this
254
       method, but should be placed in Begin/EndParticles. }
255
    procedure RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle); virtual; abstract;
256
    { Triggered after a set of particles as been rendered.
257
       If OpenGL state were altered directly (ie. not through the states
258
       caches of GLMisc), it should be restored back to the "base" state. }
259
    procedure EndParticles(var rci: TGLRenderContextInfo); virtual; abstract;
260
    { Invoked when rendering of particles for this manager is done. }
261
    procedure FinalizeRendering(var rci: TGLRenderContextInfo); dynamic; abstract;
262

263
    { ID for the next created particle. }
264
    property NextID: Integer read FNextID write FNextID;
265

266
    { Blending mode for the particles.
267
       Protected and unused in the base class. }
268
    property BlendingMode: TBlendingMode read FBlendingMode write FBlendingMode;
269
    { Apply BlendingMode relatively to the renderer's blending mode. }
270
    procedure ApplyBlendingMode(var rci: TGLRenderContextInfo);
271
    { Unapply BlendingMode relatively by restoring the renderer's blending mode. }
272
    procedure UnapplyBlendingMode(var rci: TGLRenderContextInfo);
273

274
    procedure registerUser(obj: TGLParticleFXEffect);
275
    procedure unregisterUser(obj: TGLParticleFXEffect);
276

277
  public
278
     
279
    constructor Create(aOwner: TComponent); override;
280
    destructor Destroy; override;
281

282
    procedure NotifyChange(Sender: TObject); override;
283
    procedure DoProgress(const progressTime: TProgressTimes); override;
284

285
    // Class of particles created by this manager. }
286
    class function ParticlesClass: TGLParticleClass; virtual;
287
    { Creates a new particle controled by the manager. }
288
    function CreateParticle: TGLParticle; virtual;
289
    { Create several particles at once. }
290
    procedure CreateParticles(nbParticles: Integer);
291

292
    { A TGLParticleList property. }
293
    property Particles: TGLParticleList read FParticles write SetParticles;
294
    { Return the number of particles.
295
       Note that subclasses may decide to return a particle count inferior
296
       to Particles.ItemCount, and the value returned by this method will
297
       be the one honoured at render time. }
298
    function ParticleCount: Integer; virtual;
299

300
    { If True the manager will free itself when its particle count reaches zero.
301
       Check happens in the progression event, use with caution and only
302
       if you know what you're doing! }
303
    property AutoFreeWhenEmpty: Boolean read FAutoFreeWhenEmpty write FAutoFreeWhenEmpty;
304

305
  published
306
     
307
          { References the renderer.
308
             The renderer takes care of ordering the particles of the manager
309
             (and other managers linked to it) and rendering them all depth-sorted. }
310
    property Renderer: TGLParticleFXRenderer read FRenderer write SetRenderer;
311
    { Event triggered after standard particle creation and initialization. }
312
    property OnCreateParticle: TPFXCreateParticleEvent read FOnCreateParticle write FOnCreateParticle;
313

314
    property Cadencer;
315
  end;
316

317
  // TGLParticleFXEffect
318
  //
319
  { Base class for linking scene objects to a particle FX manager. }
320
  TGLParticleFXEffect = class(TGLObjectPostEffect)
321
  private
322
     
323
    FManager: TGLParticleFXManager;
324
    FManagerName: string;
325
    FEffectScale: single;
326
    procedure SetEffectScale(const Value: single); // NOT persistent, temporarily used for persistence
327

328
  protected
329
     
330
    procedure SetManager(val: TGLParticleFXManager);
331

332
    procedure WriteToFiler(writer: TWriter); override;
333
    procedure ReadFromFiler(reader: TReader); override;
334

335
    procedure Loaded; override;
336

337
    procedure managerNotification(aManager: TGLParticleFXManager; Operation: TOperation);
338

339
  public
340
     
341
    constructor Create(aOwner: TGLXCollection); override;
342
    destructor Destroy; override;
343

344
  published
345
     
346
          { Reference to the Particle FX manager }
347
    property Manager: TGLParticleFXManager read FManager write SetManager;
348
    property EffectScale: single read FEffectScale write SetEffectScale;
349

350
  end;
351

352
  // PFX region rendering structures
353

354
  TParticleReference = packed record
355
    particle: TGLParticle;
356
    distance: Integer; // stores an IEEE single!
357
  end;
358
  PParticleReference = ^TParticleReference;
359
  TParticleReferenceArray = packed array[0..MaxInt shr 8-1] of TParticleReference;
360
  PParticleReferenceArray = ^TParticleReferenceArray;
361
  PFXPointerList = ^TFXPointerList;
362
  TFXPointerList = array[0..MaxInt shr 8-1] of Pointer;
363
  TPFXRegion = record
364
    count, capacity: Integer;
365
    particleRef: PParticleReferenceArray;
366
    particleOrder: PFXPointerList;
367
  end;
368
  PPFXRegion = ^TPFXRegion;
369

370
  // TPFXSortAccuracy
371
  //
372
  TPFXSortAccuracy = (saLow, saOneTenth, saOneThird, saOneHalf, saHigh);
373

374
  // TGLParticleFXRenderer
375
  //
376
  { Rendering interface for scene-wide particle FX.
377
     A renderer can take care of rendering any number of particle systems,
378
     its main task being to depth-sort the particles so that they are blended
379
     appropriately. 
380
     This object will usually be placed at the end of the scene hierarchy,
381
     just before the HUD overlays, its position, rotation etc. is of no
382
     importance and has no effect on the rendering of the particles. }
383
  TGLParticleFXRenderer = class(TGLBaseSceneObject)
384
  private
385
     
386
    FManagerList: TList;
387
    FLastSortTime: Double;
388
    FLastParticleCount: Integer;
389
    FZWrite, FZTest, FZCull: Boolean;
390
    FZSortAccuracy: TPFXSortAccuracy;
391
    FZMaxDistance: Single;
392
    FBlendingMode: TBlendingMode;
393
    FRegions: array[0..cPFXNbRegions - 1] of TPFXRegion;
394

395

396
  protected
397
     
398
    function StoreZMaxDistance: Boolean;
399

400
    { Register a manager }
401
    procedure RegisterManager(aManager: TGLParticleFXManager);
402
    { UnRegister a manager }
403
    procedure UnRegisterManager(aManager: TGLParticleFXManager);
404

405
    procedure UnRegisterAll;
406

407
  public
408
     
409
    constructor Create(aOwner: TComponent); override;
410
    destructor Destroy; override;
411

412
    procedure BuildList(var rci: TGLRenderContextInfo); override;
413

414
    { Time (in msec) spent sorting the particles for last render. }
415
    property LastSortTime: Double read FLastSortTime;
416
    { Amount of particles during the last render. }
417
    property LastParticleCount: Integer read FLastParticleCount;
418

419
  published
420

421
        { Specifies if particles should write to ZBuffer.
422
           If the PFXRenderer is the last object to be rendered in the scene,
423
           it is not necessary to write to the ZBuffer since the particles
424
           are depth-sorted. Writing to the ZBuffer has a performance penalty. }
425
    property ZWrite: Boolean read FZWrite write FZWrite default False;
426
    { Specifies if particles should write to test ZBuffer. }
427
    property ZTest: Boolean read FZTest write FZTest default True;
428
    { If true the renderer will cull particles that are behind the camera. }
429
    property ZCull: Boolean read FZCull write FZCull default True;
430
    { If true particles will be accurately sorted back to front.
431
       When false, only a rough ordering is used, which can result in
432
       visual glitches but may be faster. }
433
    property ZSortAccuracy: TPFXSortAccuracy read FZSortAccuracy write FZSortAccuracy default saHigh;
434
    { Maximum distance for rendering PFX particles.
435
       If zero, camera's DepthOfView is used. }
436
    property ZMaxDistance: Single read FZMaxDistance write FZMaxDistance stored StoreZMaxDistance;
437
    { Default blending mode for particles.
438
       "Additive" blending is the usual mode (increases brightness and
439
       saturates), "transparency" may be used for smoke or systems that
440
       opacify view, "opaque" is more rarely used.
441
       Note: specific PFX managers may override/ignore this setting. }
442
    property BlendingMode: TBlendingMode read FBlendingMode write FBlendingMode default bmAdditive;
443

444
    property Visible;
445
  end;
446

447
  // TGLSourcePFXVelocityMode
448
  //
449
  TGLSourcePFXVelocityMode = (svmAbsolute, svmRelative);
450

451
  // TGLSourcePFXPositionMode
452
  //
453
  TGLSourcePFXPositionMode = (spmAbsoluteOffset, spmRelative);
454

455
  // TGLSourcePFXDispersionMode
456
  //
457
  TGLSourcePFXDispersionMode = (sdmFast, sdmIsotropic);
458

459
  // TGLSourcePFXEffect
460
  //
461
  { Simple Particles Source. }
462
  TGLSourcePFXEffect = class(TGLParticleFXEffect)
463
  private
464
     
465
    FInitialVelocity: TGLCoordinates;
466
    FInitialPosition: TGLCoordinates;
467
    FPositionDispersionRange: TGLCoordinates;
468
    FVelocityDispersion: Single;
469
    FPositionDispersion: Single;
470
    FParticleInterval: Single;
471
    FVelocityMode: TGLSourcePFXVelocityMode;
472
    FPositionMode: TGLSourcePFXPositionMode;
473
    FDispersionMode: TGLSourcePFXDispersionMode;
474
    FEnabled: Boolean;
475
    FDisabledIfOwnerInvisible: Boolean;
476
    FTimeRemainder: Double;
477
    FRotationDispersion: Single;
478
    FSeed : Integer;
479

480
    Procedure SetSeed(aValue: Integer);
481

482
  protected
483
     
484
    procedure SetInitialVelocity(const val: TGLCoordinates);
485
    procedure SetInitialPosition(const val: TGLCoordinates);
486
    procedure SetPositionDispersionRange(const val: TGLCoordinates);
487
    procedure SetParticleInterval(const val: Single);
488
    procedure WriteToFiler(writer: TWriter); override;
489
    procedure ReadFromFiler(reader: TReader); override;
490

491
    function ParticleAbsoluteInitialPos: TAffineVector;
492
    procedure RndVector(const dispersion: TGLSourcePFXDispersionMode; var v: TAffineVector; var f: Single;dispersionRange: TGLCoordinates);
493
  public
494
     
495
    constructor Create(aOwner: TGLXCollection); override;
496
    destructor Destroy; override;
497

498
    class function FriendlyName: string; override;
499
    class function FriendlyDescription: string; override;
500

501
    procedure DoProgress(const progressTime: TProgressTimes); override;
502

503
    // Instantaneously creates nb particles
504
    procedure Burst(time: Double; nb: Integer);
505
    procedure RingExplosion(time: Double; minInitialSpeed, maxInitialSpeed: Single;  nbParticles: Integer);
506

507
  published
508
    property Seed : Integer read FSeed write SetSeed;
509
    property InitialVelocity: TGLCoordinates read FInitialVelocity write SetInitialVelocity;
510
    property VelocityDispersion: Single read FVelocityDispersion write FVelocityDispersion;
511
    property InitialPosition: TGLCoordinates read FInitialPosition write SetInitialPosition;
512
    property PositionDispersion: Single read FPositionDispersion write FPositionDispersion;
513
    property PositionDispersionRange: TGLCoordinates read FPositionDispersionRange write SetPositionDispersionRange;
514
    property ParticleInterval: Single read FParticleInterval write SetParticleInterval;
515
    property VelocityMode: TGLSourcePFXVelocityMode read FVelocityMode write FVelocityMode default svmAbsolute;
516
    property PositionMode: TGLSourcePFXPositionMode read FPositionMode write FPositionMode default spmAbsoluteOffset;
517
    property DispersionMode: TGLSourcePFXDispersionMode read FDispersionMode write FDispersionMode default sdmFast;
518
    property RotationDispersion: Single read FRotationDispersion write FRotationDispersion;
519
    property Enabled: boolean read FEnabled write FEnabled;
520
    property DisabledIfOwnerInvisible: boolean read FDisabledIfOwnerInvisible write FDisabledIfOwnerInvisible;
521
  end;
522

523
  // TGLDynamicPFXManager
524
  //
525
  { An abstract PFX manager for simple dynamic particles.
526
     Adds properties and progress implementation for handling moving particles
527
     (simple velocity and const acceleration integration). }
528
  TGLDynamicPFXManager = class(TGLParticleFXManager)
529
  private
530
     
531
    FAcceleration: TGLCoordinates;
532
    FFriction: Single;
533
    FCurrentTime: Double;
534

535
    //FRotationCenter: TAffineVector;
536

537
  protected
538
     
539
    procedure SetAcceleration(const val: TGLCoordinates);
540

541
    { Returns the maximum age for a particle.
542
       Particles older than that will be killed by DoProgress. }
543
    function MaxParticleAge: Single; dynamic; abstract;
544

545
    property CurrentTime: Double read FCurrentTime;
546

547
  public
548
     
549
    constructor Create(aOwner: TComponent); override;
550
    destructor Destroy; override;
551

552
    procedure DoProgress(const progressTime: TProgressTimes); override;
553

554
  published
555
     
556
      { Oriented acceleration applied to the particles. }
557
    property Acceleration: TGLCoordinates read FAcceleration write SetAcceleration;
558
    { Friction applied to the particles.
559
       Friction is applied as a speed scaling factor over 1 second, ie.
560
       a friction of 0.5 will half speed over 1 second, a friction of 3
561
       will triple speed over 1 second, and a friction of 1 (default
562
       value) will have no effect. }
563
    property Friction: Single read FFriction write FFriction;
564
  end;
565

566
  // TPFXLifeColor
567
  //
568
  TPFXLifeColor = class(TCollectionItem)
569
  private
570
     
571
    FColorInner: TGLColor;
572
    FColorOuter: TGLColor;
573
    FLifeTime, FInvLifeTime: Single;
574
    FIntervalRatio: Single;
575
    FSizeScale: Single;
576
    FDoScale: Boolean;
577
    FDoRotate: boolean;
578

579
    FRotateAngle: Single;
580

581
  protected
582
     
583
    function GetDisplayName: string; override;
584
    procedure SetColorInner(const val: TGLColor);
585
    procedure SetColorOuter(const val: TGLColor);
586
    procedure SetLifeTime(const val: Single);
587
    procedure SetSizeScale(const val: Single);
588
    procedure SetRotateAngle(const Value: Single); // indirectly persistent
589

590
  public
591
     
592
    constructor Create(Collection: TCollection); override;
593
    destructor Destroy; override;
594

595
    procedure Assign(Source: TPersistent); override;
596

597
    { Stores 1/LifeTime }
598
    property InvLifeTime: Single read FInvLifeTime;
599
    { Stores 1/(LifeTime[Next]-LifeTime[Self]) }
600
    property InvIntervalRatio: Single read FIntervalRatio;
601

602
  published
603
     
604
    property ColorInner: TGLColor read FColorInner write SetColorInner;
605
    property ColorOuter: TGLColor read FColorOuter write SetColorOuter;
606
    property LifeTime: Single read FLifeTime write SetLifeTime;
607
    property SizeScale: Single read FSizeScale write SetSizeScale;
608

609
    property RotateAngle: Single read FRotateAngle write SetRotateAngle;
610

611
  end;
612

613
  // TPFXLifeColors
614
  //
615
  TPFXLifeColors = class(TOwnedCollection)
616
  protected
617
     
618
    procedure SetItems(index: Integer; const val: TPFXLifeColor);
619
    function GetItems(index: Integer): TPFXLifeColor;
620

621
  public
622
     
623
    constructor Create(AOwner: TPersistent);
624

625
    function Add: TPFXLifeColor;
626
    function FindItemID(ID: Integer): TPFXLifeColor;
627
    property Items[index: Integer]: TPFXLifeColor read GetItems write SetItems; default;
628

629
    function MaxLifeTime: Double;
630
    function RotationsDefined: Boolean;
631
    function ScalingDefined: Boolean;
632
    procedure PrepareIntervalRatios;
633
  end;
634

635
  // TGLLifeColoredPFXManager
636
  //
637
  { Base PFX manager for particles with life colors.
638
     Particles have a core and edge color, for subclassing. }
639
  TGLLifeColoredPFXManager = class(TGLDynamicPFXManager)
640
  private
641
     
642
    FLifeColors: TPFXLifeColors;
643
    FLifeColorsLookup: TList;
644
    FLifeRotations: Boolean;
645
    FLifeScaling: Boolean;
646
    FColorInner: TGLColor;
647
    FColorOuter: TGLColor;
648
    FParticleSize: Single;
649

650
  protected
651
     
652
    procedure SetLifeColors(const val: TPFXLifeColors);
653
    procedure SetColorInner(const val: TGLColor);
654
    procedure SetColorOuter(const val: TGLColor);
655

656
    procedure InitializeRendering(var rci: TGLRenderContextInfo); override;
657
    procedure FinalizeRendering(var rci: TGLRenderContextInfo); override;
658

659
    function MaxParticleAge: Single; override;
660

661
    procedure ComputeColors(var lifeTime: Single; var inner, outer: TColorVector);
662
    procedure ComputeInnerColor(var lifeTime: Single; var inner: TColorVector);
663
    procedure ComputeOuterColor(var lifeTime: Single; var outer: TColorVector);
664
    function ComputeSizeScale(var lifeTime: Single; var sizeScale: Single): Boolean;
665
    function ComputeRotateAngle(var lifeTime, rotateAngle: Single): Boolean;
666

667
    procedure RotateVertexBuf(buf: TAffineVectorList; lifeTime: Single;
668
      const axis: TAffineVector; offsetAngle: Single);
669

670
  public
671
     
672
    constructor Create(aOwner: TComponent); override;
673
    destructor Destroy; override;
674

675
    property ParticleSize: Single read FParticleSize write FParticleSize;
676
    property ColorInner: TGLColor read FColorInner write SetColorInner;
677
    property ColorOuter: TGLColor read FColorOuter write SetColorOuter;
678
    property LifeColors: TPFXLifeColors read FLifeColors write SetLifeColors;
679

680
  published
681
     
682
    property BlendingMode default bmAdditive;
683
  end;
684

685
  TPFXDirectRenderEvent = procedure(Sender: TObject; aParticle: TGLParticle;
686
    var rci: TGLRenderContextInfo) of object;
687
  TPFXProgressEvent = procedure(Sender: TObject; const progressTime: TProgressTimes;
688
    var defaultProgress: Boolean) of object;
689
  TPFXParticleProgress = procedure(Sender: TObject; const progressTime: TProgressTimes;
690
    aParticle: TGLParticle; var killParticle: Boolean) of object;
691
  TPFXGetParticleCountEvent = function(Sender: TObject): Integer of object;
692

693
  // TGLCustomPFXManager
694
  //
695
  { A particles FX manager offering events for customization/experimentation.
696
     This manager essentially surfaces the PFX methods as events, and is best
697
     suited when you have specific particles that don't fall into any existing
698
     category, or when you want to experiment with particles and later plan to
699
     wrap things up in a full-blown manager. 
700
     If the events aren't handled, nothing will be rendered. }
701
  TGLCustomPFXManager = class(TGLLifeColoredPFXManager)
702
  private
703
     
704
    FOnInitializeRendering: TDirectRenderEvent;
705
    FOnBeginParticles: TDirectRenderEvent;
706
    FOnRenderParticle: TPFXDirectRenderEvent;
707
    FOnEndParticles: TDirectRenderEvent;
708
    FOnFinalizeRendering: TDirectRenderEvent;
709
    FOnProgress: TPFXProgressEvent;
710
    FOnParticleProgress: TPFXParticleProgress;
711
    FOnGetParticleCountEvent: TPFXGetParticleCountEvent;
712

713
  protected
714
     
715
    function TexturingMode: Cardinal; override;
716
    procedure InitializeRendering(var rci: TGLRenderContextInfo); override;
717
    procedure BeginParticles(var rci: TGLRenderContextInfo); override;
718
    procedure RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle); override;
719
    procedure EndParticles(var rci: TGLRenderContextInfo); override;
720
    procedure FinalizeRendering(var rci: TGLRenderContextInfo); override;
721

722
  public
723
     
724
    procedure DoProgress(const progressTime: TProgressTimes); override;
725
    function ParticleCount: Integer; override;
726

727
  published
728
     
729
    property OnInitializeRendering: TDirectRenderEvent read FOnInitializeRendering write FOnInitializeRendering;
730
    property OnBeginParticles: TDirectRenderEvent read FOnBeginParticles write FOnBeginParticles;
731
    property OnRenderParticle: TPFXDirectRenderEvent read FOnRenderParticle write FOnRenderParticle;
732
    property OnEndParticles: TDirectRenderEvent read FOnEndParticles write FOnEndParticles;
733
    property OnFinalizeRendering: TDirectRenderEvent read FOnFinalizeRendering write FOnFinalizeRendering;
734
    property OnProgress: TPFXProgressEvent read FOnProgress write FOnProgress;
735
    property OnParticleProgress: TPFXParticleProgress read FOnParticleProgress write FOnParticleProgress;
736
    property OnGetParticleCountEvent: TPFXGetParticleCountEvent read FOnGetParticleCountEvent write FOnGetParticleCountEvent;
737

738
    property ParticleSize;
739
    property ColorInner;
740
    property ColorOuter;
741
    property LifeColors;
742
  end;
743

744
  // TGLPolygonPFXManager
745
  //
746
  { Polygonal particles FX manager.
747
     The particles of this manager are made of N-face regular polygon with
748
     a core and edge color. No texturing is available. 
749
     If you render large particles and don't have T&L acceleration, consider
750
     using TGLPointLightPFXManager. }
751
  TGLPolygonPFXManager = class(TGLLifeColoredPFXManager)
752
  private
753
     
754
    FNbSides: Integer;
755
    Fvx, Fvy: TAffineVector; // NOT persistent
756
    FVertices: TAffineVectorList; // NOT persistent
757
    FVertBuf: TAffineVectorList; // NOT persistent
758

759
  protected
760
     
761
    procedure SetNbSides(const val: Integer);
762

763
    function TexturingMode: Cardinal; override;
764
    procedure InitializeRendering(var rci: TGLRenderContextInfo); override;
765
    procedure BeginParticles(var rci: TGLRenderContextInfo); override;
766
    procedure RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle); override;
767
    procedure EndParticles(var rci: TGLRenderContextInfo); override;
768
    procedure FinalizeRendering(var rci: TGLRenderContextInfo); override;
769

770
  public
771
     
772
    constructor Create(aOwner: TComponent); override;
773
    destructor Destroy; override;
774

775
  published
776
     
777
    property NbSides: Integer read FNbSides write SetNbSides default 6;
778

779
    property ParticleSize;
780
    property ColorInner;
781
    property ColorOuter;
782
    property LifeColors;
783
  end;
784

785
  // TSpriteColorMode
786
  //
787
  { Sprite color modes.
788
      
789
      scmFade: vertex coloring is used to fade inner-outer
790
      scmInner: vertex coloring uses inner color only
791
      scmOuter: vertex coloring uses outer color only
792
      scmNone: vertex coloring is NOT used (colors are ignored).
793
       }
794
  TSpriteColorMode = (scmFade, scmInner, scmOuter, scmNone);
795

796
  // TSpritesPerTexture
797
  //
798
  { Sprites per sprite texture for the SpritePFX. }
799
  TSpritesPerTexture = (sptOne, sptFour);
800

801
  // TGLBaseSpritePFXManager
802
  //
803
  { Base class for sprite-based particles FX managers.
804
     The particles are made of optionally centered single-textured quads. }
805
  TGLBaseSpritePFXManager = class(TGLLifeColoredPFXManager)
806
  private
807
     
808
    FTexHandle: TGLTextureHandle;
809
    Fvx, Fvy, Fvz: TAffineVector; // NOT persistent
810
    FVertices: TAffineVectorList; // NOT persistent
811
    FVertBuf: TAffineVectorList; // NOT persistent
812
    FAspectRatio: Single;
813
    FRotation: Single;
814
    FShareSprites: TGLBaseSpritePFXManager;
815

816
    FSpritesPerTexture: TSpritesPerTexture;
817
    FColorMode: TSpriteColorMode;
818

819
  protected
820
     
821
    { Subclasses should draw their stuff in this bmp32. }
822
    procedure PrepareImage(bmp32: TGLBitmap32; var texFormat: Integer); virtual; abstract;
823

824
    procedure BindTexture(var rci: TGLRenderContextInfo);
825
    procedure SetSpritesPerTexture(const val: TSpritesPerTexture); virtual;
826
    procedure SetColorMode(const val: TSpriteColorMode);
827
    procedure SetAspectRatio(const val: Single);
828
    function StoreAspectRatio: Boolean;
829
    procedure SetRotation(const val: Single);
830
    procedure SetShareSprites(const val: TGLBaseSpritePFXManager);
831

832
    function TexturingMode: Cardinal; override;
833
    procedure InitializeRendering(var rci: TGLRenderContextInfo); override;
834
    procedure BeginParticles(var rci: TGLRenderContextInfo); override;
835
    procedure RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle); override;
836
    procedure EndParticles(var rci: TGLRenderContextInfo); override;
837
    procedure FinalizeRendering(var rci: TGLRenderContextInfo); override;
838

839
    property SpritesPerTexture: TSpritesPerTexture read FSpritesPerTexture write SetSpritesPerTexture;
840

841
  public
842
     
843
    constructor Create(aOwner: TComponent); override;
844
    destructor Destroy; override;
845

846
    property ColorMode: TSpriteColorMode read FColorMode write SetColorMode;
847

848
  published
849
     
850
      { Ratio between width and height.
851
         An AspectRatio of 1 (default) will result in square sprite particles,
852
         values higher than one will result in horizontally stretched sprites,
853
         values below one will stretch vertically (assuming no rotation is applied). }
854
    property AspectRatio: Single read FAspectRatio write SetAspectRatio stored StoreAspectRatio;
855
    { Particle sprites rotation (in degrees).
856
       All particles of the PFX manager share this rotation. }
857
    property Rotation: Single read FRotation write SetRotation;
858
    { If specified the manager will reuse the other manager's sprites.
859
       Sharing sprites between PFX managers can help at the rendering stage
860
       if particles of the managers are mixed by helping reduce the number
861
       of texture switches. Note that only the texture is shared, not the
862
       colors, sizes or other dynamic parameters.  }
863
    property ShareSprites: TGLBaseSpritePFXManager read FShareSprites write FShareSprites;
864
  end;
865

866
  // TPFXPrepareTextureImageEvent
867
  //
868
  TPFXPrepareTextureImageEvent = procedure(Sender: TObject; destBmp32: TGLBitmap32; var texFormat: Integer) of object;
869

870
  // TGLPointLightPFXManager
871
  //
872
  { A sprite-based particles FX managers using user-specified code to prepare the texture. }
873
  TGLCustomSpritePFXManager = class(TGLBaseSpritePFXManager)
874
  private
875
     
876
    FOnPrepareTextureImage: TPFXPrepareTextureImageEvent;
877

878
  protected
879
     
880
    procedure PrepareImage(bmp32: TGLBitmap32; var texFormat: Integer); override;
881

882
  public
883
     
884
    constructor Create(aOwner: TComponent); override;
885
    destructor Destroy; override;
886

887
  published
888
     
889
      { Place your texture rendering code in this event. }
890
    property OnPrepareTextureImage: TPFXPrepareTextureImageEvent read FOnPrepareTextureImage write FOnPrepareTextureImage;
891

892
    property ColorMode default scmInner;
893
    property SpritesPerTexture default sptOne;
894
    property ParticleSize;
895
    property ColorInner;
896
    property ColorOuter;
897
    property LifeColors;
898
  end;
899

900
  // TGLPointLightPFXManager
901
  //
902
  { A sprite-based particles FX managers using point light maps.
903
     The texture map is a round, distance-based transparency map (center "opaque"),
904
     you can adjust the quality (size) of the underlying texture map with the
905
     TexMapSize property.
906
     This PFX manager renders particles similar to what you can get with
907
     TGLPolygonPFXManager but stresses fillrate more than T&L rate (and will
908
     usually be slower than the PolygonPFX when nbSides is low or T&L acceleration
909
     available). Consider this implementation as a sample for your own PFX managers
910
     that may use particles with more complex textures. }
911
  TGLPointLightPFXManager = class(TGLBaseSpritePFXManager)
912
  private
913
     
914
    FTexMapSize: Integer;
915

916
  protected
917
     
918
    procedure PrepareImage(bmp32: TGLBitmap32; var texFormat: Integer); override;
919

920
    procedure SetTexMapSize(const val: Integer);
921

922
  public
923
     
924
    constructor Create(aOwner: TComponent); override;
925
    destructor Destroy; override;
926

927
  published
928
     
929
      { Underlying texture map size, as a power of two.
930
         Min value is 3 (size=8), max value is 9 (size=512). }
931
    property TexMapSize: Integer read FTexMapSize write SetTexMapSize default 5;
932

933
    property ColorMode default scmInner;
934
    property ParticleSize;
935
    property ColorInner;
936
    property ColorOuter;
937
    property LifeColors;
938
  end;
939

940
  { Returns or creates the TGLBInertia within the given object's behaviours. }
941
function GetOrCreateSourcePFX(obj: TGLBaseSceneObject; const name: string = ''): TGLSourcePFXEffect;
942

943
// ------------------------------------------------------------------
944
// ------------------------------------------------------------------
945
// ------------------------------------------------------------------
946
implementation
947
// ------------------------------------------------------------------
948
// ------------------------------------------------------------------
949
// ------------------------------------------------------------------
950

951
// GetOrCreateSourcePFX
952
//
953

954
function GetOrCreateSourcePFX(obj: TGLBaseSceneObject; const name: string = ''): TGLSourcePFXEffect;
955
var
956
  i: Integer;
957
begin
958
  with obj.Effects do
959
  begin
960
    if name = '' then
961
    begin
962
      i := IndexOfClass(TGLSourcePFXEffect);
963
      if i >= 0 then
964
        Result := TGLSourcePFXEffect(Items[i])
965
      else
966
        Result := TGLSourcePFXEffect.Create(obj.Effects);
967
    end
968
    else
969
    begin
970
      i := IndexOfName(name);
971
      if i >= 0 then
972
        Result := (Items[i] as TGLSourcePFXEffect)
973
      else
974
      begin
975
        Result := TGLSourcePFXEffect.Create(obj.Effects);
976
        Result.Name := name;
977
      end;
978
    end;
979
  end;
980
end;
981

982
// ------------------
983
// ------------------ TGLParticle ------------------
984
// ------------------
985

986
// Create
987
//
988
constructor TGLParticle.Create;
989
begin
990
  FEffectScale := 1;
991
  inherited Create;
992
end;
993

994
// Destroy
995
//
996
destructor TGLParticle.Destroy;
997
begin
998
  inherited Destroy;
999
end;
1000

1001
function TGLParticle.GetPosition(const Index: Integer): Single;
1002
begin
1003
  Result := FPosition.V[Index];
1004
end;
1005

1006
procedure TGLParticle.WritePosition(const Index: Integer; const aValue: Single);
1007
begin
1008
  if (aValue <> FPosition.V[Index]) then
1009
    FPosition.V[Index] := aValue;
1010
end;
1011

1012
function TGLParticle.GetVelocity(const Index: Integer): Single;
1013
begin
1014
  Result := FVelocity.V[0];
1015
end;
1016

1017
procedure TGLParticle.WriteVelocity(const Index: Integer; const aValue: Single);
1018
begin
1019
  if (aValue <> FVelocity.V[Index]) then
1020
    FVelocity.V[Index] := aValue;
1021
end;
1022

1023
// WriteToFiler
1024
//
1025
procedure TGLParticle.WriteToFiler(writer: TVirtualWriter);
1026
begin
1027
  inherited WriteToFiler(writer);
1028
  with writer do
1029
  begin
1030
    WriteInteger(0); // Archive Version 0
1031
    WriteInteger(FID);
1032
    Write(FPosition, SizeOf(FPosition));
1033
    Write(FVelocity, SizeOf(FVelocity));
1034
    WriteFloat(FCreationTime);
1035
  end;
1036
end;
1037

1038

1039
// ReadFromFiler
1040
//
1041

1042
procedure TGLParticle.ReadFromFiler(reader: TVirtualReader);
1043
var
1044
  archiveVersion: integer;
1045
begin
1046
  inherited ReadFromFiler(reader);
1047
  archiveVersion := reader.ReadInteger;
1048
  if archiveVersion = 0 then
1049
    with reader do
1050
    begin
1051
      FID := ReadInteger;
1052
      Read(FPosition, SizeOf(FPosition));
1053
      Read(FVelocity, SizeOf(FVelocity));
1054
      FCreationTime := ReadFloat;
1055
    end
1056
  else
1057
    RaiseFilerException(archiveVersion);
1058
end;
1059

1060
// ------------------
1061
// ------------------ TGLParticleList ------------------
1062
// ------------------
1063

1064
// Create
1065
//
1066

1067
constructor TGLParticleList.Create;
1068
begin
1069
  inherited Create;
1070
  FItemList := TPersistentObjectList.Create;
1071
  FitemList.GrowthDelta := 64;
1072
  FDirectList := nil;
1073
end;
1074

1075
// Destroy
1076
//
1077

1078
destructor TGLParticleList.Destroy;
1079
begin
1080
  FItemList.CleanFree;
1081
  inherited Destroy;
1082
end;
1083

1084
// WriteToFiler
1085
//
1086

1087
procedure TGLParticleList.WriteToFiler(writer: TVirtualWriter);
1088
begin
1089
  inherited WriteToFiler(writer);
1090
  with writer do
1091
  begin
1092
    WriteInteger(0); // Archive Version 0
1093
    FItemList.WriteToFiler(writer);
1094
  end;
1095
end;
1096

1097
// ReadFromFiler
1098
//
1099

1100
procedure TGLParticleList.ReadFromFiler(reader: TVirtualReader);
1101
var
1102
  archiveVersion: integer;
1103
begin
1104
  inherited ReadFromFiler(reader);
1105
  archiveVersion := reader.ReadInteger;
1106
  if archiveVersion = 0 then
1107
    with reader do
1108
    begin
1109
      FItemList.ReadFromFilerWithEvent(reader, AfterItemCreated);
1110
      FDirectList := PGLParticleArray(FItemList.List);
1111
    end
1112
  else
1113
    RaiseFilerException(archiveVersion);
1114
end;
1115

1116
// GetItems
1117
//
1118

1119
function TGLParticleList.GetItems(index: Integer): TGLParticle;
1120
begin
1121
  Result := TGLParticle(FItemList[index]);
1122
end;
1123

1124
// SetItems
1125
//
1126

1127
procedure TGLParticleList.SetItems(index: Integer; val: TGLParticle);
1128
begin
1129
  FItemList[index] := val;
1130
end;
1131

1132
// AfterItemCreated
1133
//
1134

1135
procedure TGLParticleList.AfterItemCreated(Sender: TObject);
1136
begin
1137
  (Sender as TGLParticle).Manager := Self.Owner;
1138
end;
1139

1140
// ItemCount
1141
//
1142

1143
function TGLParticleList.ItemCount: Integer;
1144
begin
1145
  Result := FItemList.Count;
1146
end;
1147

1148
// AddItem
1149
//
1150

1151
function TGLParticleList.AddItem(aItem: TGLParticle): Integer;
1152
begin
1153
  aItem.Manager := Self.Owner;
1154
  Result := FItemList.Add(aItem);
1155
  FDirectList := PGLParticleArray(FItemList.List);
1156
end;
1157

1158
// RemoveAndFreeItem
1159
//
1160

1161
procedure TGLParticleList.RemoveAndFreeItem(aItem: TGLParticle);
1162
var
1163
  i: Integer;
1164
begin
1165
  i := FItemList.IndexOf(aItem);
1166
  if i >= 0 then
1167
  begin
1168
    if aItem.Manager = Self.Owner then
1169
      aItem.Manager := nil;
1170
    aItem.Free;
1171
    FItemList.List^[i] := nil;
1172
  end;
1173
end;
1174

1175
// IndexOfItem
1176
//
1177

1178
function TGLParticleList.IndexOfItem(aItem: TGLParticle): Integer;
1179
begin
1180
  Result := FItemList.IndexOf(aItem);
1181
end;
1182

1183
// Pack
1184
//
1185

1186
procedure TGLParticleList.Pack;
1187
begin
1188
  FItemList.Pack;
1189
  FDirectList := PGLParticleArray(FItemList.List);
1190
end;
1191

1192
// ------------------
1193
// ------------------ TGLParticleFXManager ------------------
1194
// ------------------
1195

1196
// Create
1197
//
1198

1199
constructor TGLParticleFXManager.Create(aOwner: TComponent);
1200
begin
1201
  inherited;
1202
  FUsers := TList.create;
1203
  FParticles := TGLParticleList.Create;
1204
  FParticles.Owner := Self;
1205
  FBlendingMode := bmAdditive;
1206
  RegisterManager(Self);
1207
end;
1208

1209
// Destroy
1210
//
1211

1212
destructor TGLParticleFXManager.Destroy;
1213
var
1214
  i: integer;
1215
begin
1216
  inherited Destroy;
1217
  for i := FUsers.Count - 1 downto 0 do
1218
    TGLParticleFXEffect(FUsers[i]).managerNotification(self, opRemove);
1219
  DeRegisterManager(Self);
1220
  Renderer := nil;
1221
  FParticles.Free;
1222
  FUsers.Free;
1223
end;
1224

1225
// NotifyChange
1226
//
1227

1228
procedure TGLParticleFXManager.NotifyChange(Sender: TObject);
1229
begin
1230
  if Assigned(FRenderer) then
1231
    Renderer.StructureChanged;
1232
end;
1233

1234
// DoProgress
1235
//
1236

1237
procedure TGLParticleFXManager.DoProgress(const progressTime: TProgressTimes);
1238
begin
1239
  inherited;
1240
  if FAutoFreeWhenEmpty and (FParticles.ItemCount = 0) then
1241
    Free;
1242
end;
1243

1244
// ParticlesClass
1245
//
1246

1247
class function TGLParticleFXManager.ParticlesClass: TGLParticleClass;
1248
begin
1249
  Result := TGLParticle;
1250
end;
1251

1252
// CreateParticle
1253
//
1254

1255
function TGLParticleFXManager.CreateParticle: TGLParticle;
1256
begin
1257
  Result := ParticlesClass.Create;
1258
  Result.FID := FNextID;
1259
  if Assigned(cadencer) then
1260
    Result.FCreationTime := Cadencer.CurrentTime;
1261
  Inc(FNextID);
1262
  FParticles.AddItem(Result);
1263
  if Assigned(FOnCreateParticle) then
1264
    FOnCreateParticle(Self, Result);
1265
end;
1266

1267
// CreateParticles
1268
//
1269

1270
procedure TGLParticleFXManager.CreateParticles(nbParticles: Integer);
1271
var
1272
  i: Integer;
1273
begin
1274
  FParticles.FItemList.RequiredCapacity(FParticles.ItemCount + nbParticles);
1275
  for i := 1 to nbParticles do
1276
    CreateParticle;
1277
end;
1278

1279
// SetRenderer
1280
//
1281

1282
procedure TGLParticleFXManager.SetRenderer(const val: TGLParticleFXRenderer);
1283
begin
1284
  if FRenderer <> val then
1285
  begin
1286
    if Assigned(FRenderer) then
1287
      FRenderer.UnRegisterManager(Self);
1288
    FRenderer := val;
1289
    if Assigned(FRenderer) then
1290
      FRenderer.RegisterManager(Self);
1291
  end;
1292
end;
1293

1294
// SetParticles
1295
//
1296

1297
procedure TGLParticleFXManager.SetParticles(const aParticles: TGLParticleList);
1298
begin
1299
  FParticles.Assign(aParticles);
1300
end;
1301

1302
// ParticleCount
1303
//
1304

1305
function TGLParticleFXManager.ParticleCount: Integer;
1306
begin
1307
  Result := FParticles.FItemList.Count;
1308
end;
1309

1310
// ApplyBlendingMode
1311
//
1312

1313
procedure TGLParticleFXManager.ApplyBlendingMode;
1314
begin
1315
  if Renderer.BlendingMode <> BlendingMode then
1316
  begin
1317
    // case disjunction to minimize OpenGL State changes
1318
    if Renderer.BlendingMode in [bmAdditive, bmTransparency] then
1319
    begin
1320
      case BlendingMode of
1321
        bmAdditive:
1322
          rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
1323
        bmTransparency:
1324
          rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
1325
      else // bmOpaque
1326
        rci.GLStates.Disable(stBlend);
1327
      end;
1328
    end
1329
    else
1330
    begin
1331
      case BlendingMode of
1332
        bmAdditive:
1333
          begin
1334
            rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
1335
            rci.GLStates.Enable(stBlend);
1336
          end;
1337
        bmTransparency:
1338
          begin
1339
            rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
1340
            rci.GLStates.Enable(stBlend);
1341
          end;
1342
      else
1343
        // bmOpaque, do nothing
1344
      end;
1345
    end;
1346
  end;
1347
end;
1348

1349
// ApplyBlendingMode
1350
//
1351

1352
procedure TGLParticleFXManager.UnapplyBlendingMode;
1353
begin
1354
  if Renderer.BlendingMode <> BlendingMode then
1355
  begin
1356
    // case disjunction to minimize OpenGL State changes
1357
    if BlendingMode in [bmAdditive, bmTransparency] then
1358
    begin
1359
      case Renderer.BlendingMode of
1360
        bmAdditive:
1361
          rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
1362
        bmTransparency:
1363
          rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
1364
      else // bmOpaque
1365
        rci.GLStates.Disable(stBlend);
1366
      end;
1367
    end
1368
    else
1369
    begin
1370
      case Renderer.BlendingMode of
1371
        bmAdditive:
1372
          begin
1373
            rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
1374
            rci.GLStates.Enable(stBlend);
1375
          end;
1376
        bmTransparency:
1377
          begin
1378
            rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
1379
            rci.GLStates.Enable(stBlend);
1380
          end;
1381
      else
1382
        // bmOpaque, do nothing
1383
      end;
1384
    end;
1385
  end;
1386
end;
1387

1388
// registerUser
1389
//
1390

1391
procedure TGLParticleFXManager.registerUser(obj: TGLParticleFXEffect);
1392
begin
1393
  if FUsers.IndexOf(obj) = -1 then
1394
    FUsers.Add(obj);
1395
end;
1396

1397
// unregisterUser
1398
//
1399

1400
procedure TGLParticleFXManager.unregisterUser(obj: TGLParticleFXEffect);
1401
begin
1402
  FUsers.Remove(obj);
1403
end;
1404

1405
// ------------------
1406
// ------------------ TGLParticleFXEffect ------------------
1407
// ------------------
1408

1409
// Create
1410
//
1411

1412
constructor TGLParticleFXEffect.Create(aOwner: TGLXCollection);
1413
begin
1414
  FEffectScale := 1;
1415
  inherited;
1416
end;
1417

1418
// Destroy
1419
//
1420

1421
destructor TGLParticleFXEffect.Destroy;
1422
begin
1423
  Manager := nil;
1424
  inherited Destroy;
1425
end;
1426

1427
// WriteToFiler
1428
//
1429

1430
procedure TGLParticleFXEffect.WriteToFiler(writer: TWriter);
1431
var
1432
  st: string;
1433
begin
1434
  with writer do
1435
  begin
1436
    // ArchiveVersion 1, added EffectScale
1437
    // ArchiveVersion 2, added inherited call
1438
    WriteInteger(2);
1439
    inherited;
1440
    if Manager <> nil then
1441
      st := Manager.GetNamePath
1442
    else
1443
      st := '';
1444
    WriteString(st);
1445
    WriteFloat(FEffectScale);
1446
  end;
1447
end;
1448

1449
// ReadFromFiler
1450
//
1451

1452
procedure TGLParticleFXEffect.ReadFromFiler(reader: TReader);
1453
var
1454
  archiveVersion: integer;
1455
begin
1456
  with reader do
1457
  begin
1458
    archiveVersion := ReadInteger;
1459
    Assert(archiveVersion in [0..2]);
1460
    if archiveVersion >= 2 then
1461
      inherited;
1462
    if archiveVersion >= 0 then
1463
    begin
1464
      FManagerName := ReadString;
1465
      Manager := nil;
1466
    end;
1467
    if archiveVersion >= 1 then
1468
    begin
1469
      FEffectScale := ReadFloat;
1470
    end;
1471
  end;
1472
end;
1473

1474
// Loaded
1475
//
1476

1477
procedure TGLParticleFXEffect.Loaded;
1478
var
1479
  mng: TComponent;
1480
begin
1481
  inherited;
1482
  if FManagerName <> '' then
1483
  begin
1484
    mng := FindManager(TGLParticleFXManager, FManagerName);
1485
    if Assigned(mng) then
1486
      Manager := TGLParticleFXManager(mng);
1487
    FManagerName := '';
1488
  end;
1489
end;
1490

1491
// SetManager
1492
//
1493

1494
procedure TGLParticleFXEffect.SetManager(val: TGLParticleFXManager);
1495
begin
1496
  if assigned(FManager) then
1497
    FManager.unregisterUser(self);
1498
  FManager := val;
1499
  if assigned(FManager) then
1500
    FManager.registerUser(self);
1501
end;
1502

1503
procedure TGLParticleFXEffect.SetEffectScale(const Value: single);
1504
begin
1505
  FEffectScale := Value;
1506
end;
1507

1508
// managerNotification
1509
//
1510

1511
procedure TGLParticleFXEffect.managerNotification(
1512
  aManager: TGLParticleFXManager; Operation: TOperation);
1513
begin
1514
  if (Operation = opRemove) and (aManager = manager) then
1515
    manager := nil;
1516
end;
1517

1518
// ------------------
1519
// ------------------ TGLParticleFXRenderer ------------------
1520
// ------------------
1521

1522
// Create
1523
//
1524

1525
constructor TGLParticleFXRenderer.Create(aOwner: TComponent);
1526
begin
1527
  inherited;
1528
  ObjectStyle := ObjectStyle + [osNoVisibilityCulling, osDirectDraw];
1529
  FZTest := True;
1530
  FZCull := True;
1531
  FZSortAccuracy := saHigh;
1532
  FManagerList := TList.Create;
1533
  FBlendingMode := bmAdditive;
1534
end;
1535

1536
// Destroy
1537
//
1538

1539
destructor TGLParticleFXRenderer.Destroy;
1540
var
1541
  i: Integer;
1542
begin
1543
  for i := 0 to cPFXNbRegions - 1 do
1544
  begin
1545
    FreeMem(FRegions[i].particleRef);
1546
    FreeMem(FRegions[i].particleOrder);
1547
  end;
1548

1549
  UnRegisterAll;
1550
  FManagerList.Free;
1551
  inherited Destroy;
1552
end;
1553

1554
// RegisterManager
1555
//
1556

1557
procedure TGLParticleFXRenderer.RegisterManager(aManager: TGLParticleFXManager);
1558
begin
1559
  FManagerList.Add(aManager);
1560
end;
1561

1562
// UnRegisterManager
1563
//
1564

1565
procedure TGLParticleFXRenderer.UnRegisterManager(aManager: TGLParticleFXManager);
1566
begin
1567
  FManagerList.Remove(aManager);
1568
end;
1569

1570
// UnRegisterAll
1571
//
1572

1573
procedure TGLParticleFXRenderer.UnRegisterAll;
1574
begin
1575
  while FManagerList.Count > 0 do
1576
    TGLParticleFXManager(FManagerList[FManagerList.Count - 1]).Renderer := nil;
1577
end;
1578

1579

1580
// BuildList
1581
// (beware, large and complex stuff below... this is the heart of the ParticleFX)
1582

1583
procedure TGLParticleFXRenderer.BuildList(var rci: TGLRenderContextInfo);
1584
{
1585
   Quick Explanation of what is below:
1586

1587
   The purpose is to depth-sort a large number (thousandths) of particles and
1588
   render them back to front. The rendering part is not particularly complex,
1589
   it just invokes the various PFX managers involved and request particle
1590
   renderings.
1591
   The sort uses a first-pass region partition (the depth range is split into
1592
   regions, and particles are assigned directly to the region they belong to),
1593
   then each region is sorted with a QuickSort.
1594
   The QuickSort itself is the regular classic variant, but the comparison is
1595
   made on singles as if they were integers, this is allowed by the IEEE format
1596
   in a very efficient manner if all values are superior to 1, which is ensured
1597
   by the distance calculation and a fixed offset of 1.
1598
}
1599
var
1600
  dist, distDelta, invRegionSize: Single;
1601
  managerIdx, particleIdx, regionIdx: Integer;
1602

1603
  procedure QuickSortRegion(startIndex, endIndex: Integer; region: PPFXRegion);
1604
  var
1605
    I, J: Integer;
1606
    P: Integer;
1607
    poptr: PPointerArray;
1608
    buf: Pointer;
1609
  begin
1610
    if endIndex - startIndex > 1 then
1611
    begin
1612
      poptr := @region^.particleOrder^[0];
1613
      repeat
1614
        I := startIndex;
1615
        J := endIndex;
1616
        P := PParticleReference(poptr^[(I + J) shr 1])^.distance;
1617
        repeat
1618
          while PParticleReference(poptr^[I])^.distance < P do
1619
            Inc(I);
1620
          while PParticleReference(poptr^[J])^.distance > P do
1621
            Dec(J);
1622
          if I <= J then
1623
          begin
1624
            buf := poptr^[I];
1625
            poptr^[I] := poptr^[J];
1626
            poptr^[J] := buf;
1627
            Inc(I);
1628
            Dec(J);
1629
          end;
1630
        until I > J;
1631
        if startIndex < J then
1632
          QuickSortRegion(startIndex, J, region);
1633
        startIndex := I;
1634
      until I >= endIndex;
1635
    end
1636
    else if endIndex - startIndex > 0 then
1637
    begin
1638
      poptr := @region^.particleOrder^[0];
1639
      if PParticleReference(poptr^[endIndex])^.distance < PParticleReference(poptr^[startIndex])^.distance then
1640
      begin
1641
        buf := poptr^[startIndex];
1642
        poptr^[startIndex] := poptr^[endIndex];
1643
        poptr^[endIndex] := buf;
1644
      end;
1645
    end;
1646
  end;
1647

1648
  procedure DistToRegionIdx; register;
1649
  begin
1650
    regionIdx := Trunc((dist - distDelta) * invRegionSize);
1651
  end;
1652

1653
var
1654
  minDist, maxDist, sortMaxRegion: Integer;
1655
  curManager: TGLParticleFXManager;
1656
  curList: PGLParticleArray;
1657
  curParticle: TGLParticle;
1658
  curRegion: PPFXRegion;
1659
  curParticleOrder: PPointerArray;
1660
  cameraPos, cameraVector: TAffineVector;
1661
  timer: Int64;
1662
  currentTexturingMode: Cardinal;
1663
begin
1664
  if csDesigning in ComponentState then Exit;
1665

1666

1667
  timer := StartPrecisionTimer;
1668
  // precalcs
1669
  PSingle(@minDist)^ := rci.rcci.nearClippingDistance + 1;
1670
  if ZMaxDistance <= 0 then
1671
  begin
1672
    PSingle(@maxDist)^ := rci.rcci.farClippingDistance + 1;
1673
    invRegionSize := (cPFXNbRegions - 2) / (rci.rcci.farClippingDistance - rci.rcci.nearClippingDistance);
1674
  end
1675
  else
1676
  begin
1677
    PSingle(@maxDist)^ := rci.rcci.nearClippingDistance + ZMaxDistance + 1;
1678
    invRegionSize := (cPFXNbRegions - 2) / ZMaxDistance;
1679
  end;
1680
  distDelta := rci.rcci.nearClippingDistance + 1 + 0.49999 / invRegionSize;
1681

1682
  SetVector(cameraPos, rci.cameraPosition);
1683
  SetVector(cameraVector, rci.cameraDirection);
1684
  try
1685
    // Collect particles
1686
    // only depth-clipping performed as of now.
1687
    FLastParticleCount := 0;
1688
    for managerIdx := 0 to FManagerList.Count - 1 do
1689
    begin
1690
      curManager := TGLParticleFXManager(FManagerList[managerIdx]);
1691
      curList := curManager.FParticles.List;
1692
      Inc(FLastParticleCount, curManager.ParticleCount);
1693
      for particleIdx := 0 to curManager.ParticleCount - 1 do
1694
      begin
1695
        curParticle := curList^[particleIdx];
1696
        dist := PointProject(curParticle.FPosition, cameraPos, cameraVector) + 1;
1697
        if not FZCull then
1698
        begin
1699
          if PInteger(@dist)^ < minDist then
1700
            PInteger(@dist)^ := minDist;
1701
        end;
1702
        if (PInteger(@dist)^ >= minDist) and (PInteger(@dist)^ <= maxDist) then
1703
        begin
1704
          DistToRegionIdx;
1705
          curRegion := @FRegions[regionIdx];
1706
          // add particle to region
1707
          if curRegion^.count = curRegion^.capacity then
1708
          begin
1709
            Inc(curRegion^.capacity, cPFXGranularity);
1710
            ReallocMem(curRegion^.particleRef, curRegion^.capacity * SizeOf(TParticleReference));
1711
            ReallocMem(curRegion^.particleOrder, curRegion^.capacity * SizeOf(Pointer));
1712
          end;
1713
          with curRegion^.particleRef^[curRegion^.count] do
1714
          begin
1715
            particle := curParticle;
1716
            distance := PInteger(@dist)^;
1717
          end;
1718
          Inc(curRegion^.count);
1719
        end;
1720
      end;
1721
    end;
1722
    // Sort regions
1723
    case ZSortAccuracy of
1724
      saLow: sortMaxRegion := 0;
1725
      saOneTenth: sortMaxRegion := cPFXNbRegions div 10;
1726
      saOneThird: sortMaxRegion := cPFXNbRegions div 3;
1727
      saOneHalf: sortMaxRegion := cPFXNbRegions div 2;
1728
    else
1729
      sortMaxRegion := cPFXNbRegions;
1730
    end;
1731
    for regionIdx := 0 to cPFXNbRegions - 1 do
1732
    begin
1733
      curRegion := @FRegions[regionIdx];
1734
      if curRegion^.count > 1 then
1735
      begin
1736
        // Prepare order table
1737
        with curRegion^ do
1738
          for particleIdx := 0 to count - 1 do
1739
            particleOrder^[particleIdx] := @particleRef[particleIdx];
1740
        // QuickSort
1741
        if (regionIdx < sortMaxRegion) and (FBlendingMode <> bmAdditive) then
1742
          QuickSortRegion(0, curRegion^.count - 1, curRegion);
1743
      end
1744
      else if curRegion^.Count = 1 then
1745
      begin
1746
        // Prepare order table
1747
        curRegion^.particleOrder^[0] := @curRegion^.particleRef[0];
1748
      end;
1749
    end;
1750
    FLastSortTime := StopPrecisionTimer(timer) * 1000;
1751

1752
    rci.PipelineTransformation.Push;
1753
    rci.PipelineTransformation.ModelMatrix := IdentityHmgMatrix;
1754

1755
    rci.GLStates.Disable(stCullFace);
1756
    rci.GLStates.ActiveTextureEnabled[ttTexture2D] := True;
1757
    currentTexturingMode := 0;
1758
    rci.GLStates.Disable(stLighting);
1759
    rci.GLStates.PolygonMode := pmFill;
1760

1761
    case FBlendingMode of
1762
      bmAdditive:
1763
        begin
1764
          rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOne);
1765
          rci.GLStates.Enable(stBlend);
1766
        end;
1767
      bmTransparency:
1768
        begin
1769
          rci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
1770
          rci.GLStates.Enable(stBlend);
1771
        end;
1772
    else
1773
      // bmOpaque, do nothing
1774
    end;
1775
    rci.GLStates.DepthFunc := cfLEqual;
1776
    if not FZWrite then
1777
    begin
1778
      rci.GLStates.DepthWriteMask := False;
1779
    end;
1780
    if not FZTest then
1781
      rci.GLStates.Disable(stDepthTest);
1782

1783
    try
1784
      // Initialize managers
1785
      for managerIdx := 0 to FManagerList.Count - 1 do
1786
        TGLParticleFXManager(FManagerList.Items[managerIdx]).InitializeRendering(rci);
1787
      // Start Rendering... at last ;)
1788
      try
1789
        curManager := nil;
1790
        for regionIdx := cPFXNbRegions - 1 downto 0 do
1791
        begin
1792
          curRegion := @FRegions[regionIdx];
1793
          if curRegion^.count > 0 then
1794
          begin
1795
            curParticleOrder := @curRegion^.particleOrder^[0];
1796
            for particleIdx := curRegion^.count - 1 downto 0 do
1797
            begin
1798
              curParticle := PParticleReference(curParticleOrder^[particleIdx])^.particle;
1799
              if curParticle.Manager <> curManager then
1800
              begin
1801
                if Assigned(curManager) then
1802
                  curManager.EndParticles(rci);
1803
                curManager := curParticle.Manager;
1804
                if curManager.TexturingMode <> currentTexturingMode then
1805
                begin
1806
                  if currentTexturingMode <> 0 then
1807
                    GL.Disable(currentTexturingMode);
1808
                  currentTexturingMode := curManager.TexturingMode;
1809
                  if currentTexturingMode <> 0 then
1810
                    GL.Enable(currentTexturingMode);
1811
                end;
1812
                curManager.BeginParticles(rci);
1813
              end;
1814
              curManager.RenderParticle(rci, curParticle);
1815
            end;
1816
          end;
1817
        end;
1818
        if Assigned(curManager) then
1819
          curManager.EndParticles(rci);
1820
      finally
1821
        // Finalize managers
1822
        for managerIdx := 0 to FManagerList.Count - 1 do
1823
          TGLParticleFXManager(FManagerList.Items[managerIdx]).FinalizeRendering(rci);
1824
      end;
1825
    finally
1826
      rci.PipelineTransformation.Pop;
1827
    end;
1828
    rci.GLStates.ActiveTextureEnabled[ttTexture2D] := False;
1829
    rci.GLStates.DepthWriteMask := True;
1830
  finally
1831
    // cleanup
1832
    for regionIdx := cPFXNbRegions - 1 downto 0 do
1833
      FRegions[regionIdx].count := 0;
1834
  end;
1835
end;
1836

1837
// StoreZMaxDistance
1838
//
1839

1840
function TGLParticleFXRenderer.StoreZMaxDistance: Boolean;
1841
begin
1842
  Result := (FZMaxDistance <> 0);
1843
end;
1844

1845
// ------------------
1846
// ------------------ TGLSourcePFXEffect ------------------
1847
// ------------------
1848

1849
// Create
1850
//
1851

1852
constructor TGLSourcePFXEffect.Create(aOwner: TGLXCollection);
1853
begin
1854
  inherited;
1855
  FInitialVelocity := TGLCoordinates.CreateInitialized(Self, NullHmgVector, csVector);
1856
  FInitialPosition := TGLCoordinates.CreateInitialized(Self, NullHmgVector, csPoint);
1857
  FPositionDispersionRange := TGLCoordinates.CreateInitialized(Self, XYZHmgVector, csPoint);
1858
  FVelocityDispersion := 0;
1859
  FPositionDispersion := 0;
1860
  FParticleInterval := 0.1;
1861
  FVelocityMode := svmAbsolute;
1862
  FPositionMode := spmAbsoluteOffset;
1863
  FDispersionMode := sdmFast;
1864
  FEnabled := true;
1865
  FDisabledIfOwnerInvisible := False;
1866
  FSeed := 2389;//GetTickCount64;
1867
  GLS_RNG.Randomize;
1868
end;
1869

1870
destructor TGLSourcePFXEffect.Destroy;
1871
begin
1872
  FPositionDispersionRange.Free;
1873
  FInitialVelocity.Free;
1874
  FInitialPosition.Free;
1875
  inherited Destroy;
1876
end;
1877

1878
class function TGLSourcePFXEffect.FriendlyName: string;
1879
begin
1880
  Result := 'PFX Source';
1881
end;
1882

1883
class function TGLSourcePFXEffect.FriendlyDescription: string;
1884
begin
1885
  Result := 'Simple Particles FX Source';
1886
end;
1887

1888
procedure TGLSourcePFXEffect.WriteToFiler(writer: TWriter);
1889
begin
1890
  inherited;
1891
  with writer do
1892
  begin
1893
    WriteInteger(6); // ArchiveVersion 6, added FPositionMode
1894
    // ArchiveVersion 5, added FDisabledIfOwnerInvisible:
1895
    // ArchiveVersion 4, added FRotationDispersion
1896
    // ArchiveVersion 3, added FEnabled
1897
    // ArchiveVersion 2, added FPositionDispersionRange
1898
    // ArchiveVersion 1, added FDispersionMode
1899
    FInitialVelocity.WriteToFiler(writer);
1900
    FInitialPosition.WriteToFiler(writer);
1901
    FPositionDispersionRange.WriteToFiler(writer);
1902
    WriteFloat(FVelocityDispersion);
1903
    WriteFloat(FPositionDispersion);
1904
    WriteFloat(FParticleInterval);
1905
    WriteInteger(Integer(FVelocityMode));
1906
    WriteInteger(Integer(FDispersionMode));
1907
    WriteBoolean(FEnabled);
1908
    WriteFloat(FRotationDispersion);
1909
    WriteBoolean(FDisabledIfOwnerInvisible);
1910
    WriteInteger(Integer(FPositionMode));
1911
  end;
1912
end;
1913

1914
procedure TGLSourcePFXEffect.ReadFromFiler(reader: TReader);
1915
var
1916
  archiveVersion: Integer;
1917
begin
1918
  inherited;
1919
  with reader do
1920
  begin
1921
    archiveVersion := ReadInteger;
1922
    Assert(archiveVersion in [0..6]);
1923
    FInitialVelocity.ReadFromFiler(reader);
1924
    FInitialPosition.ReadFromFiler(reader);
1925
    if archiveVersion >= 2 then
1926
      FPositionDispersionRange.ReadFromFiler(reader);
1927
    FVelocityDispersion := ReadFloat;
1928
    FPositionDispersion := ReadFloat;
1929
    FParticleInterval := ReadFloat;
1930
    FVelocityMode := TGLSourcePFXVelocityMode(ReadInteger);
1931
    if archiveVersion >= 1 then
1932
      FDispersionMode := TGLSourcePFXDispersionMode(ReadInteger);
1933
    if archiveVersion >= 3 then
1934
      FEnabled := ReadBoolean;
1935
    if archiveVersion >= 4 then
1936
      FRotationDispersion := ReadFloat;
1937
    if archiveVersion >= 5 then
1938
      FDisabledIfOwnerInvisible := ReadBoolean;
1939
    if archiveVersion >= 6 then
1940
      FPositionMode := TGLSourcePFXPositionMode(ReadInteger);
1941
  end;
1942
end;
1943

1944
Procedure TGLSourcePFXEffect.SetSeed(aValue: Integer);
1945
Begin
1946
  FSeed := aValue;
1947
  //StructureChanged;
1948
End;
1949

1950
procedure TGLSourcePFXEffect.RndVector(const dispersion: TGLSourcePFXDispersionMode; var v: TAffineVector; var f: Single;dispersionRange: TGLCoordinates);
1951
var
1952
  f2, fsq: Single;
1953
  p: TVector;
1954
  OldSeed : Longword;
1955

1956
begin
1957
  OldSeed := RandSeed;
1958
  RandSeed := Seed;
1959
  f2 := 2 * f;
1960
  if Assigned(dispersionRange) then
1961
    p := VectorScale(dispersionRange.DirectVector, f2)
1962
  else
1963
    p := VectorScale(XYZHmgVector, f2);
1964
  case dispersion of
1965
    sdmFast:
1966
      begin
1967
        v.V[0] := (GLS_RNG.Random - 0.5) * p.V[0];
1968
        v.V[1] := (GLS_RNG.Random - 0.5) * p.V[1];
1969
        v.V[2] := (GLS_RNG.Random - 0.5) * p.V[2];
1970
      end;
1971
  else
1972
    fsq := Sqr(0.5);
1973
    repeat
1974
      v.V[0] := (GLS_RNG.Random - 0.5);
1975
      v.V[1] := (GLS_RNG.Random - 0.5);
1976
      v.V[2] := (GLS_RNG.Random - 0.5);
1977
    until VectorNorm(v) <= fsq;
1978
    v.V[0] := v.V[0] * p.V[0];
1979
    v.V[1] := v.V[1] * p.V[1];
1980
    v.V[2] := v.V[2] * p.V[2];
1981
  end;
1982
end;
1983

1984
procedure TGLSourcePFXEffect.SetInitialVelocity(const val: TGLCoordinates);
1985
begin
1986
  FInitialVelocity.Assign(val);
1987
end;
1988

1989
// SetInitialPosition
1990
//
1991

1992
procedure TGLSourcePFXEffect.SetInitialPosition(const val: TGLCoordinates);
1993
begin
1994
  FInitialPosition.Assign(val);
1995
end;
1996

1997
// SetPositionDispersionRange
1998
//
1999

2000
procedure TGLSourcePFXEffect.SetPositionDispersionRange(const val: TGLCoordinates);
2001
begin
2002
  FPositionDispersionRange.Assign(val);
2003
end;
2004

2005
// SetParticleInterval
2006
//
2007

2008
procedure TGLSourcePFXEffect.SetParticleInterval(const val: Single);
2009
begin
2010
  if FParticleInterval <> val then
2011
  begin
2012
    FParticleInterval := val;
2013
    if FParticleInterval < 0 then
2014
      FParticleInterval := 0;
2015
    if FTimeRemainder > FParticleInterval then
2016
      FTimeRemainder := FParticleInterval;
2017
  end;
2018
end;
2019

2020
// DoProgress
2021
//
2022

2023
procedure TGLSourcePFXEffect.DoProgress(const progressTime: TProgressTimes);
2024
var
2025
  n: Integer;
2026
begin
2027
  if Enabled and Assigned(Manager) and (ParticleInterval > 0) then
2028
  begin
2029
    if OwnerBaseSceneObject.Visible or (not DisabledIfOwnerInvisible) then
2030
    begin
2031
      FTimeRemainder := FTimeRemainder + progressTime.deltaTime;
2032
      if FTimeRemainder > FParticleInterval then
2033
      begin
2034
        n := Trunc((FTimeRemainder - FParticleInterval) / FParticleInterval);
2035
        Burst(progressTime.newTime, n);
2036
        FTimeRemainder := FTimeRemainder - n * FParticleInterval;
2037
      end;
2038
    end;
2039
  end;
2040
end;
2041

2042
// ParticleAbsoluteInitialPos
2043
//
2044

2045
function TGLSourcePFXEffect.ParticleAbsoluteInitialPos: TAffineVector;
2046
begin
2047
  if PositionMode = spmRelative then
2048
  begin
2049
    Result := OwnerBaseSceneObject.LocalToAbsolute(InitialPosition.AsAffineVector);
2050
  end
2051
  else
2052
  begin
2053
    SetVector(Result, OwnerBaseSceneObject.AbsolutePosition);
2054
    AddVector(Result, InitialPosition.AsAffineVector);
2055
  end;
2056
end;
2057

2058
// Burst
2059
//
2060

2061
procedure TGLSourcePFXEffect.Burst(time: Double; nb: Integer);
2062

2063
var
2064
  particle: TGLParticle;
2065
  av, pos: TAffineVector;
2066
  OwnerObjRelPos: TAffineVector;
2067
  OldSeed : LongWord;
2068
begin
2069
  if Manager = nil then Exit;
2070
  OldSeed := RandSeed;
2071
  RandSeed := Seed;
2072

2073
  OwnerObjRelPos := OwnerBaseSceneObject.LocalToAbsolute(NullVector);
2074
  pos := ParticleAbsoluteInitialPos;
2075

2076
  //   if FManager is TGLDynamicPFXManager then
2077
  //     TGLDynamicPFXManager(FManager).FRotationCenter := pos;
2078

2079
  while nb > 0 do
2080
  begin
2081
    particle := Manager.CreateParticle;
2082
    particle.FEffectScale := FEffectScale;
2083
    RndVector(DispersionMode, av, FPositionDispersion, FPositionDispersionRange);
2084
    if VelocityMode = svmRelative then
2085
      av := VectorSubtract(OwnerBaseSceneObject.LocalToAbsolute(av), OwnerObjRelPos);
2086

2087
    ScaleVector(av, FEffectScale);
2088
    VectorAdd(pos, av, @particle.Position);
2089

2090
    RndVector(DispersionMode, av, FVelocityDispersion, nil);
2091
    VectorAdd(InitialVelocity.AsAffineVector, av, @particle.Velocity);
2092

2093
    particle.Velocity := VectorScale(particle.Velocity, FEffectScale);
2094
    if VelocityMode = svmRelative then
2095
      particle.FVelocity := VectorSubtract(OwnerBaseSceneObject.LocalToAbsolute(particle.FVelocity), OwnerObjRelPos);
2096

2097
    particle.CreationTime := time;
2098
    if FRotationDispersion <> 0 then
2099
    begin
2100
      particle.FRotation := GLS_RNG.Random * FRotationDispersion
2101
    End
2102
    else
2103
      particle.FRotation := 0;
2104
    Dec(nb);
2105
  end;
2106
end;
2107

2108
// RingExplosion
2109
//
2110

2111
procedure TGLSourcePFXEffect.RingExplosion(time: Double;
2112
  minInitialSpeed, maxInitialSpeed: Single;
2113
  nbParticles: Integer);
2114
var
2115
  particle: TGLParticle;
2116
  av, pos, tmp: TAffineVector;
2117
  ringVectorX, ringVectorY: TAffineVector;
2118
  fx, fy, d: Single;
2119
  OldSeed : LongWord;
2120
begin
2121
  if (Manager = nil) or (nbParticles <= 0) then Exit;
2122
  OldSeed := RandSeed;
2123
  RandSeed := Seed;
2124
  pos := ParticleAbsoluteInitialPos;
2125
  SetVector(ringVectorY, OwnerBaseSceneObject.AbsoluteUp);
2126
  SetVector(ringVectorX, OwnerBaseSceneObject.AbsoluteDirection);
2127
  ringVectorY := VectorCrossProduct(ringVectorX, ringVectorY);
2128
  while (nbParticles > 0) do
2129
  begin
2130
    // okay, ain't exactly an "isotropic" ring...
2131
    fx := GLS_RNG.Random - 0.5;
2132
    fy := GLS_RNG.Random - 0.5;
2133
    d := RLength(fx, fy);
2134
    tmp := VectorCombine(ringVectorX, ringVectorY, fx * d, fy * d);
2135
    ScaleVector(tmp, minInitialSpeed + GLS_RNG.Random * (maxInitialSpeed - minInitialSpeed));
2136
    AddVector(tmp, InitialVelocity.AsVector);
2137
    particle := Manager.CreateParticle;
2138
    with particle do
2139
    begin
2140
      RndVector(DispersionMode, av, FPositionDispersion, FPositionDispersionRange);
2141
      VectorAdd(pos, av, @Position);
2142
      RndVector(DispersionMode, av, FVelocityDispersion, nil);
2143
      VectorAdd(tmp, av, @Velocity);
2144
      if VelocityMode = svmRelative then
2145
        Velocity := OwnerBaseSceneObject.LocalToAbsolute(Velocity);
2146
      particle.CreationTime := time;
2147
    end;
2148
    Dec(nbParticles);
2149
  end;
2150
end;
2151

2152
// ------------------
2153
// ------------------ TPFXLifeColor ------------------
2154
// ------------------
2155

2156
// Create
2157
//
2158

2159
constructor TPFXLifeColor.Create(Collection: TCollection);
2160
begin
2161
  inherited Create(Collection);
2162
  FColorInner := TGLColor.CreateInitialized(Self, NullHmgVector);
2163
  FColorOuter := TGLColor.CreateInitialized(Self, NullHmgVector);
2164
  FLifeTime := 1;
2165
  FInvLifeTime := 1;
2166
  FSizeScale := 1;
2167
  FRotateAngle := 0;
2168
end;
2169

2170
// Destroy
2171
//
2172

2173
destructor TPFXLifeColor.Destroy;
2174
begin
2175
  FColorOuter.Free;
2176
  FColorInner.Free;
2177
  inherited Destroy;
2178
end;
2179

2180
 
2181
//
2182

2183
procedure TPFXLifeColor.Assign(Source: TPersistent);
2184
begin
2185
  if Source is TPFXLifeColor then
2186
  begin
2187
    FColorInner.Assign(TPFXLifeColor(Source).ColorInner);
2188
    FColorOuter.Assign(TPFXLifeColor(Source).ColorOuter);
2189
    FLifeTime := TPFXLifeColor(Source).LifeTime;
2190
    FRotateAngle := TPFXLifeColor(Source).RotateAngle;
2191
  end
2192
  else
2193
    inherited;
2194
end;
2195

2196
// GetDisplayName
2197
//
2198

2199
function TPFXLifeColor.GetDisplayName: string;
2200
begin
2201
  Result := Format('LifeTime %f - Inner [%.2f, %.2f, %.2f, %.2f] - Outer [%.2f, %.2f, %.2f, %.2f]',
2202
    [LifeTime,
2203
    ColorInner.Red, ColorInner.Green, ColorInner.Blue, ColorInner.Alpha,
2204
      ColorOuter.Red, ColorOuter.Green, ColorOuter.Blue, ColorOuter.Alpha]);
2205
end;
2206

2207
// SetColorInner
2208
//
2209

2210
procedure TPFXLifeColor.SetColorInner(const val: TGLColor);
2211
begin
2212
  FColorInner.Assign(val);
2213
end;
2214

2215
// SetColorOuter
2216
//
2217

2218
procedure TPFXLifeColor.SetColorOuter(const val: TGLColor);
2219
begin
2220
  FColorOuter.Assign(val);
2221
end;
2222

2223
// SetLifeTime
2224
//
2225

2226
procedure TPFXLifeColor.SetLifeTime(const val: Single);
2227
begin
2228
  if FLifeTime <> val then
2229
  begin
2230
    FLifeTime := val;
2231
    if FLifeTime <= 0 then
2232
      FLifeTime := 1e-6;
2233
    FInvLifeTime := 1 / FLifeTime;
2234
  end;
2235
end;
2236

2237
// SetSizeScale
2238
//
2239

2240
procedure TPFXLifeColor.SetSizeScale(const val: Single);
2241
begin
2242
  if FSizeScale <> val then
2243
  begin
2244
    FSizeScale := val;
2245
    FDoScale := (FSizeScale <> 1);
2246
  end;
2247
end;
2248

2249
procedure TPFXLifeColor.SetRotateAngle(const Value: Single);
2250
begin
2251
  if FRotateAngle <> Value then
2252
  begin
2253
    FRotateAngle := Value;
2254
    FDoRotate := (FRotateAngle <> 0);
2255
  end;
2256
end;
2257

2258
// ------------------
2259
// ------------------ TPFXLifeColors ------------------
2260
// ------------------
2261

2262
constructor TPFXLifeColors.Create(AOwner: TPersistent);
2263
begin
2264
  inherited Create(AOwner, TPFXLifeColor);
2265
end;
2266

2267
procedure TPFXLifeColors.SetItems(index: Integer; const val: TPFXLifeColor);
2268
begin
2269
  inherited Items[index] := val;
2270
end;
2271

2272
function TPFXLifeColors.GetItems(index: Integer): TPFXLifeColor;
2273
begin
2274
  Result := TPFXLifeColor(inherited Items[index]);
2275
end;
2276

2277
function TPFXLifeColors.Add: TPFXLifeColor;
2278
begin
2279
  Result := (inherited Add) as TPFXLifeColor;
2280
end;
2281

2282
// FindItemID
2283
//
2284

2285
function TPFXLifeColors.FindItemID(ID: Integer): TPFXLifeColor;
2286
begin
2287
  Result := (inherited FindItemID(ID)) as TPFXLifeColor;
2288
end;
2289

2290
// MaxLifeTime
2291
//
2292

2293
function TPFXLifeColors.MaxLifeTime: Double;
2294
begin
2295
  if Count > 0 then
2296
    Result := Items[Count - 1].LifeTime
2297
  else
2298
    Result := 1e30;
2299
end;
2300

2301
// RotationsDefined
2302
//
2303

2304
function TPFXLifeColors.RotationsDefined: Boolean;
2305
var
2306
  i: Integer;
2307
begin
2308
  for i := 0 to Count - 1 do
2309
  begin
2310
    if Items[i].RotateAngle <> 0 then
2311
    begin
2312
      Result := True;
2313
      Exit;
2314
    end;
2315
  end;
2316
  Result := False;
2317
end;
2318

2319
// ScalingDefined
2320
//
2321

2322
function TPFXLifeColors.ScalingDefined: Boolean;
2323
var
2324
  i: Integer;
2325
begin
2326
  for i := 0 to Count - 1 do
2327
  begin
2328
    if Items[i].SizeScale <> 1 then
2329
    begin
2330
      Result := True;
2331
      Exit;
2332
    end;
2333
  end;
2334
  Result := False;
2335
end;
2336

2337
// PrepareIntervalRatios
2338
//
2339

2340
procedure TPFXLifeColors.PrepareIntervalRatios;
2341
var
2342
  i: Integer;
2343
begin
2344
  for i := 0 to Count - 2 do
2345
    Items[i].FIntervalRatio := 1 / (Items[i + 1].LifeTime - Items[i].LifeTime);
2346
end;
2347

2348
// ------------------
2349
// ------------------ TGLDynamicPFXManager ------------------
2350
// ------------------
2351

2352
// Create
2353
//
2354

2355
constructor TGLDynamicPFXManager.Create(aOwner: TComponent);
2356
begin
2357
  inherited;
2358
  FAcceleration := TGLCoordinates.CreateInitialized(Self, NullHmgVector, csVector);
2359
  FFriction := 1;
2360
end;
2361

2362
// Destroy
2363
//
2364

2365
destructor TGLDynamicPFXManager.Destroy;
2366
begin
2367
  FAcceleration.Free;
2368
  inherited Destroy;
2369
end;
2370

2371
// DoProgress
2372
//
2373

2374
procedure TGLDynamicPFXManager.DoProgress(const progressTime: TProgressTimes);
2375
var
2376
  i: Integer;
2377
  curParticle: TGLParticle;
2378
  maxAge: Double;
2379
  {pos, pos1, axis,}accelVector: TAffineVector;
2380
  {ff,}dt: Single;
2381
  list: PGLParticleArray;
2382
  doFriction, doPack: Boolean;
2383
  frictionScale: Single;
2384
  //pos4: TVector;
2385
begin
2386
  maxAge := MaxParticleAge;
2387
  accelVector := Acceleration.AsAffineVector;
2388
  dt := progressTime.deltaTime;
2389
  doFriction := (FFriction <> 1);
2390
  if doFriction then
2391
  begin
2392
    frictionScale := Power(FFriction, dt)
2393
  end
2394
  else
2395
    frictionScale := 1;
2396
  FCurrentTime := progressTime.newTime;
2397

2398
  doPack := False;
2399
  list := Particles.List;
2400
  for i := 0 to Particles.ItemCount - 1 do
2401
  begin
2402
    curParticle := list^[i];
2403
    if (progressTime.newTime - curParticle.CreationTime) < maxAge then
2404
    begin
2405
      // particle alive, just update velocity and position
2406
      with curParticle do
2407
      begin
2408
        CombineVector(FPosition, FVelocity, dt);
2409

2410
        // DanB - this doesn't seem to fit here, rotation is already
2411
        // calculated when rendering
2412
        {if (FRotation <> 0) and (Renderer <> nil) then begin
2413
          pos := FPosition;
2414
          pos1 := FPosition;
2415
          ff := 1;
2416
          CombineVector(pos1, FVelocity, ff);
2417

2418
          SetVector(axis, Renderer.Scene.CurrentGLCamera.AbsolutePosition);
2419
          SetVector(axis, VectorSubtract(axis, FRotationCenter));
2420
          NormalizeVector(axis);
2421
          MakeVector(pos4, pos1);
2422
          pos4[0] := pos4[0] - FRotationCenter[0];
2423
          pos4[1] := pos4[1] - FRotationCenter[1];
2424
          pos4[2] := pos4[2] - FRotationCenter[2];
2425
          RotateVector(pos4, axis, FRotation * dt);
2426
          pos4[0] := pos4[0] + FRotationCenter[0];
2427
          pos4[1] := pos4[1] + FRotationCenter[1];
2428
          pos4[2] := pos4[2] + FRotationCenter[2];
2429
          MakeVector(pos1, pos4[0], pos4[1], pos4[2]);
2430

2431
          FVelocity := VectorSubtract(pos1, pos);
2432
          CombineVector(FPosition, FVelocity, dt);
2433
        end;}
2434

2435
        CombineVector(FVelocity, accelVector, dt);
2436
        if doFriction then
2437
          ScaleVector(FVelocity, frictionScale);
2438
      end;
2439
    end
2440
    else
2441
    begin
2442
      // kill particle
2443
      curParticle.Free;
2444
      list^[i] := nil;
2445
      doPack := True;
2446
    end;
2447
  end;
2448
  if doPack then
2449
    Particles.Pack;
2450
end;
2451

2452
// SetAcceleration
2453
//
2454

2455
procedure TGLDynamicPFXManager.SetAcceleration(const val: TGLCoordinates);
2456
begin
2457
  FAcceleration.Assign(val);
2458
end;
2459

2460
// ------------------
2461
// ------------------ TGLLifeColoredPFXManager ------------------
2462
// ------------------
2463

2464
// Create
2465
//
2466

2467
constructor TGLLifeColoredPFXManager.Create(aOwner: TComponent);
2468
begin
2469
  inherited;
2470
  FLifeColors := TPFXLifeColors.Create(Self);
2471
  FColorInner := TGLColor.CreateInitialized(Self, clrYellow);
2472
  FColorOuter := TGLColor.CreateInitialized(Self, NullHmgVector);
2473
  with FLifeColors.Add do
2474
  begin
2475
    LifeTime := 3;
2476
  end;
2477
  FParticleSize := 1;
2478
end;
2479

2480
// Destroy
2481
//
2482

2483
destructor TGLLifeColoredPFXManager.Destroy;
2484
begin
2485
  FLifeColors.Free;
2486
  FColorInner.Free;
2487
  FColorOuter.Free;
2488
  inherited Destroy;
2489
end;
2490

2491
// SetColorInner
2492
//
2493

2494
procedure TGLLifeColoredPFXManager.SetColorInner(const val: TGLColor);
2495
begin
2496
  FColorInner.Assign(val);
2497
end;
2498

2499
// SetColorOuter
2500
//
2501

2502
procedure TGLLifeColoredPFXManager.SetColorOuter(const val: TGLColor);
2503
begin
2504
  FColorOuter.Assign(val);
2505
end;
2506

2507
// SetLifeColors
2508
//
2509

2510
procedure TGLLifeColoredPFXManager.SetLifeColors(const val: TPFXLifeColors);
2511
begin
2512
  FLifeColors.Assign(Self);
2513
end;
2514

2515
// InitializeRendering
2516
//
2517

2518
procedure TGLLifeColoredPFXManager.InitializeRendering(var rci: TGLRenderContextInfo);
2519
var
2520
  i, n: Integer;
2521
begin
2522
  n := LifeColors.Count;
2523
  FLifeColorsLookup := TList.Create;
2524
  FLifeColorsLookup.Capacity := n;
2525
  for i := 0 to n - 1 do
2526
    FLifeColorsLookup.Add(LifeColors[i]);
2527
  FLifeRotations := LifeColors.RotationsDefined;
2528
  FLifeScaling := LifeColors.ScalingDefined;
2529
  LifeColors.PrepareIntervalRatios;
2530
end;
2531

2532
// FinalizeRendering
2533
//
2534

2535
procedure TGLLifeColoredPFXManager.FinalizeRendering(var rci: TGLRenderContextInfo);
2536
begin
2537
  FLifeColorsLookup.Free;
2538
end;
2539

2540
// MaxParticleAge
2541
//
2542

2543
function TGLLifeColoredPFXManager.MaxParticleAge: Single;
2544
begin
2545
  Result := LifeColors.MaxLifeTime;
2546
end;
2547

2548
// ComputeColors
2549
//
2550

2551
procedure TGLLifeColoredPFXManager.ComputeColors(var lifeTime: Single; var inner, outer: TColorVector);
2552
var
2553
  i, k, n: Integer;
2554
  f: Single;
2555
  lck, lck1: TPFXLifeColor;
2556
begin
2557
  with LifeColors do
2558
  begin
2559
    n := Count - 1;
2560
    if n < 0 then
2561
    begin
2562
      inner := ColorInner.Color;
2563
      outer := ColorOuter.Color;
2564
    end
2565
    else
2566
    begin
2567
      if n > 0 then
2568
      begin
2569
        k := -1;
2570
        for i := 0 to n do
2571
          if TPFXLifeColor(FLifeColorsLookup.Items[i]).LifeTime < lifeTime then
2572
            k := i;
2573
        if k < n then
2574
          Inc(k);
2575
      end
2576
      else
2577
        k := 0;
2578
      case k of
2579
        0:
2580
          begin
2581
            lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
2582
            f := lifeTime * lck.InvLifeTime;
2583
            VectorLerp(ColorInner.Color, lck.ColorInner.Color, f, inner);
2584
            VectorLerp(ColorOuter.Color, lck.ColorOuter.Color, f, outer);
2585
          end;
2586
      else
2587
        lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
2588
        lck1 := TPFXLifeColor(FLifeColorsLookup.Items[k - 1]);
2589
        f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
2590
        VectorLerp(lck1.ColorInner.Color, lck.ColorInner.Color, f, inner);
2591
        VectorLerp(lck1.ColorOuter.Color, lck.ColorOuter.Color, f, outer);
2592
      end;
2593
    end;
2594
  end;
2595
end;
2596

2597
// ComputeInnerColor
2598
//
2599

2600
procedure TGLLifeColoredPFXManager.ComputeInnerColor(var lifeTime: Single; var inner: TColorVector);
2601
var
2602
  i, k, n: Integer;
2603
  f: Single;
2604
  lck, lck1: TPFXLifeColor;
2605
  lifeColorsLookupList: PFXPointerList;
2606
begin
2607
  with LifeColors do
2608
  begin
2609
    n := Count - 1;
2610
    if n < 0 then
2611
      inner := ColorInner.Color
2612
    else
2613
    begin
2614
      lifeColorsLookupList := @FLifeColorsLookup.List[0];
2615
      if n > 0 then
2616
      begin
2617
        k := -1;
2618
        for i := 0 to n do
2619
          if TPFXLifeColor(lifeColorsLookupList^[i]).LifeTime < lifeTime then
2620
            k := i;
2621
        if k < n then
2622
          Inc(k);
2623
      end
2624
      else
2625
        k := 0;
2626
      if k = 0 then
2627
      begin
2628
        lck := TPFXLifeColor(lifeColorsLookupList^[k]);
2629
        f := lifeTime * lck.InvLifeTime;
2630
        VectorLerp(ColorInner.Color, lck.ColorInner.Color, f, inner);
2631
      end
2632
      else
2633
      begin
2634
        lck := TPFXLifeColor(lifeColorsLookupList^[k]);
2635
        lck1 := TPFXLifeColor(lifeColorsLookupList^[k - 1]);
2636
        f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
2637
        VectorLerp(lck1.ColorInner.Color, lck.ColorInner.Color, f, inner);
2638
      end;
2639
    end;
2640
  end;
2641
end;
2642

2643
// ComputeOuterColor
2644
//
2645

2646
procedure TGLLifeColoredPFXManager.ComputeOuterColor(var lifeTime: Single; var outer: TColorVector);
2647
var
2648
  i, k, n: Integer;
2649
  f: Single;
2650
  lck, lck1: TPFXLifeColor;
2651
begin
2652
  with LifeColors do
2653
  begin
2654
    n := Count - 1;
2655
    if n < 0 then
2656
      outer := ColorOuter.Color
2657
    else
2658
    begin
2659
      if n > 0 then
2660
      begin
2661
        k := -1;
2662
        for i := 0 to n do
2663
          if TPFXLifeColor(FLifeColorsLookup.Items[i]).LifeTime < lifeTime then
2664
            k := i;
2665
        if k < n then
2666
          Inc(k);
2667
      end
2668
      else
2669
        k := 0;
2670
      case k of
2671
        0:
2672
          begin
2673
            lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
2674
            f := lifeTime * lck.InvLifeTime;
2675
            VectorLerp(ColorOuter.Color, lck.ColorOuter.Color, f, outer);
2676
          end;
2677
      else
2678
        lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
2679
        lck1 := TPFXLifeColor(FLifeColorsLookup.Items[k - 1]);
2680
        f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
2681
        VectorLerp(lck1.ColorOuter.Color, lck.ColorOuter.Color, f, outer);
2682
      end;
2683
    end;
2684
  end;
2685
end;
2686

2687
// ComputeSizeScale
2688
//
2689

2690
function TGLLifeColoredPFXManager.ComputeSizeScale(var lifeTime: Single; var sizeScale: Single): Boolean;
2691
var
2692
  i, k, n: Integer;
2693
  f: Single;
2694
  lck, lck1: TPFXLifeColor;
2695
begin
2696
  with LifeColors do
2697
  begin
2698
    n := Count - 1;
2699
    if n < 0 then
2700
      Result := False
2701
    else
2702
    begin
2703
      if n > 0 then
2704
      begin
2705
        k := -1;
2706
        for i := 0 to n do
2707
          if TPFXLifeColor(FLifeColorsLookup.Items[i]).LifeTime < lifeTime then
2708
            k := i;
2709
        if k < n then
2710
          Inc(k);
2711
      end
2712
      else
2713
        k := 0;
2714
      case k of
2715
        0:
2716
          begin
2717
            lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
2718
            Result := lck.FDoScale;
2719
            if Result then
2720
            begin
2721
              f := lifeTime * lck.InvLifeTime;
2722
              sizeScale := Lerp(1, lck.SizeScale, f);
2723
            end;
2724
          end;
2725
      else
2726
        lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
2727
        lck1 := TPFXLifeColor(FLifeColorsLookup.Items[k - 1]);
2728
        Result := lck.FDoScale or lck1.FDoScale;
2729
        if Result then
2730
        begin
2731
          f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
2732
          sizeScale := Lerp(lck1.SizeScale, lck.SizeScale, f);
2733
        end;
2734
      end;
2735
    end;
2736
  end;
2737
end;
2738

2739
// ComputeRotateAngle
2740
//
2741

2742
function TGLLifeColoredPFXManager.ComputeRotateAngle(var lifeTime: Single; var rotateAngle: Single): Boolean;
2743
var
2744
  i, k, n: Integer;
2745
  f: Single;
2746
  lck, lck1: TPFXLifeColor;
2747
begin
2748
  with LifeColors do
2749
  begin
2750
    n := Count - 1;
2751
    if n < 0 then
2752
      Result := False
2753
    else
2754
    begin
2755
      if n > 0 then
2756
      begin
2757
        k := -1;
2758
        for i := 0 to n do
2759
          if Items[i].LifeTime < lifeTime then
2760
            k := i;
2761
        if k < n then
2762
          Inc(k);
2763
      end
2764
      else
2765
        k := 0;
2766
      case k of
2767
        0:
2768
          begin
2769
            lck := LifeColors[k];
2770
            Result := lck.FDoRotate;
2771
            if Result then
2772
            begin
2773
              f := lifeTime * lck.InvLifeTime;
2774
              rotateAngle := Lerp(1, lck.rotateAngle, f);
2775
            end;
2776
          end;
2777
      else
2778
        lck := LifeColors[k];
2779
        lck1 := LifeColors[k - 1];
2780
        Result := lck.FDoRotate or lck1.FDoRotate;
2781
        if Result then
2782
        begin
2783
          f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
2784
          rotateAngle := Lerp(lck1.rotateAngle, lck.rotateAngle, f);
2785
        end;
2786
      end;
2787
    end;
2788
  end;
2789
end;
2790

2791
// RotateVertexBuf
2792
//
2793

2794
procedure TGLLifeColoredPFXManager.RotateVertexBuf(buf: TAffineVectorList;
2795
  lifeTime: Single; const axis: TAffineVector; offsetAngle: Single);
2796
var
2797
  rotateAngle: Single;
2798
  rotMatrix: TMatrix;
2799
  diff: Single;
2800
  lifeRotationApplied: Boolean;
2801
begin
2802
  rotateAngle := 0;
2803
  lifeRotationApplied := ComputeRotateAngle(lifeTime, rotateAngle);
2804
  rotateAngle := rotateAngle + offsetAngle;
2805
  if lifeRotationApplied or (rotateAngle <> 0) then
2806
  begin
2807
    diff := DegToRad(rotateAngle);
2808
    rotMatrix := CreateRotationMatrix(axis, diff);
2809
    buf.TransformAsVectors(rotMatrix);
2810
  end;
2811
end;
2812

2813
// ------------------
2814
// ------------------ TGLCustomPFXManager ------------------
2815
// ------------------
2816

2817
// DoProgress
2818
//
2819

2820
procedure TGLCustomPFXManager.DoProgress(const progressTime: TProgressTimes);
2821
var
2822
  i: Integer;
2823
  list: PGLParticleArray;
2824
  curParticle: TGLParticle;
2825
  defaultProgress, killParticle, doPack: Boolean;
2826
begin
2827
  if Assigned(FOnProgress) then
2828
  begin
2829
    defaultProgress := False;
2830
    FOnProgress(Self, progressTime, defaultProgress);
2831
    if defaultProgress then
2832
      inherited;
2833
  end
2834
  else
2835
    inherited;
2836
  if Assigned(FOnParticleProgress) then
2837
  begin
2838
    doPack := False;
2839
    list := Particles.List;
2840
    for i := 0 to Particles.ItemCount - 1 do
2841
    begin
2842
      killParticle := True;
2843
      curParticle := list^[i];
2844
      FOnParticleProgress(Self, progressTime, curParticle, killParticle);
2845
      if killParticle then
2846
      begin
2847
        curParticle.Free;
2848
        list^[i] := nil;
2849
        doPack := True;
2850
      end;
2851
    end;
2852
    if doPack then
2853
      Particles.Pack;
2854
  end;
2855
end;
2856

2857
// TexturingMode
2858
//
2859

2860
function TGLCustomPFXManager.TexturingMode: Cardinal;
2861
begin
2862
  Result := 0;
2863
end;
2864

2865
// InitializeRendering
2866
//
2867

2868
procedure TGLCustomPFXManager.InitializeRendering(var rci: TGLRenderContextInfo);
2869
begin
2870
  inherited;
2871
  if Assigned(FOnInitializeRendering) then
2872
    FOnInitializeRendering(Self, rci);
2873
end;
2874

2875
// BeginParticles
2876
//
2877

2878
procedure TGLCustomPFXManager.BeginParticles(var rci: TGLRenderContextInfo);
2879
begin
2880
  if Assigned(FOnBeginParticles) then
2881
    FOnBeginParticles(Self, rci);
2882
end;
2883

2884
// RenderParticle
2885
//
2886

2887
procedure TGLCustomPFXManager.RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle);
2888
begin
2889
  if Assigned(FOnRenderParticle) then
2890
    FOnRenderParticle(Self, aParticle, rci);
2891
end;
2892

2893
// EndParticles
2894
//
2895

2896
procedure TGLCustomPFXManager.EndParticles(var rci: TGLRenderContextInfo);
2897
begin
2898
  if Assigned(FOnEndParticles) then
2899
    FOnEndParticles(Self, rci);
2900
end;
2901

2902
// FinalizeRendering
2903
//
2904

2905
procedure TGLCustomPFXManager.FinalizeRendering(var rci: TGLRenderContextInfo);
2906
begin
2907
  if Assigned(FOnFinalizeRendering) then
2908
    FOnFinalizeRendering(Self, rci);
2909
  inherited;
2910
end;
2911

2912
// ParticleCount
2913
//
2914

2915
function TGLCustomPFXManager.ParticleCount: Integer;
2916
begin
2917
  if Assigned(FOnGetParticleCountEvent) then
2918
    Result := FOnGetParticleCountEvent(Self)
2919
  else
2920
    Result := FParticles.FItemList.Count;
2921
end;
2922

2923
// ------------------
2924
// ------------------ TGLPolygonPFXManager ------------------
2925
// ------------------
2926

2927
// Create
2928
//
2929

2930
constructor TGLPolygonPFXManager.Create(aOwner: TComponent);
2931
begin
2932
  inherited;
2933
  FNbSides := 6;
2934
end;
2935

2936
// Destroy
2937
//
2938

2939
destructor TGLPolygonPFXManager.Destroy;
2940
begin
2941
  inherited Destroy;
2942
end;
2943

2944
// SetNbSides
2945
//
2946

2947
procedure TGLPolygonPFXManager.SetNbSides(const val: Integer);
2948
begin
2949
  if val <> FNbSides then
2950
  begin
2951
    FNbSides := val;
2952
    if FNbSides < 3 then
2953
      FNbSides := 3;
2954
    NotifyChange(Self);
2955
  end;
2956
end;
2957

2958
// TexturingMode
2959
//
2960

2961
function TGLPolygonPFXManager.TexturingMode: Cardinal;
2962
begin
2963
  Result := 0;
2964
end;
2965

2966
// InitializeRendering
2967
//
2968

2969
procedure TGLPolygonPFXManager.InitializeRendering(var rci: TGLRenderContextInfo);
2970
var
2971
  i: Integer;
2972
  matrix: TMatrix;
2973
  s, c: Single;
2974
begin
2975
  inherited;
2976
  GL.GetFloatv(GL_MODELVIEW_MATRIX, @matrix);
2977
  for i := 0 to 2 do
2978
  begin
2979
    Fvx.V[i] := matrix.V[i].V[0] * FParticleSize;
2980
    Fvy.V[i] := matrix.V[i].V[1] * FParticleSize;
2981
  end;
2982
  FVertices := TAffineVectorList.Create;
2983
  FVertices.Capacity := FNbSides;
2984
  for i := 0 to FNbSides - 1 do
2985
  begin
2986
    SinCos(i * c2PI / FNbSides, s, c);
2987
    FVertices.Add(VectorCombine(FVx, Fvy, c, s));
2988
  end;
2989
  FVertBuf := TAffineVectorList.Create;
2990
  FVertBuf.Count := FVertices.Count;
2991
end;
2992

2993
// BeginParticles
2994
//
2995

2996
procedure TGLPolygonPFXManager.BeginParticles(var rci: TGLRenderContextInfo);
2997
begin
2998
  ApplyBlendingMode(rci);
2999
end;
3000

3001
// RenderParticle
3002
//
3003

3004
procedure TGLPolygonPFXManager.RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle);
3005
var
3006
  i: Integer;
3007
  lifeTime, sizeScale: Single;
3008
  inner, outer: TColorVector;
3009
  pos: TAffineVector;
3010
  vertexList: PAffineVectorArray;
3011
begin
3012
  lifeTime := FCurrentTime - aParticle.CreationTime;
3013
  ComputeColors(lifeTime, inner, outer);
3014

3015
  pos := aParticle.Position;
3016

3017
  vertexList := FVertBuf.List;
3018

3019
  // copy vertices
3020
  for I := 0 to FVertBuf.Count - 1 do
3021
    vertexList[i] := FVertices[i];
3022

3023
  // rotate vertices (if needed)
3024
  if FLifeRotations or (aParticle.FRotation <> 0) then
3025
    RotateVertexBuf(FVertBuf, lifeTime, AffineVectorMake(rci.cameraDirection), aParticle.FRotation);
3026

3027
  // scale vertices (if needed) then translate to particle position
3028
  if FLifeScaling or (aParticle.FEffectScale <> 1) then
3029
  begin
3030
    if FLifeScaling and ComputeSizeScale(lifeTime, sizeScale) then
3031
      sizeScale := sizeScale * aParticle.FEffectScale
3032
    else
3033
      sizeScale := aParticle.FEffectScale;
3034

3035
    for i := 0 to FVertBuf.Count - 1 do
3036
      vertexList^[i] := VectorCombine(vertexList^[i], pos, sizeScale, 1);
3037
  end
3038
  else
3039
    FVertBuf.Translate(pos);
3040

3041
  GL.Begin_(GL_TRIANGLE_FAN);
3042
  GL.Color4fv(@inner);
3043
  GL.Vertex3fv(@pos);
3044
  GL.Color4fv(@outer);
3045
  for i := 0 to FVertBuf.Count - 1 do
3046
    GL.Vertex3fv(@vertexList[i]);
3047

3048
  GL.Vertex3fv(@vertexList[0]);
3049
  GL.End_;
3050
end;
3051

3052
// EndParticles
3053
//
3054

3055
procedure TGLPolygonPFXManager.EndParticles(var rci: TGLRenderContextInfo);
3056
begin
3057
  UnapplyBlendingMode(rci);
3058
end;
3059

3060
// FinalizeRendering
3061
//
3062

3063
procedure TGLPolygonPFXManager.FinalizeRendering(var rci: TGLRenderContextInfo);
3064
begin
3065
  FVertBuf.Free;
3066
  FVertices.Free;
3067
  inherited;
3068
end;
3069

3070
// ------------------
3071
// ------------------ TGLBaseSpritePFXManager ------------------
3072
// ------------------
3073

3074
// Create
3075
//
3076

3077
constructor TGLBaseSpritePFXManager.Create(aOwner: TComponent);
3078
begin
3079
  inherited;
3080
  FTexHandle := TGLTextureHandle.Create;
3081
  FSpritesPerTexture := sptOne;
3082
  FAspectRatio := 1;
3083
end;
3084

3085
// Destroy
3086
//
3087

3088
destructor TGLBaseSpritePFXManager.Destroy;
3089
begin
3090
  FTexHandle.Free;
3091
  FShareSprites := nil;
3092
  inherited Destroy;
3093
end;
3094

3095
// SetSpritesPerTexture
3096
//
3097

3098
procedure TGLBaseSpritePFXManager.SetSpritesPerTexture(const val: TSpritesPerTexture);
3099
begin
3100
  if val <> FSpritesPerTexture then
3101
  begin
3102
    FSpritesPerTexture := val;
3103
    FTexHandle.DestroyHandle;
3104
    NotifyChange(Self);
3105
  end;
3106
end;
3107

3108
// SetColorMode
3109
//
3110

3111
procedure TGLBaseSpritePFXManager.SetColorMode(const val: TSpriteColorMode);
3112
begin
3113
  if val <> FColorMode then
3114
  begin
3115
    FColorMode := val;
3116
    NotifyChange(Self);
3117
  end;
3118
end;
3119

3120
// SetAspectRatio
3121
//
3122

3123
procedure TGLBaseSpritePFXManager.SetAspectRatio(const val: Single);
3124
begin
3125
  if FAspectRatio <> val then
3126
  begin
3127
    FAspectRatio := ClampValue(val, 1e-3, 1e3);
3128
    NotifyChange(Self);
3129
  end;
3130
end;
3131

3132
// StoreAspectRatio
3133
//
3134

3135
function TGLBaseSpritePFXManager.StoreAspectRatio: Boolean;
3136
begin
3137
  Result := (FAspectRatio <> 1);
3138
end;
3139

3140
// SetRotation
3141
//
3142

3143
procedure TGLBaseSpritePFXManager.SetRotation(const val: Single);
3144
begin
3145
  if FRotation <> val then
3146
  begin
3147
    FRotation := val;
3148
    NotifyChange(Self);
3149
  end;
3150
end;
3151

3152
// SetShareSprites
3153
//
3154

3155
procedure TGLBaseSpritePFXManager.SetShareSprites(const val: TGLBaseSpritePFXManager);
3156
begin
3157
  if FShareSprites <> val then
3158
  begin
3159
    if Assigned(FShareSprites) then
3160
      FShareSprites.RemoveFreeNotification(Self);
3161
    FShareSprites := val;
3162
    if Assigned(FShareSprites) then
3163
      FShareSprites.FreeNotification(Self);
3164
  end;
3165
end;
3166

3167
// BindTexture
3168
//
3169

3170
procedure TGLBaseSpritePFXManager.BindTexture(var rci: TGLRenderContextInfo);
3171
var
3172
  bmp32: TGLBitmap32;
3173
  tw, th, td, tf: Integer;
3174
begin
3175
  if Assigned(FShareSprites) then
3176
    FShareSprites.BindTexture(rci)
3177
  else
3178
  begin
3179
    if FTexHandle.Handle = 0 then
3180
    begin
3181
      FTexHandle.AllocateHandle;
3182
      FTexHandle.Target := ttTexture2D;
3183
      rci.GLStates.TextureBinding[0, ttTexture2D] := FTexHandle.Handle;
3184
      GL.Hint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
3185
      rci.GLStates.UnpackAlignment := 4;
3186
      rci.GLStates.UnpackRowLength := 0;
3187
      rci.GLStates.UnpackSkipRows := 0;
3188
      rci.GLStates.UnpackSkipPixels := 0;
3189

3190
      GL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
3191
      GL.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3192

3193
      bmp32 := TGLBitmap32.Create;
3194
      try
3195
        tf := GL_RGBA;
3196
        PrepareImage(bmp32, tf);
3197
        bmp32.RegisterAsOpenGLTexture(
3198
          FTexHandle,
3199
          True,
3200
          tf, tw, th, td);
3201
      finally
3202
        bmp32.Free;
3203
      end;
3204
    end
3205
    else
3206
    begin
3207

3208
      rci.GLStates.TextureBinding[0, ttTexture2D] := FTexHandle.Handle;
3209
    end;
3210
  end;
3211
end;
3212

3213
// TexturingMode
3214
//
3215

3216
function TGLBaseSpritePFXManager.TexturingMode: Cardinal;
3217
begin
3218
  Result := GL_TEXTURE_2D;
3219
end;
3220

3221
// InitializeRendering
3222
//
3223

3224
procedure TGLBaseSpritePFXManager.InitializeRendering(var rci: TGLRenderContextInfo);
3225
var
3226
  i: Integer;
3227
  matrix: TMatrix;
3228
  s, c, w, h: Single;
3229
begin
3230
  inherited;
3231
  GL.GetFloatv(GL_MODELVIEW_MATRIX, @matrix);
3232

3233
  w := FParticleSize * Sqrt(FAspectRatio);
3234
  h := Sqr(FParticleSize) / w;
3235

3236
  for i := 0 to 2 do
3237
  begin
3238
    Fvx.V[i] := matrix.V[i].V[0] * w;
3239
    Fvy.V[i] := matrix.V[i].V[1] * h;
3240
    Fvz.V[i] := matrix.V[i].V[2];
3241
  end;
3242

3243
  FVertices := TAffineVectorList.Create;
3244
  for i := 0 to 3 do
3245
  begin
3246
    SinCos(i * cPIdiv2 + cPIdiv4, s, c);
3247
    FVertices.Add(VectorCombine(Fvx, Fvy, c, s));
3248
  end;
3249
  if FRotation <> 0 then
3250
  begin
3251
    matrix := CreateRotationMatrix(Fvz, -FRotation);
3252
    FVertices.TransformAsPoints(matrix);
3253
  end;
3254

3255
  FVertBuf := TAffineVectorList.Create;
3256
  FVertBuf.Count := FVertices.Count;
3257
end;
3258

3259
// BeginParticles
3260
//
3261

3262
procedure TGLBaseSpritePFXManager.BeginParticles(var rci: TGLRenderContextInfo);
3263
begin
3264
  BindTexture(rci);
3265
  if ColorMode = scmNone then
3266
    GL.TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
3267
  else
3268
    GL.TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
3269
  ApplyBlendingMode(rci);
3270
  if ColorMode <> scmFade then
3271
    GL.Begin_(GL_QUADS);
3272
end;
3273

3274
// RenderParticle
3275
//
3276

3277
procedure TGLBaseSpritePFXManager.RenderParticle(var rci: TGLRenderContextInfo; aParticle: TGLParticle);
3278
type
3279
  TTexCoordsSet = array[0..3] of TTexPoint;
3280
  PTexCoordsSet = ^TTexCoordsSet;
3281
const
3282
  cBaseTexCoordsSet: TTexCoordsSet = ((S: 1; T: 1), (S: 0; T: 1), (S: 0; T: 0), (S: 1; T: 0));
3283
  cTexCoordsSets: array[0..3] of TTexCoordsSet =
3284
   (((S: 1.0; T: 1.0), (S: 0.5; T: 1.0), (S: 0.5; T: 0.5), (S: 1.0; T: 0.5)),
3285
    ((S: 0.5; T: 1.0), (S: 0.0; T: 1.0), (S: 0.0; T: 0.5), (S: 0.5; T: 0.5)),
3286
    ((S: 1.0; T: 0.5), (S: 0.5; T: 0.5), (S: 0.5; T: 0.0), (S: 1.0; T: 0.0)),
3287
    ((S: 0.5; T: 0.5), (S: 0.0; T: 0.5), (S: 0.0; T: 0.0), (S: 0.5; T: 0.0)));
3288
var
3289
  lifeTime, sizeScale: Single;
3290
  inner, outer: TColorVector;
3291
  pos: TAffineVector;
3292
  vertexList: PAffineVectorArray;
3293
  i: Integer;
3294
  tcs: PTexCoordsSet;
3295
  spt: TSpritesPerTexture;
3296

3297
  procedure IssueVertices;
3298
  begin
3299
    GL.TexCoord2fv(@tcs[0]);
3300
    GL.Vertex3fv(@vertexList[0]);
3301
    GL.TexCoord2fv(@tcs[1]);
3302
    GL.Vertex3fv(@vertexList[1]);
3303
    GL.TexCoord2fv(@tcs[2]);
3304
    GL.Vertex3fv(@vertexList[2]);
3305
    GL.TexCoord2fv(@tcs[3]);
3306
    GL.Vertex3fv(@vertexList[3]);
3307
  end;
3308

3309
begin
3310
  lifeTime := FCurrentTime - aParticle.CreationTime;
3311

3312
  if Assigned(ShareSprites) then
3313
    spt := ShareSprites.SpritesPerTexture
3314
  else
3315
    spt := SpritesPerTexture;
3316
  case spt of
3317
    sptFour: tcs := @cTexCoordsSets[(aParticle.ID and 3)];
3318
  else
3319
    tcs := @cBaseTexCoordsSet;
3320
  end;
3321

3322
  pos := aParticle.Position;
3323
  vertexList := FVertBuf.List;
3324
  sizeScale := 1;
3325

3326
  // copy vertices
3327
  for i := 0 to FVertBuf.Count - 1 do
3328
    vertexList^[i] := FVertices[i];
3329

3330
  // rotate vertices (if needed)
3331
  if FLifeRotations or (aParticle.FRotation <> 0) then
3332
    RotateVertexBuf(FVertBuf, lifeTime, AffineVectorMake(rci.cameraDirection), aParticle.FRotation);
3333

3334
  // scale vertices (if needed) then translate to particle position
3335
  if FLifeScaling or (aParticle.FEffectScale <> 1) then
3336
  begin
3337
    if FLifeScaling and ComputeSizeScale(lifeTime, sizeScale) then
3338
      sizeScale := sizeScale * aParticle.FEffectScale
3339
    else
3340
      sizeScale := aParticle.FEffectScale;
3341

3342
    for i := 0 to FVertBuf.Count - 1 do
3343
      vertexList^[i] := VectorCombine(vertexList^[i], pos, sizeScale, 1);
3344
  end
3345
  else
3346
    FVertBuf.Translate(pos);
3347

3348
  case ColorMode of
3349
    scmFade:
3350
      begin
3351
        ComputeColors(lifeTime, inner, outer);
3352
        GL.Begin_(GL_TRIANGLE_FAN);
3353
        GL.Color4fv(@inner);
3354
        GL.TexCoord2f((tcs^[0].S + tcs^[2].S) * 0.5, (tcs^[0].T + tcs^[2].T) * 0.5);
3355
        GL.Vertex3fv(@pos);
3356
        GL.Color4fv(@outer);
3357
        IssueVertices;
3358
        GL.TexCoord2fv(@tcs[0]);
3359
        GL.Vertex3fv(@vertexList[0]);
3360
        GL.End_;
3361
      end;
3362
    scmInner:
3363
      begin
3364
        ComputeInnerColor(lifeTime, inner);
3365
        GL.Color4fv(@inner);
3366
        IssueVertices;
3367
      end;
3368
    scmOuter:
3369
      begin
3370
        ComputeOuterColor(lifeTime, outer);
3371
        GL.Color4fv(@outer);
3372
        IssueVertices;
3373
      end;
3374
    scmNone:
3375
      begin
3376
        IssueVertices;
3377
      end;
3378
  else
3379
    Assert(False);
3380
  end;
3381
end;
3382

3383
// EndParticles
3384
//
3385

3386
procedure TGLBaseSpritePFXManager.EndParticles(var rci: TGLRenderContextInfo);
3387
begin
3388
  if ColorMode <> scmFade then
3389
    GL.End_;
3390
  UnApplyBlendingMode(rci);
3391
end;
3392

3393
// FinalizeRendering
3394
//
3395

3396
procedure TGLBaseSpritePFXManager.FinalizeRendering(var rci: TGLRenderContextInfo);
3397
begin
3398
  FVertBuf.Free;
3399
  FVertices.Free;
3400
  inherited;
3401
end;
3402

3403
// ------------------
3404
// ------------------ TGLCustomSpritePFXManager ------------------
3405
// ------------------
3406

3407
// Create
3408
//
3409

3410
constructor TGLCustomSpritePFXManager.Create(aOwner: TComponent);
3411
begin
3412
  inherited;
3413
  FColorMode := scmInner;
3414
  FSpritesPerTexture := sptOne;
3415
end;
3416

3417
// Destroy
3418
//
3419

3420
destructor TGLCustomSpritePFXManager.Destroy;
3421
begin
3422
  inherited Destroy;
3423
end;
3424

3425
// BindTexture
3426
//
3427

3428
procedure TGLCustomSpritePFXManager.PrepareImage(bmp32: TGLBitmap32; var texFormat: Integer);
3429
begin
3430
  if Assigned(FOnPrepareTextureImage) then
3431
    FOnPrepareTextureImage(Self, bmp32, texFormat);
3432
end;
3433

3434
// ------------------
3435
// ------------------ TGLPointLightPFXManager ------------------
3436
// ------------------
3437

3438
// Create
3439
//
3440

3441
constructor TGLPointLightPFXManager.Create(aOwner: TComponent);
3442
begin
3443
  inherited;
3444
  FTexMapSize := 5;
3445
  FColorMode := scmInner;
3446
end;
3447

3448
// Destroy
3449
//
3450

3451
destructor TGLPointLightPFXManager.Destroy;
3452
begin
3453
  inherited Destroy;
3454
end;
3455

3456
// SetTexMapSize
3457
//
3458

3459
procedure TGLPointLightPFXManager.SetTexMapSize(const val: Integer);
3460
begin
3461
  if val <> FTexMapSize then
3462
  begin
3463
    FTexMapSize := val;
3464
    if FTexMapSize < 3 then
3465
      FTexMapSize := 3;
3466
    if FTexMapSize > 9 then
3467
      FTexMapSize := 9;
3468
    NotifyChange(Self);
3469
  end;
3470
end;
3471

3472
// BindTexture
3473
//
3474

3475
procedure TGLPointLightPFXManager.PrepareImage(bmp32: TGLBitmap32; var texFormat: Integer);
3476
var
3477
  s: Integer;
3478
  x, y, d, h2: Integer;
3479
  ih2, f, fy: Single;
3480
  scanLine1, scanLine2: PGLPixel32Array;
3481
begin
3482
  s := (1 shl TexMapSize);
3483
  bmp32.Width := s;
3484
  bmp32.Height := s;
3485
  bmp32.Blank := false;
3486
  texFormat := GL_LUMINANCE_ALPHA;
3487

3488
  h2 := s div 2;
3489
  ih2 := 1 / h2;
3490
  for y := 0 to h2 - 1 do
3491
  begin
3492
    fy := Sqr((y + 0.5 - h2) * ih2);
3493
    scanLine1 := bmp32.ScanLine[y];
3494
    scanLine2 := bmp32.ScanLine[s - 1 - y];
3495
    for x := 0 to h2 - 1 do
3496
    begin
3497
      f := Sqr((x + 0.5 - h2) * ih2) + fy;
3498
      if f < 1 then
3499
      begin
3500
        d := Trunc((1 - Sqrt(f)) * 256);
3501
        d := d + (d shl 8) + (d shl 16) + (d shl 24);
3502
      end
3503
      else
3504
        d := 0;
3505
      PInteger(@scanLine1[x])^ := d;
3506
      PInteger(@scanLine2[x])^ := d;
3507
      PInteger(@scanLine1[s - 1 - x])^ := d;
3508
      PInteger(@scanLine2[s - 1 - x])^ := d;
3509
    end;
3510
  end;
3511
end;
3512

3513
// ------------------------------------------------------------------
3514
// ------------------------------------------------------------------
3515
// ------------------------------------------------------------------
3516
initialization
3517
  // ------------------------------------------------------------------
3518
  // ------------------------------------------------------------------
3519
  // ------------------------------------------------------------------
3520

3521
     // class registrations
3522
  RegisterClasses([TGLParticle, TGLParticleList,
3523
    TGLParticleFXEffect, TGLParticleFXRenderer,
3524
      TGLCustomPFXManager,
3525
      TGLPolygonPFXManager,
3526
      TGLCustomSpritePFXManager,
3527
      TGLPointLightPFXManager]);
3528
  RegisterXCollectionItemClass(TGLSourcePFXEffect);
3529

3530
finalization
3531

3532
  UnregisterXCollectionItemClass(TGLSourcePFXEffect);
3533

3534
end.
3535

3536

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.