123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- //
- // The graphics engine GLScene
- //
- unit GLS.Particles;
- (* Particle systems, based on replication of full-featured scene objects. *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- System.Classes,
- System.SysUtils,
- Stage.VectorTypes,
- GLS.Scene,
- GLS.XCollection,
- GLS.PersistentClasses,
- Stage.VectorGeometry,
- Stage.OpenGLTokens,
- GLS.Context,
- GLS.Color,
- GLS.BaseClasses,
- GLS.RenderContextInfo,
- GLS.State;
- type
- TGLParticleEvent = procedure(Sender: TObject; particle: TGLBaseSceneObject) of object;
- (* Manager object of a particle system.
- Particles in a TGLParticles system are described as normal scene objects,
- however their children are to be :
- "particle template" : the first object (index=0), this one will be
- duplicated to create new particles, it does not receive progression
- events and is visible at design-time only.
- "live particle" : the other objects (index>0), this ones are rendered
- and receive progression events.
- TGLParticles may also maintain an internal, non-persistent
- ("freezed") set of objects : the allocated objects pool. Why ? Creating
- and freeing objects takes cpu-cycles, especially for the TComponent class,
- and GLScene objects are TComponent. To reduce this load (and at the expense
- of memory space), the particle systems can move "dead" particles to a pool
- instead of freeing them, and will pick in the pool instead of creating
- new objects when new particles are requested. To take advantage of this
- behaviour, you should set the ParticlePoolSize property to a non-null
- value and use the KillParticle function instead of "Free" to kill a particle.
- All direct access to a TGLParticles children should be avoided.
- For high-performance particle systems of basic particles, you should
- look into GLParticleFX instead, TGLParticles being rather focused on
- complex particles. *)
- TGLParticles = class(TGLImmaterialSceneObject)
- private
- FCubeSize: TGLFloat;
- FEdgeColor: TGLColor;
- FVisibleAtRunTime: Boolean;
- particlePool: TList;
- FParticlePoolSize: Integer;
- FOnCreateParticle: TGLParticleEvent;
- FOnActivateParticle: TGLParticleEvent;
- FOnKillParticle: TGLParticleEvent;
- FOnDestroyParticle: TGLParticleEvent;
- FOnBeforeRenderParticles, FOnAfterRenderParticles: TGLDirectRenderEvent;
- protected
- procedure SetCubeSize(const val: TGLFloat);
- procedure SetEdgeColor(const val: TGLColor);
- procedure SetVisibleAtRunTime(const val: Boolean);
- procedure SetParticlePoolSize(val: Integer);
- procedure ClearParticlePool;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var ARci: TGLRenderContextInfo); override;
- procedure DoRender(var ARci: TGLRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean); override;
- procedure DoProgress(const progressTime: TGLProgressTimes); override;
- (* Request creation of a new particle.
- Particle will be either created or retrieved from the particlePool. *)
- function CreateParticle: TGLBaseSceneObject;
- (* Kill given particle.
- If particlePool is not full, particle will be sent to the pool,
- if not, it will be freed. *)
- procedure KillParticle(aParticle: TGLBaseSceneObject);
- // Kill all particles.
- procedure KillParticles;
- published
- property CubeSize: TGLFloat read FCubeSize write SetCubeSize;
- property EdgeColor: TGLColor read FEdgeColor write SetEdgeColor;
- property VisibleAtRunTime: Boolean read FVisibleAtRunTime write SetVisibleAtRunTime default False;
- (* Size of the particle pool (for storing killed particles).
- Default size is zero, meaning the particlePool is disabled. *)
- property ParticlePoolSize: Integer read FParticlePoolSize write SetParticlePoolSize default 0;
- (* Fired a particle has been created as a template duplicate.
- When the event is triggered, the particle has yet been added to the scene. *)
- property OnCreateParticle: TGLParticleEvent read FOnCreateParticle write FOnCreateParticle;
- (* Fired when a particle will get in the "live" list.
- The particle has just been "Assigned" with the template, may happen after a creation or a pick from the particle pool. *)
- property OnActivateParticle: TGLParticleEvent read FOnActivateParticle write FOnActivateParticle;
- (* Triggered when a particle is killed.
- When the event is fired, the particle is still parented, after this event, the particle will either go
- to the pool or be destroyed if the pool is full. *)
- property OnKillParticle: TGLParticleEvent read FOnKillParticle write FOnKillParticle;
- // Triggered just before destroying a particle. The particle can be in the pool (ie. not parented)
- property OnDestroyParticle: TGLParticleEvent read FOnDestroyParticle write FOnDestroyParticle;
- // Fired before rendering the first of the particles.
- property OnBeforeRenderParticles: TGLDirectRenderEvent read FOnBeforeRenderParticles write FOnBeforeRenderParticles;
- // Fired after rendering the last of the particles.
- property OnAfterRenderParticles: TGLDirectRenderEvent read FOnAfterRenderParticles write FOnAfterRenderParticles;
- end;
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- //----------------- TGLParticles -----------------------------------------------------
-
- constructor TGLParticles.Create(AOwner: TComponent);
- begin
- inherited;
- ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
- FCubeSize := 1;
- FEdgeColor := TGLColor.Create(Self);
- FEdgeColor.Initialize(clrWhite);
- particlePool := TList.Create;
- end;
-
- destructor TGLParticles.Destroy;
- begin
- FEdgeColor.Free;
- ClearParticlePool;
- particlePool.Free;
- inherited;
- end;
- procedure TGLParticles.Assign(Source: TPersistent);
- begin
- if Source is TGLParticles then
- begin
- FCubeSize := TGLParticles(Source).FCubeSize;
- FEdgeColor.Color := TGLParticles(Source).FEdgeColor.Color;
- FVisibleAtRunTime := TGLParticles(Source).FVisibleAtRunTime;
- ClearParticlePool;
- FParticlePoolSize := TGLParticles(Source).FParticlePoolSize;
- FOnCreateParticle := TGLParticles(Source).FOnCreateParticle;
- FOnActivateParticle := TGLParticles(Source).FOnActivateParticle;
- FOnKillParticle := TGLParticles(Source).FOnKillParticle;
- FOnDestroyParticle := TGLParticles(Source).FOnDestroyParticle;
- end;
- inherited Assign(Source);
- end;
- procedure TGLParticles.ClearParticlePool;
- var
- particle: TGLBaseSceneObject;
- i: Integer;
- begin
- if Assigned(FOnDestroyParticle) then
- begin
- for i := 0 to particlePool.Count - 1 do
- begin
- particle := TGLBaseSceneObject(particlePool[i]);
- FOnDestroyParticle(Self, particle);
- particle.Free;
- end;
- end
- else
- for i := 0 to particlePool.Count - 1 do
- TGLBaseSceneObject(particlePool[i]).Free;
- particlePool.Clear;
- end;
- procedure TGLParticles.BuildList(var ARci: TGLRenderContextInfo);
- var
- mi, ma: Single;
- begin
- ARci.GLStates.Disable(stLighting);
- ARci.GLStates.Enable(stLineStipple);
- ARci.GLStates.Enable(stLineSmooth);
- ARci.GLStates.Enable(stBlend);
- ARci.GLStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
- ARci.GLStates.LineWidth := 1;
- ARci.GLStates.LineStippleFactor := 1;
- ARci.GLStates.LineStipplePattern := $AAAA;
- ma := FCubeSize * 0.5;
- mi := -ma;
- with EdgeColor do
- gl.Color3f(Color.X, Color.Y, Color.Z);
- gl.Begin_(GL_LINE_STRIP);
- // front face
- gl.Vertex3f(ma, mi, mi);
- gl.Vertex3f(ma, ma, mi);
- gl.Vertex3f(ma, ma, ma);
- gl.Vertex3f(ma, mi, ma);
- gl.Vertex3f(ma, mi, mi);
- // partial up back fac
- gl.Vertex3f(mi, mi, mi);
- gl.Vertex3f(mi, mi, ma);
- gl.Vertex3f(mi, ma, ma);
- gl.Vertex3f(mi, ma, mi);
- // right side low
- gl.Vertex3f(ma, ma, mi);
- gl.End_;
- gl.Begin_(GL_LINES);
- // right high
- gl.Vertex3f(ma, ma, ma);
- gl.Vertex3f(mi, ma, ma);
- // back low
- gl.Vertex3f(mi, mi, mi);
- gl.Vertex3f(mi, ma, mi);
- // left high
- gl.Vertex3f(ma, mi, ma);
- gl.Vertex3f(mi, mi, ma);
- gl.End_;
- end;
- procedure TGLParticles.DoRender(var ARci: TGLRenderContextInfo;
- ARenderSelf, ARenderChildren: Boolean);
- begin
- if (csDesigning in ComponentState) or (FVisibleAtRunTime) then
- BuildList(ARci);
- if Assigned(FOnBeforeRenderParticles) then
- FOnBeforeRenderParticles(Self, ARci);
- if csDesigning in ComponentState then
- begin
- // design-time, everything is visible for user convenience
- if Count > 0 then
- Self.RenderChildren(0, Count - 1, ARci);
- end
- else
- begin
- // run-time, template is NOT visible
- if Count > 1 then
- Self.RenderChildren(1, Count - 1, ARci);
- end;
- if Assigned(FOnAfterRenderParticles) then
- FOnAfterRenderParticles(Self, ARci);
- end;
- procedure TGLParticles.DoProgress(const progressTime: TGLProgressTimes);
- var
- i: Integer;
- begin
- for i := Count - 1 downto 1 do
- Children[i].DoProgress(progressTime);
- Behaviours.DoProgress(progressTime);
- if Assigned(OnProgress) then
- with progressTime do
- OnProgress(Self, deltaTime, newTime);
- end;
- procedure TGLParticles.SetCubeSize(const val: TGLFloat);
- begin
- if val <> FCubeSize then
- begin
- FCubeSize := val;
- StructureChanged;
- end;
- end;
- procedure TGLParticles.SetEdgeColor(const val: TGLColor);
- begin
- if val <> FEdgeColor then
- begin
- FEdgeColor.Assign(val);
- StructureChanged;
- end;
- end;
- procedure TGLParticles.SetVisibleAtRunTime(const val: Boolean);
- begin
- if val <> FVisibleAtRunTime then
- begin
- FVisibleAtRunTime := val;
- StructureChanged;
- end;
- end;
- procedure TGLParticles.SetParticlePoolSize(val: Integer);
- var
- particle: TGLBaseSceneObject;
- begin
- if val < 0 then
- val := 0;
- if FParticlePoolSize <> val then
- begin
- FParticlePoolSize := val;
- with particlePool do
- while Count > val do
- begin
- particle := TGLBaseSceneObject(Items[Count - 1]);
- if Assigned(FOnDestroyParticle) then
- FOnDestroyParticle(Self, particle);
- particle.Free;
- Delete(Count - 1);
- end;
- end;
- end;
- function TGLParticles.CreateParticle: TGLBaseSceneObject;
- begin
- if Count > 0 then
- begin
- if particlePool.Count > 0 then
- begin
- Result := TGLBaseSceneObject(particlePool[particlePool.Count - 1]);
- particlePool.Delete(particlePool.Count - 1);
- Result.Assign(Children[0]);
- end
- else
- begin
- Result := TGLSceneObjectClass(Children[0].ClassType).Create(Self);
- Result.Assign(Children[0]);
- if Assigned(FOnCreateParticle) then
- FOnCreateParticle(Self, Result);
- end;
- AddChild(Result);
- if Assigned(FOnActivateParticle) then
- FOnActivateParticle(Self, Result);
- end
- else
- Result := nil;
- end;
- procedure TGLParticles.KillParticle(aParticle: TGLBaseSceneObject);
- begin
- Assert(aParticle.Parent = Self, 'KillParticle : particle is not mine !');
- if Assigned(FOnKillParticle) then
- FOnKillParticle(Self, aParticle);
- if particlePool.Count < FParticlePoolSize then
- begin
- Remove(aParticle, False);
- particlePool.Add(aParticle)
- end
- else
- begin
- if Assigned(FOnDestroyParticle) then
- FOnDestroyParticle(Self, aParticle);
- aParticle.Free;
- end;
- end;
- procedure TGLParticles.KillParticles;
- begin
- while Count > 1 do
- KillParticle(Children[Count - 1]);
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterClass(TGLParticles);
- end.
|