GXS.ParticleFX.pas 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.ParticleFX;
  5. (*
  6. Base classes for scene-wide blended particles FX.
  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. interface
  12. {$I Stage.Defines.inc}
  13. uses
  14. Winapi.OpenGL,
  15. System.Classes,
  16. System.SysUtils,
  17. System.Types,
  18. System.Math,
  19. Stage.VectorTypes,
  20. Stage.VectorGeometry,
  21. Stage.Utils,
  22. GXS.PersistentClasses,
  23. GXS.VectorLists,
  24. Stage.Manager,
  25. GXS.Scene,
  26. GXS.ImageUtils,
  27. GXS.State,
  28. GXS.Material,
  29. GXS.Cadencer,
  30. GXS.Graphics,
  31. GXS.Context,
  32. GXS.Color,
  33. GXS.BaseClasses,
  34. GXS.Coordinates,
  35. GXS.RenderContextInfo,
  36. Stage.PipelineTransform,
  37. GXS.XCollection,
  38. Stage.TextureFormat;
  39. const
  40. cPFXNbRegions = 128; // number of distance regions
  41. cPFXGranularity = 128; // granularity of particles per region
  42. type
  43. TgxParticleList = class;
  44. TgxParticleFXManager = class;
  45. TgxParticleFXEffect = class;
  46. (* Base class for particles.
  47. The class implements properties for position, velocity and time, whatever
  48. you need in excess of that will have to be placed in subclasses (this
  49. class should remain as compact as possible). *)
  50. TgxParticle = class(TgxPersistentObject)
  51. private
  52. FID, FTag: Integer;
  53. FManager: TgxParticleFXManager; // NOT persistent
  54. FPosition: TAffineVector;
  55. FVelocity: TAffineVector;
  56. FRotation: Single;
  57. FCreationTime: Double;
  58. FEffectScale: Single;
  59. function GetPosition(const Index: Integer): Single;
  60. procedure WritePosition(const Index: Integer; const aValue: Single);
  61. function GetVelocity(const Index: Integer): Single;
  62. procedure WriteVelocity(const Index: Integer; const aValue: Single);
  63. public
  64. constructor Create; override;
  65. destructor Destroy; override;
  66. procedure WriteToFiler(writer: TgxVirtualWriter); override;
  67. procedure ReadFromFiler(reader: TgxVirtualReader); override;
  68. property Manager: TgxParticleFXManager read FManager write FManager;
  69. (* Particle's ID, given at birth.
  70. ID is a value unique per manager. *)
  71. property ID: Integer read FID;
  72. (* Particle's absolute position.
  73. Note that this property is read-accessed directly at rendering time
  74. in the innards of the depth-sorting code. *)
  75. property Position: TAffineVector read FPosition write FPosition;
  76. (* Particle's velocity.
  77. This velocity is indicative and is NOT automatically applied
  78. to the position during progression events by this class (subclasses
  79. may implement that). *)
  80. property Velocity: TAffineVector read FVelocity write FVelocity;
  81. // Time at which particle was created
  82. property CreationTime: Double read FCreationTime write FCreationTime;
  83. property PosX : Single index 0 read GetPosition write WritePosition;
  84. property PosY : Single index 1 read GetPosition write WritePosition;
  85. property PosZ : Single index 2 read GetPosition write WritePosition;
  86. property VelX : Single index 0 read GetVelocity write WriteVelocity;
  87. property VelY : Single index 1 read GetVelocity write WriteVelocity;
  88. property VelZ : Single index 2 read GetVelocity write WriteVelocity;
  89. property Tag: Integer read FTag write FTag;
  90. end;
  91. TgxParticleClass = class of TgxParticle;
  92. TgxParticleArray = array[0..MaxInt shr 4] of TgxParticle;
  93. PGLParticleArray = ^TgxParticleArray;
  94. (* List of particles.
  95. This list is managed with particles and performance in mind, make sure to
  96. check methods doc. *)
  97. TgxParticleList = class(TgxPersistentObject)
  98. private
  99. FOwner: TgxParticleFXManager; // NOT persistent
  100. FItemList: TgxPersistentObjectList;
  101. FDirectList: PGLParticleArray; // NOT persistent
  102. protected
  103. function GetItems(index: Integer): TgxParticle;
  104. procedure SetItems(index: Integer; val: TgxParticle);
  105. procedure AfterItemCreated(Sender: TObject);
  106. public
  107. constructor Create; override;
  108. destructor Destroy; override;
  109. procedure WriteToFiler(writer: TgxVirtualWriter); override;
  110. procedure ReadFromFiler(reader: TgxVirtualReader); override;
  111. // Refers owner manager
  112. property Owner: TgxParticleFXManager read FOwner write FOwner;
  113. property Items[index: Integer]: TgxParticle read GetItems write SetItems; default;
  114. function ItemCount: Integer;
  115. (* Adds a particle to the list.
  116. Particle owneship is defined blindly, if the particle was previously
  117. in another list, it won't be automatically removed from that list. *)
  118. function AddItem(aItem: TgxParticle): Integer;
  119. (* Removes and frees a particular item for the list.
  120. If the item is not part of the list, nothing is done.
  121. If found in the list, the item's "slot" is set to nil and item is
  122. freed (after setting its ownership to nil). The nils can be removed
  123. with a call to Pack. *)
  124. procedure RemoveAndFreeItem(aItem: TgxParticle);
  125. function IndexOfItem(aItem: TgxParticle): Integer;
  126. (* Packs the list by removing all "nil" items.
  127. Note: this functions is orders of magnitude faster than the TList
  128. version. *)
  129. procedure Pack;
  130. property List: PGLParticleArray read FDirectList;
  131. end;
  132. TgxParticleFXRenderer = class;
  133. TPFXCreateParticleEvent = procedure(Sender: TObject; aParticle: TgxParticle) of object;
  134. (* Base class for particle FX managers.
  135. Managers take care of life and death of particles for a particular
  136. particles FX system. You can have multiple scene-wide particle
  137. FX managers in a scene, handled by the same ParticleFxRenderer.
  138. Before subclassing, make sure you understood how the Initialize/Finalize
  139. Rendering, Begin/End Particles and RenderParticles methods (and also
  140. understood that rendering of manager's particles may be interwoven). *)
  141. TgxParticleFXManager = class(TgxCadencedComponent)
  142. private
  143. FBlendingMode: TgxBlendingMode;
  144. FRenderer: TgxParticleFXRenderer;
  145. FParticles: TgxParticleList;
  146. FNextID: Integer;
  147. FOnCreateParticle: TPFXCreateParticleEvent;
  148. FAutoFreeWhenEmpty: Boolean;
  149. FUsers: TList; //list of objects that use this manager
  150. protected
  151. procedure SetRenderer(const val: TgxParticleFXRenderer);
  152. procedure SetParticles(const aParticles: TgxParticleList);
  153. (* Texturing mode for the particles.
  154. Subclasses should return GL_TEXTURE_1D, 2D or 3D depending on their
  155. needs, and zero if they don't use texturing. This method is used
  156. to reduce the number of texturing activations/deactivations. *)
  157. function TexturingMode: Cardinal; virtual; abstract;
  158. (* Invoked when the particles of the manager will be rendered.
  159. This method is fired with the "base" OpenGL states and matrices
  160. that will be used throughout the whole rendering, per-frame
  161. initialization should take place here.
  162. OpenGL states/matrices should not be altered in any way here. *)
  163. procedure InitializeRendering(var rci: TgxRenderContextInfo); virtual; abstract;
  164. (* Triggered just before rendering a set of particles.
  165. The current OpenGL state should be assumed to be the "base" one as
  166. was found during InitializeRendering. Manager-specific states should
  167. be established here.
  168. Multiple BeginParticles can occur during a render (but all will be
  169. between InitializeRendering and Finalizerendering, and at least one
  170. particle will be rendered before EndParticles is invoked). *)
  171. procedure BeginParticles(var rci: TgxRenderContextInfo); virtual; abstract;
  172. (* Request to render a particular particle.
  173. Due to the nature of the rendering, no particular order should be
  174. assumed. If possible, no OpenGL state changes should be made in this
  175. method, but should be placed in Begin/EndParticles. *)
  176. procedure RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle); virtual; abstract;
  177. (* Triggered after a set of particles as been rendered.
  178. If OpenGL state were altered directly (ie. not through the states
  179. caches of GLMisc), it should be restored back to the "base" state. *)
  180. procedure EndParticles(var rci: TgxRenderContextInfo); virtual; abstract;
  181. // Invoked when rendering of particles for this manager is done.
  182. procedure FinalizeRendering(var rci: TgxRenderContextInfo); virtual; abstract;
  183. // ID for the next created particle.
  184. property NextID: Integer read FNextID write FNextID;
  185. (* Blending mode for the particles.
  186. Protected and unused in the base class. *)
  187. property BlendingMode: TgxBlendingMode read FBlendingMode write FBlendingMode;
  188. // Apply BlendingMode relatively to the renderer's blending mode.
  189. procedure ApplyBlendingMode(var rci: TgxRenderContextInfo);
  190. // Unapply BlendingMode relatively by restoring the renderer's blending mode.
  191. procedure UnapplyBlendingMode(var rci: TgxRenderContextInfo);
  192. procedure registerUser(obj: TgxParticleFXEffect);
  193. procedure unregisterUser(obj: TgxParticleFXEffect);
  194. public
  195. constructor Create(aOwner: TComponent); override;
  196. destructor Destroy; override;
  197. procedure NotifyChange(Sender: TObject); override;
  198. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  199. // Class of particles created by this manager. }
  200. class function ParticlesClass: TgxParticleClass; virtual;
  201. // Creates a new particle controlled by the manager.
  202. function CreateParticle: TgxParticle; virtual;
  203. // Create several particles at once.
  204. procedure CreateParticles(nbParticles: Integer);
  205. // A TgxParticleList property.
  206. property Particles: TgxParticleList read FParticles write SetParticles;
  207. (* Return the number of particles.
  208. Note that subclasses may decide to return a particle count inferior
  209. to Particles.ItemCount, and the value returned by this method will
  210. be the one honoured at render time. *)
  211. function ParticleCount: Integer; virtual;
  212. (* If True the manager will free itself when its particle count reaches zero.
  213. Check happens in the progression event, use with caution and only
  214. if you know what you're doing! *)
  215. property AutoFreeWhenEmpty: Boolean read FAutoFreeWhenEmpty write FAutoFreeWhenEmpty;
  216. published
  217. (* References the renderer.
  218. The renderer takes care of ordering the particles of the manager
  219. (and other managers linked to it) and rendering them all depth-sorted. *)
  220. property Renderer: TgxParticleFXRenderer read FRenderer write SetRenderer;
  221. // Event triggered after standard particle creation and initialization.
  222. property OnCreateParticle: TPFXCreateParticleEvent read FOnCreateParticle write FOnCreateParticle;
  223. property Cadencer;
  224. end;
  225. // Base class for linking scene objects to a particle FX manager.
  226. TgxParticleFXEffect = class(TgxObjectPostEffect)
  227. private
  228. FManager: TgxParticleFXManager;
  229. FManagerName: string;
  230. FEffectScale: single;
  231. procedure SetEffectScale(const Value: single); // NOT persistent, temporarily used for persistence
  232. protected
  233. procedure SetManager(val: TgxParticleFXManager);
  234. procedure WriteToFiler(writer: TWriter); override;
  235. procedure ReadFromFiler(reader: TReader); override;
  236. procedure Loaded; override;
  237. procedure managerNotification(aManager: TgxParticleFXManager; Operation: TOperation);
  238. public
  239. constructor Create(aOwner: TXCollection); override;
  240. destructor Destroy; override;
  241. published
  242. // Reference to the Particle FX manager
  243. property Manager: TgxParticleFXManager read FManager write SetManager;
  244. property EffectScale: single read FEffectScale write SetEffectScale;
  245. end;
  246. // PFX region rendering structures
  247. TParticleReference = packed record
  248. particle: TgxParticle;
  249. distance: Integer; // stores an IEEE single!
  250. end;
  251. PParticleReference = ^TParticleReference;
  252. TParticleReferenceArray = packed array[0..MaxInt shr 8-1] of TParticleReference;
  253. PParticleReferenceArray = ^TParticleReferenceArray;
  254. PFXPointerList = ^TFXPointerList;
  255. TFXPointerList = array[0..MaxInt shr 8-1] of Pointer;
  256. TPFXRegion = record
  257. count, capacity: Integer;
  258. particleRef: PParticleReferenceArray;
  259. particleOrder: PFXPointerList;
  260. end;
  261. PPFXRegion = ^TPFXRegion;
  262. TPFXSortAccuracy = (saLow, saOneTenth, saOneThird, saOneHalf, saHigh);
  263. (* Rendering interface for scene-wide particle FX.
  264. A renderer can take care of rendering any number of particle systems,
  265. its main task being to depth-sort the particles so that they are blended
  266. appropriately.
  267. This object will usually be placed at the end of the scene hierarchy,
  268. just before the HUD overlays, its position, rotation etc. is of no
  269. importance and has no effect on the rendering of the particles. *)
  270. TgxParticleFXRenderer = class(TgxBaseSceneObject)
  271. private
  272. FManagerList: TList;
  273. FLastSortTime: Double;
  274. FLastParticleCount: Integer;
  275. FZWrite, FZTest, FZCull: Boolean;
  276. FZSortAccuracy: TPFXSortAccuracy;
  277. FZMaxDistance: Single;
  278. FBlendingMode: TgxBlendingMode;
  279. FRegions: array[0..cPFXNbRegions - 1] of TPFXRegion;
  280. protected
  281. function StoreZMaxDistance: Boolean;
  282. // Register a manager
  283. procedure RegisterManager(aManager: TgxParticleFXManager);
  284. // UnRegister a manager
  285. procedure UnRegisterManager(aManager: TgxParticleFXManager);
  286. procedure UnRegisterAll;
  287. public
  288. constructor Create(aOwner: TComponent); override;
  289. destructor Destroy; override;
  290. (* Quick Explanation of what is below:
  291. The purpose is to depth-sort a large number (thousandths) of particles and
  292. render them back to front. The rendering part is not particularly complex,
  293. it just invokes the various PFX managers involved and request particle
  294. renderings.
  295. The sort uses a first-pass region partition (the depth range is split into
  296. regions, and particles are assigned directly to the region they belong to),
  297. then each region is sorted with a QuickSort.
  298. The QuickSort itself is the regular classic variant, but the comparison is
  299. made on singles as if they were integers, this is allowed by the IEEE format
  300. in a very efficient manner if all values are superior to 1, which is ensured
  301. by the distance calculation and a fixed offset of 1 *)
  302. procedure BuildList(var rci: TgxRenderContextInfo); override;
  303. // Time (in msec) spent sorting the particles for last render.
  304. property LastSortTime: Double read FLastSortTime;
  305. // Amount of particles during the last render.
  306. property LastParticleCount: Integer read FLastParticleCount;
  307. published
  308. (*Specifies if particles should write to ZBuffer.
  309. If the PFXRenderer is the last object to be rendered in the scene,
  310. it is not necessary to write to the ZBuffer since the particles
  311. are depth-sorted. Writing to the ZBuffer has a performance penalty. *)
  312. property ZWrite: Boolean read FZWrite write FZWrite default False;
  313. // Specifies if particles should write to test ZBuffer.
  314. property ZTest: Boolean read FZTest write FZTest default True;
  315. // If true the renderer will cull particles that are behind the camera.
  316. property ZCull: Boolean read FZCull write FZCull default True;
  317. (* If true particles will be accurately sorted back to front.
  318. When false, only a rough ordering is used, which can result in
  319. visual glitches but may be faster. *)
  320. property ZSortAccuracy: TPFXSortAccuracy read FZSortAccuracy write FZSortAccuracy default saHigh;
  321. (* Maximum distance for rendering PFX particles.
  322. If zero, camera's DepthOfView is used. *)
  323. property ZMaxDistance: Single read FZMaxDistance write FZMaxDistance stored StoreZMaxDistance;
  324. (* Default blending mode for particles.
  325. "Additive" blending is the usual mode (increases brightness and
  326. saturates), "transparency" may be used for smoke or systems that
  327. opacify view, "opaque" is more rarely used.
  328. Note: specific PFX managers may override/ignore this setting. *)
  329. property BlendingMode: TgxBlendingMode read FBlendingMode write FBlendingMode default bmAdditive;
  330. property Visible;
  331. end;
  332. TgxSourcePFXVelocityMode = (svmAbsolute, svmRelative);
  333. TgxSourcePFXPositionMode = (spmAbsoluteOffset, spmRelative);
  334. TgxSourcePFXDispersionMode = (sdmFast, sdmIsotropic);
  335. // Simple Particles Source.
  336. TgxSourcePFXEffect = class(TgxParticleFXEffect)
  337. private
  338. FInitialVelocity: TgxCoordinates;
  339. FInitialPosition: TgxCoordinates;
  340. FPositionDispersionRange: TgxCoordinates;
  341. FVelocityDispersion: Single;
  342. FPositionDispersion: Single;
  343. FParticleInterval: Single;
  344. FVelocityMode: TgxSourcePFXVelocityMode;
  345. FPositionMode: TgxSourcePFXPositionMode;
  346. FDispersionMode: TgxSourcePFXDispersionMode;
  347. FEnabled: Boolean;
  348. FDisabledIfOwnerInvisible: Boolean;
  349. FTimeRemainder: Double;
  350. FRotationDispersion: Single;
  351. protected
  352. procedure SetInitialVelocity(const val: TgxCoordinates);
  353. procedure SetInitialPosition(const val: TgxCoordinates);
  354. procedure SetPositionDispersionRange(const val: TgxCoordinates);
  355. procedure SetParticleInterval(const val: Single);
  356. procedure WriteToFiler(writer: TWriter); override;
  357. procedure ReadFromFiler(reader: TReader); override;
  358. function ParticleAbsoluteInitialPos: TAffineVector;
  359. public
  360. constructor Create(aOwner: TXCollection); override;
  361. destructor Destroy; override;
  362. class function FriendlyName: string; override;
  363. class function FriendlyDescription: string; override;
  364. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  365. // Instantaneously creates nb particles
  366. procedure Burst(time: Double; nb: Integer);
  367. procedure RingExplosion(time: Double;
  368. minInitialSpeed, maxInitialSpeed: Single;
  369. nbParticles: Integer);
  370. published
  371. property InitialVelocity: TgxCoordinates read FInitialVelocity write SetInitialVelocity;
  372. property VelocityDispersion: Single read FVelocityDispersion write FVelocityDispersion;
  373. property InitialPosition: TgxCoordinates read FInitialPosition write SetInitialPosition;
  374. property PositionDispersion: Single read FPositionDispersion write FPositionDispersion;
  375. property PositionDispersionRange: TgxCoordinates read FPositionDispersionRange write SetPositionDispersionRange;
  376. property ParticleInterval: Single read FParticleInterval write SetParticleInterval;
  377. property VelocityMode: TgxSourcePFXVelocityMode read FVelocityMode write FVelocityMode default svmAbsolute;
  378. property PositionMode: TgxSourcePFXPositionMode read FPositionMode write FPositionMode default spmAbsoluteOffset;
  379. property DispersionMode: TgxSourcePFXDispersionMode read FDispersionMode write FDispersionMode default sdmFast;
  380. property RotationDispersion: Single read FRotationDispersion write FRotationDispersion;
  381. property Enabled: boolean read FEnabled write FEnabled;
  382. property DisabledIfOwnerInvisible: boolean read FDisabledIfOwnerInvisible write FDisabledIfOwnerInvisible;
  383. end;
  384. (* An abstract PFX manager for simple dynamic particles.
  385. Adds properties and progress implementation for handling moving particles
  386. (simple velocity and const acceleration integration). *)
  387. TgxDynamicPFXManager = class(TgxParticleFXManager)
  388. private
  389. FAcceleration: TgxCoordinates;
  390. FFriction: Single;
  391. FCurrentTime: Double;
  392. //FRotationCenter: TAffineVector;
  393. protected
  394. procedure SetAcceleration(const val: TgxCoordinates);
  395. (* Returns the maximum age for a particle.
  396. Particles older than that will be killed by DoProgress. *)
  397. function MaxParticleAge: Single; virtual; abstract;
  398. property CurrentTime: Double read FCurrentTime;
  399. public
  400. constructor Create(aOwner: TComponent); override;
  401. destructor Destroy; override;
  402. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  403. published
  404. // Oriented acceleration applied to the particles.
  405. property Acceleration: TgxCoordinates read FAcceleration write SetAcceleration;
  406. (* Friction applied to the particles.
  407. Friction is applied as a speed scaling factor over 1 second, ie.
  408. a friction of 0.5 will half speed over 1 second, a friction of 3
  409. will triple speed over 1 second, and a friction of 1 (default
  410. value) will have no effect. *)
  411. property Friction: Single read FFriction write FFriction;
  412. end;
  413. TPFXLifeColor = class(TCollectionItem)
  414. private
  415. FColorInner: TgxColor;
  416. FColorOuter: TgxColor;
  417. FLifeTime, FInvLifeTime: Single;
  418. FIntervalRatio: Single;
  419. FSizeScale: Single;
  420. FDoScale: Boolean;
  421. FDoRotate: boolean;
  422. FRotateAngle: Single;
  423. protected
  424. function GetDisplayName: string; override;
  425. procedure SetColorInner(const val: TgxColor);
  426. procedure SetColorOuter(const val: TgxColor);
  427. procedure SetLifeTime(const val: Single);
  428. procedure SetSizeScale(const val: Single);
  429. procedure SetRotateAngle(const Value: Single); // indirectly persistent
  430. public
  431. constructor Create(Collection: TCollection); override;
  432. destructor Destroy; override;
  433. procedure Assign(Source: TPersistent); override;
  434. // Stores 1/LifeTime
  435. property InvLifeTime: Single read FInvLifeTime;
  436. // Stores 1/(LifeTime[Next]-LifeTime[Self])
  437. property InvIntervalRatio: Single read FIntervalRatio;
  438. published
  439. property ColorInner: TgxColor read FColorInner write SetColorInner;
  440. property ColorOuter: TgxColor read FColorOuter write SetColorOuter;
  441. property LifeTime: Single read FLifeTime write SetLifeTime;
  442. property SizeScale: Single read FSizeScale write SetSizeScale;
  443. property RotateAngle: Single read FRotateAngle write SetRotateAngle;
  444. end;
  445. TPFXLifeColors = class(TOwnedCollection)
  446. protected
  447. procedure SetItems(index: Integer; const val: TPFXLifeColor);
  448. function GetItems(index: Integer): TPFXLifeColor;
  449. public
  450. constructor Create(AOwner: TPersistent);
  451. function Add: TPFXLifeColor;
  452. function FindItemID(ID: Integer): TPFXLifeColor;
  453. property Items[index: Integer]: TPFXLifeColor read GetItems write SetItems; default;
  454. function MaxLifeTime: Double;
  455. function RotationsDefined: Boolean;
  456. function ScalingDefined: Boolean;
  457. procedure PrepareIntervalRatios;
  458. end;
  459. (* Base PFX manager for particles with life colors.
  460. Particles have a core and edge color, for subclassing. *)
  461. TgxLifeColoredPFXManager = class(TgxDynamicPFXManager)
  462. private
  463. FLifeColors: TPFXLifeColors;
  464. FLifeColorsLookup: TList;
  465. FLifeRotations: Boolean;
  466. FLifeScaling: Boolean;
  467. FColorInner: TgxColor;
  468. FColorOuter: TgxColor;
  469. FParticleSize: Single;
  470. protected
  471. procedure SetLifeColors(const val: TPFXLifeColors);
  472. procedure SetColorInner(const val: TgxColor);
  473. procedure SetColorOuter(const val: TgxColor);
  474. procedure InitializeRendering(var rci: TgxRenderContextInfo); override;
  475. procedure FinalizeRendering(var rci: TgxRenderContextInfo); override;
  476. function MaxParticleAge: Single; override;
  477. procedure ComputeColors(var lifeTime: Single; var inner, outer: TgxColorVector);
  478. procedure ComputeInnerColor(var lifeTime: Single; var inner: TgxColorVector);
  479. procedure ComputeOuterColor(var lifeTime: Single; var outer: TgxColorVector);
  480. function ComputeSizeScale(var lifeTime: Single; var sizeScale: Single): Boolean;
  481. function ComputeRotateAngle(var lifeTime, rotateAngle: Single): Boolean;
  482. procedure RotateVertexBuf(buf: TgxAffineVectorList; lifeTime: Single;
  483. const axis: TAffineVector; offsetAngle: Single);
  484. public
  485. constructor Create(aOwner: TComponent); override;
  486. destructor Destroy; override;
  487. property ParticleSize: Single read FParticleSize write FParticleSize;
  488. property ColorInner: TgxColor read FColorInner write SetColorInner;
  489. property ColorOuter: TgxColor read FColorOuter write SetColorOuter;
  490. property LifeColors: TPFXLifeColors read FLifeColors write SetLifeColors;
  491. published
  492. property BlendingMode default bmAdditive;
  493. end;
  494. TPFXDirectRenderEvent = procedure(Sender: TObject; aParticle: TgxParticle;
  495. var rci: TgxRenderContextInfo) of object;
  496. TPFXProgressEvent = procedure(Sender: TObject; const progressTime: TgxProgressTimes;
  497. var defaultProgress: Boolean) of object;
  498. TPFXParticleProgress = procedure(Sender: TObject; const progressTime: TgxProgressTimes;
  499. aParticle: TgxParticle; var killParticle: Boolean) of object;
  500. TPFXGetParticleCountEvent = function(Sender: TObject): Integer of object;
  501. (* A particles FX manager offering events for customization/experimentation.
  502. This manager essentially surfaces the PFX methods as events, and is best
  503. suited when you have specific particles that don't fall into any existing
  504. category, or when you want to experiment with particles and later plan to
  505. wrap things up in a full-blown manager.
  506. If the events aren't handled, nothing will be rendered. *)
  507. TgxCustomPFXManager = class(TgxLifeColoredPFXManager)
  508. private
  509. FOnInitializeRendering: TDirectRenderEvent;
  510. FOnBeginParticles: TDirectRenderEvent;
  511. FOnRenderParticle: TPFXDirectRenderEvent;
  512. FOnEndParticles: TDirectRenderEvent;
  513. FOnFinalizeRendering: TDirectRenderEvent;
  514. FOnProgress: TPFXProgressEvent;
  515. FOnParticleProgress: TPFXParticleProgress;
  516. FOnGetParticleCountEvent: TPFXGetParticleCountEvent;
  517. protected
  518. function TexturingMode: Cardinal; override;
  519. procedure InitializeRendering(var rci: TgxRenderContextInfo); override;
  520. procedure BeginParticles(var rci: TgxRenderContextInfo); override;
  521. procedure RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle); override;
  522. procedure EndParticles(var rci: TgxRenderContextInfo); override;
  523. procedure FinalizeRendering(var rci: TgxRenderContextInfo); override;
  524. public
  525. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  526. function ParticleCount: Integer; override;
  527. published
  528. property OnInitializeRendering: TDirectRenderEvent read FOnInitializeRendering write FOnInitializeRendering;
  529. property OnBeginParticles: TDirectRenderEvent read FOnBeginParticles write FOnBeginParticles;
  530. property OnRenderParticle: TPFXDirectRenderEvent read FOnRenderParticle write FOnRenderParticle;
  531. property OnEndParticles: TDirectRenderEvent read FOnEndParticles write FOnEndParticles;
  532. property OnFinalizeRendering: TDirectRenderEvent read FOnFinalizeRendering write FOnFinalizeRendering;
  533. property OnProgress: TPFXProgressEvent read FOnProgress write FOnProgress;
  534. property OnParticleProgress: TPFXParticleProgress read FOnParticleProgress write FOnParticleProgress;
  535. property OnGetParticleCountEvent: TPFXGetParticleCountEvent read FOnGetParticleCountEvent write FOnGetParticleCountEvent;
  536. property ParticleSize;
  537. property ColorInner;
  538. property ColorOuter;
  539. property LifeColors;
  540. end;
  541. (* Polygonal particles FX manager.
  542. The particles of this manager are made of N-face regular polygon with
  543. a core and edge color. No texturing is available.
  544. If you render large particles and don't have T&L acceleration, consider
  545. using TgxPointLightPFXManager. *)
  546. TgxPolygonPFXManager = class(TgxLifeColoredPFXManager)
  547. private
  548. FNbSides: Integer;
  549. Fvx, Fvy: TAffineVector; // NOT persistent
  550. FVertices: TgxAffineVectorList; // NOT persistent
  551. FVertBuf: TgxAffineVectorList; // NOT persistent
  552. protected
  553. procedure SetNbSides(const val: Integer);
  554. function TexturingMode: Cardinal; override;
  555. procedure InitializeRendering(var rci: TgxRenderContextInfo); override;
  556. procedure BeginParticles(var rci: TgxRenderContextInfo); override;
  557. procedure RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle); override;
  558. procedure EndParticles(var rci: TgxRenderContextInfo); override;
  559. procedure FinalizeRendering(var rci: TgxRenderContextInfo); override;
  560. public
  561. constructor Create(aOwner: TComponent); override;
  562. destructor Destroy; override;
  563. published
  564. property NbSides: Integer read FNbSides write SetNbSides default 6;
  565. property ParticleSize;
  566. property ColorInner;
  567. property ColorOuter;
  568. property LifeColors;
  569. end;
  570. (* Sprite color modes.
  571. scmFade: vertex coloring is used to fade inner-outer
  572. scmInner: vertex coloring uses inner color only
  573. scmOuter: vertex coloring uses outer color only
  574. scmNone: vertex coloring is NOT used (colors are ignored). *)
  575. TSpriteColorMode = (scmFade, scmInner, scmOuter, scmNone);
  576. // Sprites per sprite texture for the SpritePFX.
  577. TSpritesPerTexture = (sptOne, sptFour);
  578. (* Base class for sprite-based particles FX managers.
  579. The particles are made of optionally centered single-textured quads. *)
  580. TgxBaseSpritePFXManager = class(TgxLifeColoredPFXManager)
  581. private
  582. FTexHandle: TgxTextureHandle;
  583. Fvx, Fvy, Fvz: TAffineVector; // NOT persistent
  584. FVertices: TgxAffineVectorList; // NOT persistent
  585. FVertBuf: TgxAffineVectorList; // NOT persistent
  586. FAspectRatio: Single;
  587. FRotation: Single;
  588. FShareSprites: TgxBaseSpritePFXManager;
  589. FSpritesPerTexture: TSpritesPerTexture;
  590. FColorMode: TSpriteColorMode;
  591. protected
  592. // Subclasses should draw their stuff in this bmp32.
  593. procedure PrepareImage(bmp32: TgxBitmap32; var texFormat: Integer); virtual; abstract;
  594. procedure BindTexture(var rci: TgxRenderContextInfo);
  595. procedure SetSpritesPerTexture(const val: TSpritesPerTexture); virtual;
  596. procedure SetColorMode(const val: TSpriteColorMode);
  597. procedure SetAspectRatio(const val: Single);
  598. function StoreAspectRatio: Boolean;
  599. procedure SetRotation(const val: Single);
  600. procedure SetShareSprites(const val: TgxBaseSpritePFXManager);
  601. function TexturingMode: Cardinal; override;
  602. procedure InitializeRendering(var rci: TgxRenderContextInfo); override;
  603. procedure BeginParticles(var rci: TgxRenderContextInfo); override;
  604. procedure RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle); override;
  605. procedure EndParticles(var rci: TgxRenderContextInfo); override;
  606. procedure FinalizeRendering(var rci: TgxRenderContextInfo); override;
  607. property SpritesPerTexture: TSpritesPerTexture read FSpritesPerTexture write SetSpritesPerTexture;
  608. public
  609. constructor Create(aOwner: TComponent); override;
  610. destructor Destroy; override;
  611. property ColorMode: TSpriteColorMode read FColorMode write SetColorMode;
  612. published
  613. (* Ratio between width and height.
  614. An AspectRatio of 1 (default) will result in square sprite particles,
  615. values higher than one will result in horizontally stretched sprites,
  616. values below one will stretch vertically (assuming no rotation is applied). *)
  617. property AspectRatio: Single read FAspectRatio write SetAspectRatio stored StoreAspectRatio;
  618. (* Particle sprites rotation (in degrees).
  619. All particles of the PFX manager share this rotation. *)
  620. property Rotation: Single read FRotation write SetRotation;
  621. (* If specified the manager will reuse the other manager's sprites.
  622. Sharing sprites between PFX managers can help at the rendering stage
  623. if particles of the managers are mixed by helping reduce the number
  624. of texture switches. Note that only the texture is shared, not the
  625. colors, sizes or other dynamic parameters. *)
  626. property ShareSprites: TgxBaseSpritePFXManager read FShareSprites write FShareSprites;
  627. end;
  628. TPFXPrepareTextureImageEvent = procedure(Sender: TObject; destBmp32: TgxBitmap32; var texFormat: Integer) of object;
  629. (* A sprite-based particles FX managers using user-specified
  630. code to prepare the texture .*)
  631. TgxCustomSpritePFXManager = class(TgxBaseSpritePFXManager)
  632. private
  633. FOnPrepareTextureImage: TPFXPrepareTextureImageEvent;
  634. protected
  635. procedure PrepareImage(bmp32: TgxBitmap32; var texFormat: Integer); override;
  636. public
  637. constructor Create(aOwner: TComponent); override;
  638. destructor Destroy; override;
  639. published
  640. // Place your texture rendering code in this event.
  641. property OnPrepareTextureImage: TPFXPrepareTextureImageEvent read FOnPrepareTextureImage write FOnPrepareTextureImage;
  642. property ColorMode default scmInner;
  643. property SpritesPerTexture default sptOne;
  644. property ParticleSize;
  645. property ColorInner;
  646. property ColorOuter;
  647. property LifeColors;
  648. end;
  649. (* A sprite-based particles FX managers using point light maps.
  650. The texture map is a round, distance-based transparency map (center "opaque"),
  651. you can adjust the quality (size) of the underlying texture map with the
  652. TexMapSize property.
  653. This PFX manager renders particles similar to what you can get with
  654. TgxPolygonPFXManager but stresses fillrate more than T&L rate (and will
  655. usually be slower than the PolygonPFX when nbSides is low or T&L acceleration
  656. available). Consider this implementation as a sample for your own PFX managers
  657. that may use particles with more complex textures. *)
  658. TgxPointLightPFXManager = class(TgxBaseSpritePFXManager)
  659. private
  660. FTexMapSize: Integer;
  661. protected
  662. procedure PrepareImage(bmp32: TgxBitmap32; var texFormat: Integer); override;
  663. procedure SetTexMapSize(const val: Integer);
  664. public
  665. constructor Create(aOwner: TComponent); override;
  666. destructor Destroy; override;
  667. published
  668. (* Underlying texture map size, as a power of two.
  669. Min value is 3 (size=8), max value is 9 (size=512). *)
  670. property TexMapSize: Integer read FTexMapSize write SetTexMapSize default 5;
  671. property ColorMode default scmInner;
  672. property ParticleSize;
  673. property ColorInner;
  674. property ColorOuter;
  675. property LifeColors;
  676. end;
  677. { Returns or creates the TgxBInertia within the given object's behaviours. }
  678. function GetOrCreateSourcePFX(obj: TgxBaseSceneObject; const name: string = ''): TgxSourcePFXEffect;
  679. // ------------------------------------------------------------------
  680. implementation
  681. // ------------------------------------------------------------------
  682. function GetOrCreateSourcePFX(obj: TgxBaseSceneObject; const name: string = ''): TgxSourcePFXEffect;
  683. var
  684. i: Integer;
  685. begin
  686. with obj.Effects do
  687. begin
  688. if name = '' then
  689. begin
  690. i := IndexOfClass(TgxSourcePFXEffect);
  691. if i >= 0 then
  692. Result := TgxSourcePFXEffect(Items[i])
  693. else
  694. Result := TgxSourcePFXEffect.Create(obj.Effects);
  695. end
  696. else
  697. begin
  698. i := IndexOfName(name);
  699. if i >= 0 then
  700. Result := (Items[i] as TgxSourcePFXEffect)
  701. else
  702. begin
  703. Result := TgxSourcePFXEffect.Create(obj.Effects);
  704. Result.Name := name;
  705. end;
  706. end;
  707. end;
  708. end;
  709. procedure RndVector(const dispersion: TgxSourcePFXDispersionMode;
  710. var v: TAffineVector; var f: Single;
  711. dispersionRange: TgxCoordinates);
  712. var
  713. f2, fsq: Single;
  714. p: TVector4f;
  715. begin
  716. f2 := 2 * f;
  717. if Assigned(dispersionRange) then
  718. p := VectorScale(dispersionRange.DirectVector, f2)
  719. else
  720. p := VectorScale(XYZHmgVector, f2);
  721. case dispersion of
  722. sdmFast:
  723. begin
  724. v.X := (Random - 0.5) * p.X;
  725. v.Y := (Random - 0.5) * p.Y;
  726. v.Z := (Random - 0.5) * p.Z;
  727. end;
  728. else
  729. fsq := Sqr(0.5);
  730. repeat
  731. v.X := (Random - 0.5);
  732. v.Y := (Random - 0.5);
  733. v.Z := (Random - 0.5);
  734. until VectorNorm(v) <= fsq;
  735. v.X := v.X * p.X;
  736. v.Y := v.Y * p.Y;
  737. v.Z := v.Z * p.Z;
  738. end;
  739. end;
  740. // ------------------
  741. // ------------------ TgxParticle ------------------
  742. // ------------------
  743. constructor TgxParticle.Create;
  744. begin
  745. FEffectScale := 1;
  746. inherited Create;
  747. end;
  748. destructor TgxParticle.Destroy;
  749. begin
  750. inherited Destroy;
  751. end;
  752. function TgxParticle.GetPosition(const Index: Integer): Single;
  753. begin
  754. Result := FPosition.V[Index];
  755. end;
  756. procedure TgxParticle.WritePosition(const Index: Integer; const aValue: Single);
  757. begin
  758. if (aValue <> FPosition.V[Index]) then
  759. FPosition.V[Index] := aValue;
  760. end;
  761. function TgxParticle.GetVelocity(const Index: Integer): Single;
  762. begin
  763. Result := FVelocity.X;
  764. end;
  765. procedure TgxParticle.WriteVelocity(const Index: Integer; const aValue: Single);
  766. begin
  767. if (aValue <> FVelocity.V[Index]) then
  768. FVelocity.V[Index] := aValue;
  769. end;
  770. procedure TgxParticle.WriteToFiler(writer: TgxVirtualWriter);
  771. begin
  772. inherited WriteToFiler(writer);
  773. with writer do
  774. begin
  775. WriteInteger(0); // Archive Version 0
  776. WriteInteger(FID);
  777. Write(FPosition, SizeOf(FPosition));
  778. Write(FVelocity, SizeOf(FVelocity));
  779. WriteFloat(FCreationTime);
  780. end;
  781. end;
  782. procedure TgxParticle.ReadFromFiler(reader: TgxVirtualReader);
  783. var
  784. archiveVersion: integer;
  785. begin
  786. inherited ReadFromFiler(reader);
  787. archiveVersion := reader.ReadInteger;
  788. if archiveVersion = 0 then
  789. with reader do
  790. begin
  791. FID := ReadInteger;
  792. Read(FPosition, SizeOf(FPosition));
  793. Read(FVelocity, SizeOf(FVelocity));
  794. FCreationTime := ReadFloat;
  795. end
  796. else
  797. RaiseFilerException(archiveVersion);
  798. end;
  799. // ------------------
  800. // ------------------ TgxParticleList ------------------
  801. // ------------------
  802. constructor TgxParticleList.Create;
  803. begin
  804. inherited Create;
  805. FItemList := TgxPersistentObjectList.Create;
  806. FitemList.GrowthDelta := 64;
  807. FDirectList := nil;
  808. end;
  809. destructor TgxParticleList.Destroy;
  810. begin
  811. FItemList.CleanFree;
  812. inherited Destroy;
  813. end;
  814. procedure TgxParticleList.WriteToFiler(writer: TgxVirtualWriter);
  815. begin
  816. inherited WriteToFiler(writer);
  817. with writer do
  818. begin
  819. WriteInteger(0); // Archive Version 0
  820. FItemList.WriteToFiler(writer);
  821. end;
  822. end;
  823. procedure TgxParticleList.ReadFromFiler(reader: TgxVirtualReader);
  824. var
  825. archiveVersion: integer;
  826. begin
  827. inherited ReadFromFiler(reader);
  828. archiveVersion := reader.ReadInteger;
  829. if archiveVersion = 0 then
  830. with reader do
  831. begin
  832. FItemList.ReadFromFilerWithEvent(reader, AfterItemCreated);
  833. FDirectList := PGLParticleArray(FItemList.List);
  834. end
  835. else
  836. RaiseFilerException(archiveVersion);
  837. end;
  838. function TgxParticleList.GetItems(index: Integer): TgxParticle;
  839. begin
  840. Result := TgxParticle(FItemList[index]);
  841. end;
  842. procedure TgxParticleList.SetItems(index: Integer; val: TgxParticle);
  843. begin
  844. FItemList[index] := val;
  845. end;
  846. procedure TgxParticleList.AfterItemCreated(Sender: TObject);
  847. begin
  848. (Sender as TgxParticle).Manager := Self.Owner;
  849. end;
  850. function TgxParticleList.ItemCount: Integer;
  851. begin
  852. Result := FItemList.Count;
  853. end;
  854. function TgxParticleList.AddItem(aItem: TgxParticle): Integer;
  855. begin
  856. aItem.Manager := Self.Owner;
  857. Result := FItemList.Add(aItem);
  858. FDirectList := PGLParticleArray(FItemList.List);
  859. end;
  860. procedure TgxParticleList.RemoveAndFreeItem(aItem: TgxParticle);
  861. var
  862. i: Integer;
  863. begin
  864. i := FItemList.IndexOf(aItem);
  865. if i >= 0 then
  866. begin
  867. if aItem.Manager = Self.Owner then
  868. aItem.Manager := nil;
  869. aItem.Free;
  870. FItemList.List^[i] := nil;
  871. end;
  872. end;
  873. function TgxParticleList.IndexOfItem(aItem: TgxParticle): Integer;
  874. begin
  875. Result := FItemList.IndexOf(aItem);
  876. end;
  877. procedure TgxParticleList.Pack;
  878. begin
  879. FItemList.Pack;
  880. FDirectList := PGLParticleArray(FItemList.List);
  881. end;
  882. // ------------------
  883. // ------------------ TgxParticleFXManager ------------------
  884. // ------------------
  885. constructor TgxParticleFXManager.Create(aOwner: TComponent);
  886. begin
  887. inherited;
  888. FUsers := TList.create;
  889. FParticles := TgxParticleList.Create;
  890. FParticles.Owner := Self;
  891. FBlendingMode := bmAdditive;
  892. RegisterManager(Self);
  893. end;
  894. destructor TgxParticleFXManager.Destroy;
  895. var
  896. i: integer;
  897. begin
  898. inherited Destroy;
  899. for i := FUsers.Count - 1 downto 0 do
  900. TgxParticleFXEffect(FUsers[i]).managerNotification(self, opRemove);
  901. DeRegisterManager(Self);
  902. Renderer := nil;
  903. FParticles.Free;
  904. FUsers.Free;
  905. end;
  906. procedure TgxParticleFXManager.NotifyChange(Sender: TObject);
  907. begin
  908. if Assigned(FRenderer) then
  909. Renderer.StructureChanged;
  910. end;
  911. procedure TgxParticleFXManager.DoProgress(const progressTime: TgxProgressTimes);
  912. begin
  913. inherited;
  914. if FAutoFreeWhenEmpty and (FParticles.ItemCount = 0) then
  915. Free;
  916. end;
  917. class function TgxParticleFXManager.ParticlesClass: TgxParticleClass;
  918. begin
  919. Result := TgxParticle;
  920. end;
  921. function TgxParticleFXManager.CreateParticle: TgxParticle;
  922. begin
  923. Result := ParticlesClass.Create;
  924. Result.FID := FNextID;
  925. if Assigned(cadencer) then
  926. Result.FCreationTime := Cadencer.CurrentTime;
  927. Inc(FNextID);
  928. FParticles.AddItem(Result);
  929. if Assigned(FOnCreateParticle) then
  930. FOnCreateParticle(Self, Result);
  931. end;
  932. procedure TgxParticleFXManager.CreateParticles(nbParticles: Integer);
  933. var
  934. i: Integer;
  935. begin
  936. FParticles.FItemList.RequiredCapacity(FParticles.ItemCount + nbParticles);
  937. for i := 1 to nbParticles do
  938. CreateParticle;
  939. end;
  940. procedure TgxParticleFXManager.SetRenderer(const val: TgxParticleFXRenderer);
  941. begin
  942. if FRenderer <> val then
  943. begin
  944. if Assigned(FRenderer) then
  945. FRenderer.UnRegisterManager(Self);
  946. FRenderer := val;
  947. if Assigned(FRenderer) then
  948. FRenderer.RegisterManager(Self);
  949. end;
  950. end;
  951. procedure TgxParticleFXManager.SetParticles(const aParticles: TgxParticleList);
  952. begin
  953. FParticles.Assign(aParticles);
  954. end;
  955. function TgxParticleFXManager.ParticleCount: Integer;
  956. begin
  957. Result := FParticles.FItemList.Count;
  958. end;
  959. procedure TgxParticleFXManager.ApplyBlendingMode;
  960. begin
  961. if Renderer.BlendingMode <> BlendingMode then
  962. begin
  963. // case disjunction to minimize OpenGL State changes
  964. if Renderer.BlendingMode in [bmAdditive, bmTransparency] then
  965. begin
  966. case BlendingMode of
  967. bmAdditive:
  968. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOne);
  969. bmTransparency:
  970. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  971. else // bmOpaque
  972. rci.gxStates.Disable(stBlend);
  973. end;
  974. end
  975. else
  976. begin
  977. case BlendingMode of
  978. bmAdditive:
  979. begin
  980. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOne);
  981. rci.gxStates.Enable(stBlend);
  982. end;
  983. bmTransparency:
  984. begin
  985. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  986. rci.gxStates.Enable(stBlend);
  987. end;
  988. else
  989. // bmOpaque, do nothing
  990. end;
  991. end;
  992. end;
  993. end;
  994. procedure TgxParticleFXManager.UnapplyBlendingMode;
  995. begin
  996. if Renderer.BlendingMode <> BlendingMode then
  997. begin
  998. // case disjunction to minimize OpenGL State changes
  999. if BlendingMode in [bmAdditive, bmTransparency] then
  1000. begin
  1001. case Renderer.BlendingMode of
  1002. bmAdditive:
  1003. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOne);
  1004. bmTransparency:
  1005. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  1006. else // bmOpaque
  1007. rci.gxStates.Disable(stBlend);
  1008. end;
  1009. end
  1010. else
  1011. begin
  1012. case Renderer.BlendingMode of
  1013. bmAdditive:
  1014. begin
  1015. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOne);
  1016. rci.gxStates.Enable(stBlend);
  1017. end;
  1018. bmTransparency:
  1019. begin
  1020. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  1021. rci.gxStates.Enable(stBlend);
  1022. end;
  1023. else
  1024. // bmOpaque, do nothing
  1025. end;
  1026. end;
  1027. end;
  1028. end;
  1029. procedure TgxParticleFXManager.RegisterUser(obj: TgxParticleFXEffect);
  1030. begin
  1031. if FUsers.IndexOf(obj) = -1 then
  1032. FUsers.Add(obj);
  1033. end;
  1034. procedure TgxParticleFXManager.UnregisterUser(obj: TgxParticleFXEffect);
  1035. begin
  1036. FUsers.Remove(obj);
  1037. end;
  1038. // ------------------
  1039. // ------------------ TgxParticleFXEffect ------------------
  1040. // ------------------
  1041. constructor TgxParticleFXEffect.Create(aOwner: TXCollection);
  1042. begin
  1043. FEffectScale := 1;
  1044. inherited;
  1045. end;
  1046. destructor TgxParticleFXEffect.Destroy;
  1047. begin
  1048. Manager := nil;
  1049. inherited Destroy;
  1050. end;
  1051. procedure TgxParticleFXEffect.WriteToFiler(writer: TWriter);
  1052. var
  1053. st: string;
  1054. begin
  1055. with writer do
  1056. begin
  1057. // ArchiveVersion 1, added EffectScale
  1058. // ArchiveVersion 2, added inherited call
  1059. WriteInteger(2);
  1060. inherited;
  1061. if Manager <> nil then
  1062. st := Manager.GetNamePath
  1063. else
  1064. st := '';
  1065. WriteString(st);
  1066. WriteFloat(FEffectScale);
  1067. end;
  1068. end;
  1069. procedure TgxParticleFXEffect.ReadFromFiler(reader: TReader);
  1070. var
  1071. archiveVersion: integer;
  1072. begin
  1073. with reader do
  1074. begin
  1075. archiveVersion := ReadInteger;
  1076. Assert(archiveVersion in [0..2]);
  1077. if archiveVersion >= 2 then
  1078. inherited;
  1079. if archiveVersion >= 0 then
  1080. begin
  1081. FManagerName := ReadString;
  1082. Manager := nil;
  1083. end;
  1084. if archiveVersion >= 1 then
  1085. begin
  1086. FEffectScale := ReadFloat;
  1087. end;
  1088. end;
  1089. end;
  1090. procedure TgxParticleFXEffect.Loaded;
  1091. var
  1092. mng: TComponent;
  1093. begin
  1094. inherited;
  1095. if FManagerName <> '' then
  1096. begin
  1097. mng := FindManager(TgxParticleFXManager, FManagerName);
  1098. if Assigned(mng) then
  1099. Manager := TgxParticleFXManager(mng);
  1100. FManagerName := '';
  1101. end;
  1102. end;
  1103. procedure TgxParticleFXEffect.SetManager(val: TgxParticleFXManager);
  1104. begin
  1105. if assigned(FManager) then
  1106. FManager.unregisterUser(self);
  1107. FManager := val;
  1108. if assigned(FManager) then
  1109. FManager.registerUser(self);
  1110. end;
  1111. procedure TgxParticleFXEffect.SetEffectScale(const Value: single);
  1112. begin
  1113. FEffectScale := Value;
  1114. end;
  1115. procedure TgxParticleFXEffect.managerNotification(
  1116. aManager: TgxParticleFXManager; Operation: TOperation);
  1117. begin
  1118. if (Operation = opRemove) and (aManager = manager) then
  1119. manager := nil;
  1120. end;
  1121. // ------------------
  1122. // ------------------ TgxParticleFXRenderer ------------------
  1123. // ------------------
  1124. constructor TgxParticleFXRenderer.Create(aOwner: TComponent);
  1125. begin
  1126. inherited;
  1127. ObjectStyle := ObjectStyle + [osNoVisibilityCulling, osDirectDraw];
  1128. FZTest := True;
  1129. FZCull := True;
  1130. FZSortAccuracy := saHigh;
  1131. FManagerList := TList.Create;
  1132. FBlendingMode := bmAdditive;
  1133. end;
  1134. destructor TgxParticleFXRenderer.Destroy;
  1135. var
  1136. i: Integer;
  1137. begin
  1138. for i := 0 to cPFXNbRegions - 1 do
  1139. begin
  1140. FreeMem(FRegions[i].particleRef);
  1141. FreeMem(FRegions[i].particleOrder);
  1142. end;
  1143. UnRegisterAll;
  1144. FManagerList.Free;
  1145. inherited Destroy;
  1146. end;
  1147. procedure TgxParticleFXRenderer.RegisterManager(aManager: TgxParticleFXManager);
  1148. begin
  1149. FManagerList.Add(aManager);
  1150. end;
  1151. procedure TgxParticleFXRenderer.UnRegisterManager(aManager: TgxParticleFXManager);
  1152. begin
  1153. FManagerList.Remove(aManager);
  1154. end;
  1155. procedure TgxParticleFXRenderer.UnRegisterAll;
  1156. begin
  1157. while FManagerList.Count > 0 do
  1158. TgxParticleFXManager(FManagerList[FManagerList.Count - 1]).Renderer := nil;
  1159. end;
  1160. // (beware, large and complex stuff below... this is the heart of the ParticleFX)
  1161. procedure TgxParticleFXRenderer.BuildList(var rci: TgxRenderContextInfo);
  1162. (* Quick Explanation of what is below:
  1163. The purpose is to depth-sort a large number (thousandths) of particles and
  1164. render them back to front. The rendering part is not particularly complex,
  1165. it just invokes the various PFX managers involved and request particle
  1166. renderings.
  1167. The sort uses a first-pass region partition (the depth range is split into
  1168. regions, and particles are assigned directly to the region they belong to),
  1169. then each region is sorted with a QuickSort.
  1170. The QuickSort itself is the regular classic variant, but the comparison is
  1171. made on singles as if they were integers, this is allowed by the IEEE format
  1172. in a very efficient manner if all values are superior to 1, which is ensured
  1173. by the distance calculation and a fixed offset of 1.*)
  1174. var
  1175. dist, distDelta, invRegionSize: Single;
  1176. managerIdx, particleIdx, regionIdx: Integer;
  1177. procedure QuickSortRegion(startIndex, endIndex: Integer; region: PPFXRegion);
  1178. var
  1179. I, J: Integer;
  1180. P: Integer;
  1181. poptr: PPointerArray;
  1182. buf: Pointer;
  1183. begin
  1184. if endIndex - startIndex > 1 then
  1185. begin
  1186. poptr := @region^.particleOrder^[0];
  1187. repeat
  1188. I := startIndex;
  1189. J := endIndex;
  1190. P := PParticleReference(poptr^[(I + J) shr 1])^.distance;
  1191. repeat
  1192. while PParticleReference(poptr^[I])^.distance < P do
  1193. Inc(I);
  1194. while PParticleReference(poptr^[J])^.distance > P do
  1195. Dec(J);
  1196. if I <= J then
  1197. begin
  1198. buf := poptr^[I];
  1199. poptr^[I] := poptr^[J];
  1200. poptr^[J] := buf;
  1201. Inc(I);
  1202. Dec(J);
  1203. end;
  1204. until I > J;
  1205. if startIndex < J then
  1206. QuickSortRegion(startIndex, J, region);
  1207. startIndex := I;
  1208. until I >= endIndex;
  1209. end
  1210. else if endIndex - startIndex > 0 then
  1211. begin
  1212. poptr := @region^.particleOrder^[0];
  1213. if PParticleReference(poptr^[endIndex])^.distance < PParticleReference(poptr^[startIndex])^.distance then
  1214. begin
  1215. buf := poptr^[startIndex];
  1216. poptr^[startIndex] := poptr^[endIndex];
  1217. poptr^[endIndex] := buf;
  1218. end;
  1219. end;
  1220. end;
  1221. procedure DistToRegionIdx; register;
  1222. {$IFOPT O-}
  1223. begin
  1224. regionIdx := Trunc((dist - distDelta) * invRegionSize);
  1225. {$ELSE}
  1226. // !! WARNING !! This may cause incorrect behaviour if optimization is turned
  1227. // off for the project.
  1228. asm
  1229. FLD dist
  1230. FSUB distDelta
  1231. FMUL invRegionSize
  1232. FISTP regionIdx
  1233. {$ENDIF}
  1234. end;
  1235. var
  1236. minDist, maxDist, sortMaxRegion: Integer;
  1237. curManager: TgxParticleFXManager;
  1238. curList: PGLParticleArray;
  1239. curParticle: TgxParticle;
  1240. curRegion: PPFXRegion;
  1241. curParticleOrder: PPointerArray;
  1242. cameraPos, cameraVector: TAffineVector;
  1243. timer: Int64;
  1244. currentTexturingMode: Cardinal;
  1245. begin
  1246. if csDesigning in ComponentState then
  1247. Exit;
  1248. timer := StartPrecisionTimer;
  1249. // precalcs
  1250. PSingle(@minDist)^ := rci.rcci.nearClippingDistance + 1;
  1251. if ZMaxDistance <= 0 then
  1252. begin
  1253. PSingle(@maxDist)^ := rci.rcci.farClippingDistance + 1;
  1254. invRegionSize := (cPFXNbRegions - 2) / (rci.rcci.farClippingDistance - rci.rcci.nearClippingDistance);
  1255. end
  1256. else
  1257. begin
  1258. PSingle(@maxDist)^ := rci.rcci.nearClippingDistance + ZMaxDistance + 1;
  1259. invRegionSize := (cPFXNbRegions - 2) / ZMaxDistance;
  1260. end;
  1261. distDelta := rci.rcci.nearClippingDistance + 1 + 0.49999 / invRegionSize;
  1262. SetVector(cameraPos, rci.cameraPosition);
  1263. SetVector(cameraVector, rci.cameraDirection);
  1264. try
  1265. // Collect particles
  1266. // only depth-clipping performed as of now.
  1267. FLastParticleCount := 0;
  1268. for managerIdx := 0 to FManagerList.Count - 1 do
  1269. begin
  1270. curManager := TgxParticleFXManager(FManagerList[managerIdx]);
  1271. curList := curManager.FParticles.List;
  1272. Inc(FLastParticleCount, curManager.ParticleCount);
  1273. for particleIdx := 0 to curManager.ParticleCount - 1 do
  1274. begin
  1275. curParticle := curList^[particleIdx];
  1276. dist := PointProject(curParticle.FPosition, cameraPos, cameraVector) + 1;
  1277. if not FZCull then
  1278. begin
  1279. if PInteger(@dist)^ < minDist then
  1280. PInteger(@dist)^ := minDist;
  1281. end;
  1282. if (PInteger(@dist)^ >= minDist) and (PInteger(@dist)^ <= maxDist) then
  1283. begin
  1284. DistToRegionIdx;
  1285. curRegion := @FRegions[regionIdx];
  1286. // add particle to region
  1287. if curRegion^.count = curRegion^.capacity then
  1288. begin
  1289. Inc(curRegion^.capacity, cPFXGranularity);
  1290. ReallocMem(curRegion^.particleRef, curRegion^.capacity * SizeOf(TParticleReference));
  1291. ReallocMem(curRegion^.particleOrder, curRegion^.capacity * SizeOf(Pointer));
  1292. end;
  1293. with curRegion^.particleRef^[curRegion^.count] do
  1294. begin
  1295. particle := curParticle;
  1296. distance := PInteger(@dist)^;
  1297. end;
  1298. Inc(curRegion^.count);
  1299. end;
  1300. end;
  1301. end;
  1302. // Sort regions
  1303. case ZSortAccuracy of
  1304. saLow: sortMaxRegion := 0;
  1305. saOneTenth: sortMaxRegion := cPFXNbRegions div 10;
  1306. saOneThird: sortMaxRegion := cPFXNbRegions div 3;
  1307. saOneHalf: sortMaxRegion := cPFXNbRegions div 2;
  1308. else
  1309. sortMaxRegion := cPFXNbRegions;
  1310. end;
  1311. for regionIdx := 0 to cPFXNbRegions - 1 do
  1312. begin
  1313. curRegion := @FRegions[regionIdx];
  1314. if curRegion^.count > 1 then
  1315. begin
  1316. // Prepare order table
  1317. with curRegion^ do
  1318. for particleIdx := 0 to count - 1 do
  1319. particleOrder^[particleIdx] := @particleRef[particleIdx];
  1320. // QuickSort
  1321. if (regionIdx < sortMaxRegion) and (FBlendingMode <> bmAdditive) then
  1322. QuickSortRegion(0, curRegion^.count - 1, curRegion);
  1323. end
  1324. else if curRegion^.Count = 1 then
  1325. begin
  1326. // Prepare order table
  1327. curRegion^.particleOrder^[0] := @curRegion^.particleRef[0];
  1328. end;
  1329. end;
  1330. FLastSortTime := StopPrecisionTimer(timer) * 1000;
  1331. rci.PipelineTransformation.Push;
  1332. rci.PipelineTransformation.SetModelMatrix(IdentityHmgMatrix);
  1333. rci.gxStates.Disable(stCullFace);
  1334. rci.gxStates.ActiveTextureEnabled[ttTexture2D] := True;
  1335. currentTexturingMode := 0;
  1336. rci.gxStates.Disable(stLighting);
  1337. rci.gxStates.PolygonMode := pmFill;
  1338. case FBlendingMode of
  1339. bmAdditive:
  1340. begin
  1341. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOne);
  1342. rci.gxStates.Enable(stBlend);
  1343. end;
  1344. bmTransparency:
  1345. begin
  1346. rci.gxStates.SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  1347. rci.gxStates.Enable(stBlend);
  1348. end;
  1349. else
  1350. // bmOpaque, do nothing
  1351. end;
  1352. rci.gxStates.DepthFunc := cfLEqual;
  1353. if not FZWrite then
  1354. begin
  1355. rci.gxStates.DepthWriteMask := False;
  1356. end;
  1357. if not FZTest then
  1358. rci.gxStates.Disable(stDepthTest);
  1359. try
  1360. // Initialize managers
  1361. for managerIdx := 0 to FManagerList.Count - 1 do
  1362. TgxParticleFXManager(FManagerList.Items[managerIdx]).InitializeRendering(rci);
  1363. // Start Rendering... at last ;)
  1364. try
  1365. curManager := nil;
  1366. for regionIdx := cPFXNbRegions - 1 downto 0 do
  1367. begin
  1368. curRegion := @FRegions[regionIdx];
  1369. if curRegion^.count > 0 then
  1370. begin
  1371. curParticleOrder := @curRegion^.particleOrder^[0];
  1372. for particleIdx := curRegion^.count - 1 downto 0 do
  1373. begin
  1374. curParticle := PParticleReference(curParticleOrder^[particleIdx])^.particle;
  1375. if curParticle.Manager <> curManager then
  1376. begin
  1377. if Assigned(curManager) then
  1378. curManager.EndParticles(rci);
  1379. curManager := curParticle.Manager;
  1380. if curManager.TexturingMode <> currentTexturingMode then
  1381. begin
  1382. if currentTexturingMode <> 0 then
  1383. glDisable(currentTexturingMode);
  1384. currentTexturingMode := curManager.TexturingMode;
  1385. if currentTexturingMode <> 0 then
  1386. glEnable(currentTexturingMode);
  1387. end;
  1388. curManager.BeginParticles(rci);
  1389. end;
  1390. curManager.RenderParticle(rci, curParticle);
  1391. end;
  1392. end;
  1393. end;
  1394. if Assigned(curManager) then
  1395. curManager.EndParticles(rci);
  1396. finally
  1397. // Finalize managers
  1398. for managerIdx := 0 to FManagerList.Count - 1 do
  1399. TgxParticleFXManager(FManagerList.Items[managerIdx]).FinalizeRendering(rci);
  1400. end;
  1401. finally
  1402. rci.PipelineTransformation.Pop;
  1403. end;
  1404. rci.gxStates.ActiveTextureEnabled[ttTexture2D] := False;
  1405. rci.gxStates.DepthWriteMask := True;
  1406. finally
  1407. // cleanup
  1408. for regionIdx := cPFXNbRegions - 1 downto 0 do
  1409. FRegions[regionIdx].count := 0;
  1410. end;
  1411. end;
  1412. function TgxParticleFXRenderer.StoreZMaxDistance: Boolean;
  1413. begin
  1414. Result := (FZMaxDistance <> 0);
  1415. end;
  1416. // ------------------
  1417. // ------------------ TgxSourcePFXEffect ------------------
  1418. // ------------------
  1419. constructor TgxSourcePFXEffect.Create(aOwner: TXCollection);
  1420. begin
  1421. inherited;
  1422. FInitialVelocity := TgxCoordinates.CreateInitialized(Self, NullHmgVector, csVector);
  1423. FInitialPosition := TgxCoordinates.CreateInitialized(Self, NullHmgVector, csPoint);
  1424. FPositionDispersionRange := TgxCoordinates.CreateInitialized(Self, XYZHmgVector, csPoint);
  1425. FVelocityDispersion := 0;
  1426. FPositionDispersion := 0;
  1427. FParticleInterval := 0.1;
  1428. FVelocityMode := svmAbsolute;
  1429. FPositionMode := spmAbsoluteOffset;
  1430. FDispersionMode := sdmFast;
  1431. FEnabled := true;
  1432. FDisabledIfOwnerInvisible := False;
  1433. end;
  1434. destructor TgxSourcePFXEffect.Destroy;
  1435. begin
  1436. FPositionDispersionRange.Free;
  1437. FInitialVelocity.Free;
  1438. FInitialPosition.Free;
  1439. inherited Destroy;
  1440. end;
  1441. class function TgxSourcePFXEffect.FriendlyName: string;
  1442. begin
  1443. Result := 'PFX Source';
  1444. end;
  1445. class function TgxSourcePFXEffect.FriendlyDescription: string;
  1446. begin
  1447. Result := 'Simple Particles FX Source';
  1448. end;
  1449. procedure TgxSourcePFXEffect.WriteToFiler(writer: TWriter);
  1450. begin
  1451. inherited;
  1452. with writer do
  1453. begin
  1454. WriteInteger(6); // ArchiveVersion 6, added FPositionMode
  1455. // ArchiveVersion 5, added FDisabledIfOwnerInvisible:
  1456. // ArchiveVersion 4, added FRotationDispersion
  1457. // ArchiveVersion 3, added FEnabled
  1458. // ArchiveVersion 2, added FPositionDispersionRange
  1459. // ArchiveVersion 1, added FDispersionMode
  1460. FInitialVelocity.WriteToFiler(writer);
  1461. FInitialPosition.WriteToFiler(writer);
  1462. FPositionDispersionRange.WriteToFiler(writer);
  1463. WriteFloat(FVelocityDispersion);
  1464. WriteFloat(FPositionDispersion);
  1465. WriteFloat(FParticleInterval);
  1466. WriteInteger(Integer(FVelocityMode));
  1467. WriteInteger(Integer(FDispersionMode));
  1468. WriteBoolean(FEnabled);
  1469. WriteFloat(FRotationDispersion);
  1470. WriteBoolean(FDisabledIfOwnerInvisible);
  1471. WriteInteger(Integer(FPositionMode));
  1472. end;
  1473. end;
  1474. procedure TgxSourcePFXEffect.ReadFromFiler(reader: TReader);
  1475. var
  1476. archiveVersion: Integer;
  1477. begin
  1478. inherited;
  1479. with reader do
  1480. begin
  1481. archiveVersion := ReadInteger;
  1482. Assert(archiveVersion in [0..6]);
  1483. FInitialVelocity.ReadFromFiler(reader);
  1484. FInitialPosition.ReadFromFiler(reader);
  1485. if archiveVersion >= 2 then
  1486. FPositionDispersionRange.ReadFromFiler(reader);
  1487. FVelocityDispersion := ReadFloat;
  1488. FPositionDispersion := ReadFloat;
  1489. FParticleInterval := ReadFloat;
  1490. FVelocityMode := TgxSourcePFXVelocityMode(ReadInteger);
  1491. if archiveVersion >= 1 then
  1492. FDispersionMode := TgxSourcePFXDispersionMode(ReadInteger);
  1493. if archiveVersion >= 3 then
  1494. FEnabled := ReadBoolean;
  1495. if archiveVersion >= 4 then
  1496. FRotationDispersion := ReadFloat;
  1497. if archiveVersion >= 5 then
  1498. FDisabledIfOwnerInvisible := ReadBoolean;
  1499. if archiveVersion >= 6 then
  1500. FPositionMode := TgxSourcePFXPositionMode(ReadInteger);
  1501. end;
  1502. end;
  1503. procedure TgxSourcePFXEffect.SetInitialVelocity(const val: TgxCoordinates);
  1504. begin
  1505. FInitialVelocity.Assign(val);
  1506. end;
  1507. procedure TgxSourcePFXEffect.SetInitialPosition(const val: TgxCoordinates);
  1508. begin
  1509. FInitialPosition.Assign(val);
  1510. end;
  1511. procedure TgxSourcePFXEffect.SetPositionDispersionRange(const val: TgxCoordinates);
  1512. begin
  1513. FPositionDispersionRange.Assign(val);
  1514. end;
  1515. procedure TgxSourcePFXEffect.SetParticleInterval(const val: Single);
  1516. begin
  1517. if FParticleInterval <> val then
  1518. begin
  1519. FParticleInterval := val;
  1520. if FParticleInterval < 0 then
  1521. FParticleInterval := 0;
  1522. if FTimeRemainder > FParticleInterval then
  1523. FTimeRemainder := FParticleInterval;
  1524. end;
  1525. end;
  1526. procedure TgxSourcePFXEffect.DoProgress(const progressTime: TgxProgressTimes);
  1527. var
  1528. n: Integer;
  1529. begin
  1530. if Enabled and Assigned(Manager) and (ParticleInterval > 0) then
  1531. begin
  1532. if OwnerBaseSceneObject.Visible or (not DisabledIfOwnerInvisible) then
  1533. begin
  1534. FTimeRemainder := FTimeRemainder + progressTime.deltaTime;
  1535. if FTimeRemainder > FParticleInterval then
  1536. begin
  1537. n := Trunc((FTimeRemainder - FParticleInterval) / FParticleInterval);
  1538. Burst(progressTime.newTime, n);
  1539. FTimeRemainder := FTimeRemainder - n * FParticleInterval;
  1540. end;
  1541. end;
  1542. end;
  1543. end;
  1544. function TgxSourcePFXEffect.ParticleAbsoluteInitialPos: TAffineVector;
  1545. begin
  1546. if PositionMode = spmRelative then
  1547. begin
  1548. Result := OwnerBaseSceneObject.LocalToAbsolute(InitialPosition.AsAffineVector);
  1549. end
  1550. else
  1551. begin
  1552. SetVector(Result, OwnerBaseSceneObject.AbsolutePosition);
  1553. AddVector(Result, InitialPosition.AsAffineVector);
  1554. end;
  1555. end;
  1556. procedure TgxSourcePFXEffect.Burst(time: Double; nb: Integer);
  1557. var
  1558. particle: TgxParticle;
  1559. av, pos: TAffineVector;
  1560. OwnerObjRelPos: TAffineVector;
  1561. begin
  1562. if Manager = nil then
  1563. Exit;
  1564. OwnerObjRelPos := OwnerBaseSceneObject.LocalToAbsolute(NullVector);
  1565. pos := ParticleAbsoluteInitialPos;
  1566. // if FManager is TgxDynamicPFXManager then
  1567. // TgxDynamicPFXManager(FManager).FRotationCenter := pos;
  1568. while nb > 0 do
  1569. begin
  1570. particle := Manager.CreateParticle;
  1571. particle.FEffectScale := FEffectScale;
  1572. RndVector(DispersionMode, av, FPositionDispersion, FPositionDispersionRange);
  1573. if VelocityMode = svmRelative then
  1574. av := VectorSubtract(OwnerBaseSceneObject.LocalToAbsolute(av), OwnerObjRelPos);
  1575. ScaleVector(av, FEffectScale);
  1576. VectorAdd(pos, av, @particle.Position);
  1577. RndVector(DispersionMode, av, FVelocityDispersion, nil);
  1578. VectorAdd(InitialVelocity.AsAffineVector, av, @particle.Velocity);
  1579. particle.Velocity := VectorScale(particle.Velocity, FEffectScale);
  1580. if VelocityMode = svmRelative then
  1581. particle.FVelocity := VectorSubtract(OwnerBaseSceneObject.LocalToAbsolute(particle.FVelocity), OwnerObjRelPos);
  1582. particle.CreationTime := time;
  1583. if FRotationDispersion <> 0 then
  1584. particle.FRotation := Random * FRotationDispersion
  1585. else
  1586. particle.FRotation := 0;
  1587. Dec(nb);
  1588. end;
  1589. end;
  1590. procedure TgxSourcePFXEffect.RingExplosion(time: Double;
  1591. minInitialSpeed, maxInitialSpeed: Single;
  1592. nbParticles: Integer);
  1593. var
  1594. particle: TgxParticle;
  1595. av, pos, tmp: TAffineVector;
  1596. ringVectorX, ringVectorY: TAffineVector;
  1597. fx, fy, d: Single;
  1598. begin
  1599. if (Manager = nil) or (nbParticles <= 0) then
  1600. Exit;
  1601. pos := ParticleAbsoluteInitialPos;
  1602. SetVector(ringVectorY, OwnerBaseSceneObject.AbsoluteUp);
  1603. SetVector(ringVectorX, OwnerBaseSceneObject.AbsoluteDirection);
  1604. ringVectorY := VectorCrossProduct(ringVectorX, ringVectorY);
  1605. while (nbParticles > 0) do
  1606. begin
  1607. // okay, ain't exactly an "isotropic" ring...
  1608. fx := Random - 0.5;
  1609. fy := Random - 0.5;
  1610. d := RLength(fx, fy);
  1611. tmp := VectorCombine(ringVectorX, ringVectorY, fx * d, fy * d);
  1612. ScaleVector(tmp, minInitialSpeed + Random * (maxInitialSpeed - minInitialSpeed));
  1613. AddVector(tmp, InitialVelocity.AsVector);
  1614. particle := Manager.CreateParticle;
  1615. with particle do
  1616. begin
  1617. RndVector(DispersionMode, av, FPositionDispersion, FPositionDispersionRange);
  1618. VectorAdd(pos, av, @Position);
  1619. RndVector(DispersionMode, av, FVelocityDispersion, nil);
  1620. VectorAdd(tmp, av, @Velocity);
  1621. if VelocityMode = svmRelative then
  1622. Velocity := OwnerBaseSceneObject.LocalToAbsolute(Velocity);
  1623. particle.CreationTime := time;
  1624. end;
  1625. Dec(nbParticles);
  1626. end;
  1627. end;
  1628. // ------------------
  1629. // ------------------ TPFXLifeColor ------------------
  1630. // ------------------
  1631. constructor TPFXLifeColor.Create(Collection: TCollection);
  1632. begin
  1633. inherited Create(Collection);
  1634. FColorInner := TgxColor.CreateInitialized(Self, NullHmgVector);
  1635. FColorOuter := TgxColor.CreateInitialized(Self, NullHmgVector);
  1636. FLifeTime := 1;
  1637. FInvLifeTime := 1;
  1638. FSizeScale := 1;
  1639. FRotateAngle := 0;
  1640. end;
  1641. destructor TPFXLifeColor.Destroy;
  1642. begin
  1643. FColorOuter.Free;
  1644. FColorInner.Free;
  1645. inherited Destroy;
  1646. end;
  1647. procedure TPFXLifeColor.Assign(Source: TPersistent);
  1648. begin
  1649. if Source is TPFXLifeColor then
  1650. begin
  1651. FColorInner.Assign(TPFXLifeColor(Source).ColorInner);
  1652. FColorOuter.Assign(TPFXLifeColor(Source).ColorOuter);
  1653. FLifeTime := TPFXLifeColor(Source).LifeTime;
  1654. FRotateAngle := TPFXLifeColor(Source).RotateAngle;
  1655. end
  1656. else
  1657. inherited;
  1658. end;
  1659. function TPFXLifeColor.GetDisplayName: string;
  1660. begin
  1661. Result := Format('LifeTime %f - Inner [%.2f, %.2f, %.2f, %.2f] - Outer [%.2f, %.2f, %.2f, %.2f]',
  1662. [LifeTime,
  1663. ColorInner.Red, ColorInner.Green, ColorInner.Blue, ColorInner.Alpha,
  1664. ColorOuter.Red, ColorOuter.Green, ColorOuter.Blue, ColorOuter.Alpha]);
  1665. end;
  1666. procedure TPFXLifeColor.SetColorInner(const val: TgxColor);
  1667. begin
  1668. FColorInner.Assign(val);
  1669. end;
  1670. procedure TPFXLifeColor.SetColorOuter(const val: TgxColor);
  1671. begin
  1672. FColorOuter.Assign(val);
  1673. end;
  1674. procedure TPFXLifeColor.SetLifeTime(const val: Single);
  1675. begin
  1676. if FLifeTime <> val then
  1677. begin
  1678. FLifeTime := val;
  1679. if FLifeTime <= 0 then
  1680. FLifeTime := 1e-6;
  1681. FInvLifeTime := 1 / FLifeTime;
  1682. end;
  1683. end;
  1684. procedure TPFXLifeColor.SetSizeScale(const val: Single);
  1685. begin
  1686. if FSizeScale <> val then
  1687. begin
  1688. FSizeScale := val;
  1689. FDoScale := (FSizeScale <> 1);
  1690. end;
  1691. end;
  1692. procedure TPFXLifeColor.SetRotateAngle(const Value: Single);
  1693. begin
  1694. if FRotateAngle <> Value then
  1695. begin
  1696. FRotateAngle := Value;
  1697. FDoRotate := (FRotateAngle <> 0);
  1698. end;
  1699. end;
  1700. // ------------------
  1701. // ------------------ TPFXLifeColors ------------------
  1702. // ------------------
  1703. constructor TPFXLifeColors.Create(AOwner: TPersistent);
  1704. begin
  1705. inherited Create(AOwner, TPFXLifeColor);
  1706. end;
  1707. procedure TPFXLifeColors.SetItems(index: Integer; const val: TPFXLifeColor);
  1708. begin
  1709. inherited Items[index] := val;
  1710. end;
  1711. function TPFXLifeColors.GetItems(index: Integer): TPFXLifeColor;
  1712. begin
  1713. Result := TPFXLifeColor(inherited Items[index]);
  1714. end;
  1715. function TPFXLifeColors.Add: TPFXLifeColor;
  1716. begin
  1717. Result := (inherited Add) as TPFXLifeColor;
  1718. end;
  1719. function TPFXLifeColors.FindItemID(ID: Integer): TPFXLifeColor;
  1720. begin
  1721. Result := (inherited FindItemID(ID)) as TPFXLifeColor;
  1722. end;
  1723. function TPFXLifeColors.MaxLifeTime: Double;
  1724. begin
  1725. if Count > 0 then
  1726. Result := Items[Count - 1].LifeTime
  1727. else
  1728. Result := 1e30;
  1729. end;
  1730. function TPFXLifeColors.RotationsDefined: Boolean;
  1731. var
  1732. i: Integer;
  1733. begin
  1734. for i := 0 to Count - 1 do
  1735. begin
  1736. if Items[i].RotateAngle <> 0 then
  1737. begin
  1738. Result := True;
  1739. Exit;
  1740. end;
  1741. end;
  1742. Result := False;
  1743. end;
  1744. function TPFXLifeColors.ScalingDefined: Boolean;
  1745. var
  1746. i: Integer;
  1747. begin
  1748. for i := 0 to Count - 1 do
  1749. begin
  1750. if Items[i].SizeScale <> 1 then
  1751. begin
  1752. Result := True;
  1753. Exit;
  1754. end;
  1755. end;
  1756. Result := False;
  1757. end;
  1758. procedure TPFXLifeColors.PrepareIntervalRatios;
  1759. var
  1760. i: Integer;
  1761. begin
  1762. for i := 0 to Count - 2 do
  1763. Items[i].FIntervalRatio := 1 / (Items[i + 1].LifeTime - Items[i].LifeTime);
  1764. end;
  1765. // ------------------
  1766. // ------------------ TgxDynamicPFXManager ------------------
  1767. // ------------------
  1768. constructor TgxDynamicPFXManager.Create(aOwner: TComponent);
  1769. begin
  1770. inherited;
  1771. FAcceleration := TgxCoordinates.CreateInitialized(Self, NullHmgVector, csVector);
  1772. FFriction := 1;
  1773. end;
  1774. destructor TgxDynamicPFXManager.Destroy;
  1775. begin
  1776. FAcceleration.Free;
  1777. inherited Destroy;
  1778. end;
  1779. procedure TgxDynamicPFXManager.DoProgress(const progressTime: TgxProgressTimes);
  1780. var
  1781. i: Integer;
  1782. curParticle: TgxParticle;
  1783. maxAge: Double;
  1784. {pos, pos1, axis,}accelVector: TAffineVector;
  1785. {ff,}dt: Single;
  1786. list: PGLParticleArray;
  1787. doFriction, doPack: Boolean;
  1788. frictionScale: Single;
  1789. //pos4: TVector4f;
  1790. begin
  1791. maxAge := MaxParticleAge;
  1792. accelVector := Acceleration.AsAffineVector;
  1793. dt := progressTime.deltaTime;
  1794. doFriction := (FFriction <> 1);
  1795. if doFriction then
  1796. begin
  1797. frictionScale := Power(FFriction, dt)
  1798. end
  1799. else
  1800. frictionScale := 1;
  1801. FCurrentTime := progressTime.newTime;
  1802. doPack := False;
  1803. list := Particles.List;
  1804. for i := 0 to Particles.ItemCount - 1 do
  1805. begin
  1806. curParticle := list^[i];
  1807. if (progressTime.newTime - curParticle.CreationTime) < maxAge then
  1808. begin
  1809. // particle alive, just update velocity and position
  1810. with curParticle do
  1811. begin
  1812. CombineVector(FPosition, FVelocity, dt);
  1813. // DanB - this doesn't seem to fit here, rotation is already
  1814. // calculated when rendering
  1815. {if (FRotation <> 0) and (Renderer <> nil) then begin
  1816. pos := FPosition;
  1817. pos1 := FPosition;
  1818. ff := 1;
  1819. CombineVector(pos1, FVelocity, ff);
  1820. SetVector(axis, Renderer.Scene.CurrentGLCamera.AbsolutePosition);
  1821. SetVector(axis, VectorSubtract(axis, FRotationCenter));
  1822. NormalizeVector(axis);
  1823. MakeVector(pos4, pos1);
  1824. pos4[0] := pos4[0] - FRotationCenter[0];
  1825. pos4[1] := pos4[1] - FRotationCenter[1];
  1826. pos4[2] := pos4[2] - FRotationCenter[2];
  1827. RotateVector(pos4, axis, FRotation * dt);
  1828. pos4[0] := pos4[0] + FRotationCenter[0];
  1829. pos4[1] := pos4[1] + FRotationCenter[1];
  1830. pos4[2] := pos4[2] + FRotationCenter[2];
  1831. MakeVector(pos1, pos4[0], pos4[1], pos4[2]);
  1832. FVelocity := VectorSubtract(pos1, pos);
  1833. CombineVector(FPosition, FVelocity, dt);
  1834. end;}
  1835. CombineVector(FVelocity, accelVector, dt);
  1836. if doFriction then
  1837. ScaleVector(FVelocity, frictionScale);
  1838. end;
  1839. end
  1840. else
  1841. begin
  1842. // kill particle
  1843. curParticle.Free;
  1844. list^[i] := nil;
  1845. doPack := True;
  1846. end;
  1847. end;
  1848. if doPack then
  1849. Particles.Pack;
  1850. end;
  1851. procedure TgxDynamicPFXManager.SetAcceleration(const val: TgxCoordinates);
  1852. begin
  1853. FAcceleration.Assign(val);
  1854. end;
  1855. // ------------------
  1856. // ------------------ TgxLifeColoredPFXManager ------------------
  1857. // ------------------
  1858. constructor TgxLifeColoredPFXManager.Create(aOwner: TComponent);
  1859. begin
  1860. inherited;
  1861. FLifeColors := TPFXLifeColors.Create(Self);
  1862. FColorInner := TgxColor.CreateInitialized(Self, clrYellow);
  1863. FColorOuter := TgxColor.CreateInitialized(Self, NullHmgVector);
  1864. with FLifeColors.Add do
  1865. begin
  1866. LifeTime := 3;
  1867. end;
  1868. FParticleSize := 1;
  1869. end;
  1870. destructor TgxLifeColoredPFXManager.Destroy;
  1871. begin
  1872. FLifeColors.Free;
  1873. FColorInner.Free;
  1874. FColorOuter.Free;
  1875. inherited Destroy;
  1876. end;
  1877. procedure TgxLifeColoredPFXManager.SetColorInner(const val: TgxColor);
  1878. begin
  1879. FColorInner.Assign(val);
  1880. end;
  1881. procedure TgxLifeColoredPFXManager.SetColorOuter(const val: TgxColor);
  1882. begin
  1883. FColorOuter.Assign(val);
  1884. end;
  1885. procedure TgxLifeColoredPFXManager.SetLifeColors(const val: TPFXLifeColors);
  1886. begin
  1887. FLifeColors.Assign(Self);
  1888. end;
  1889. procedure TgxLifeColoredPFXManager.InitializeRendering(var rci: TgxRenderContextInfo);
  1890. var
  1891. i, n: Integer;
  1892. begin
  1893. n := LifeColors.Count;
  1894. FLifeColorsLookup := TList.Create;
  1895. FLifeColorsLookup.Capacity := n;
  1896. for i := 0 to n - 1 do
  1897. FLifeColorsLookup.Add(LifeColors[i]);
  1898. FLifeRotations := LifeColors.RotationsDefined;
  1899. FLifeScaling := LifeColors.ScalingDefined;
  1900. LifeColors.PrepareIntervalRatios;
  1901. end;
  1902. procedure TgxLifeColoredPFXManager.FinalizeRendering(var rci: TgxRenderContextInfo);
  1903. begin
  1904. FLifeColorsLookup.Free;
  1905. end;
  1906. function TgxLifeColoredPFXManager.MaxParticleAge: Single;
  1907. begin
  1908. Result := LifeColors.MaxLifeTime;
  1909. end;
  1910. procedure TgxLifeColoredPFXManager.ComputeColors(var lifeTime: Single; var inner, outer: TgxColorVector);
  1911. var
  1912. i, k, n: Integer;
  1913. f: Single;
  1914. lck, lck1: TPFXLifeColor;
  1915. begin
  1916. with LifeColors do
  1917. begin
  1918. n := Count - 1;
  1919. if n < 0 then
  1920. begin
  1921. inner := ColorInner.Color;
  1922. outer := ColorOuter.Color;
  1923. end
  1924. else
  1925. begin
  1926. if n > 0 then
  1927. begin
  1928. k := -1;
  1929. for i := 0 to n do
  1930. if TPFXLifeColor(FLifeColorsLookup.Items[i]).LifeTime < lifeTime then
  1931. k := i;
  1932. if k < n then
  1933. Inc(k);
  1934. end
  1935. else
  1936. k := 0;
  1937. case k of
  1938. 0:
  1939. begin
  1940. lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
  1941. f := lifeTime * lck.InvLifeTime;
  1942. VectorLerp(ColorInner.Color, lck.ColorInner.Color, f, inner);
  1943. VectorLerp(ColorOuter.Color, lck.ColorOuter.Color, f, outer);
  1944. end;
  1945. else
  1946. lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
  1947. lck1 := TPFXLifeColor(FLifeColorsLookup.Items[k - 1]);
  1948. f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
  1949. VectorLerp(lck1.ColorInner.Color, lck.ColorInner.Color, f, inner);
  1950. VectorLerp(lck1.ColorOuter.Color, lck.ColorOuter.Color, f, outer);
  1951. end;
  1952. end;
  1953. end;
  1954. end;
  1955. procedure TgxLifeColoredPFXManager.ComputeInnerColor(var lifeTime: Single; var inner: TgxColorVector);
  1956. var
  1957. i, k, n: Integer;
  1958. f: Single;
  1959. lck, lck1: TPFXLifeColor;
  1960. lifeColorsLookupList: PFXPointerList;
  1961. begin
  1962. with LifeColors do
  1963. begin
  1964. n := Count - 1;
  1965. if n < 0 then
  1966. inner := ColorInner.Color
  1967. else
  1968. begin
  1969. lifeColorsLookupList := @FLifeColorsLookup.List[0];
  1970. if n > 0 then
  1971. begin
  1972. k := -1;
  1973. for i := 0 to n do
  1974. if TPFXLifeColor(lifeColorsLookupList^[i]).LifeTime < lifeTime then
  1975. k := i;
  1976. if k < n then
  1977. Inc(k);
  1978. end
  1979. else
  1980. k := 0;
  1981. if k = 0 then
  1982. begin
  1983. lck := TPFXLifeColor(lifeColorsLookupList^[k]);
  1984. f := lifeTime * lck.InvLifeTime;
  1985. VectorLerp(ColorInner.Color, lck.ColorInner.Color, f, inner);
  1986. end
  1987. else
  1988. begin
  1989. lck := TPFXLifeColor(lifeColorsLookupList^[k]);
  1990. lck1 := TPFXLifeColor(lifeColorsLookupList^[k - 1]);
  1991. f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
  1992. VectorLerp(lck1.ColorInner.Color, lck.ColorInner.Color, f, inner);
  1993. end;
  1994. end;
  1995. end;
  1996. end;
  1997. procedure TgxLifeColoredPFXManager.ComputeOuterColor(var lifeTime: Single; var outer: TgxColorVector);
  1998. var
  1999. i, k, n: Integer;
  2000. f: Single;
  2001. lck, lck1: TPFXLifeColor;
  2002. begin
  2003. with LifeColors do
  2004. begin
  2005. n := Count - 1;
  2006. if n < 0 then
  2007. outer := ColorOuter.Color
  2008. else
  2009. begin
  2010. if n > 0 then
  2011. begin
  2012. k := -1;
  2013. for i := 0 to n do
  2014. if TPFXLifeColor(FLifeColorsLookup.Items[i]).LifeTime < lifeTime then
  2015. k := i;
  2016. if k < n then
  2017. Inc(k);
  2018. end
  2019. else
  2020. k := 0;
  2021. case k of
  2022. 0:
  2023. begin
  2024. lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
  2025. f := lifeTime * lck.InvLifeTime;
  2026. VectorLerp(ColorOuter.Color, lck.ColorOuter.Color, f, outer);
  2027. end;
  2028. else
  2029. lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
  2030. lck1 := TPFXLifeColor(FLifeColorsLookup.Items[k - 1]);
  2031. f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
  2032. VectorLerp(lck1.ColorOuter.Color, lck.ColorOuter.Color, f, outer);
  2033. end;
  2034. end;
  2035. end;
  2036. end;
  2037. function TgxLifeColoredPFXManager.ComputeSizeScale(var lifeTime: Single; var sizeScale: Single): Boolean;
  2038. var
  2039. i, k, n: Integer;
  2040. f: Single;
  2041. lck, lck1: TPFXLifeColor;
  2042. begin
  2043. with LifeColors do
  2044. begin
  2045. n := Count - 1;
  2046. if n < 0 then
  2047. Result := False
  2048. else
  2049. begin
  2050. if n > 0 then
  2051. begin
  2052. k := -1;
  2053. for i := 0 to n do
  2054. if TPFXLifeColor(FLifeColorsLookup.Items[i]).LifeTime < lifeTime then
  2055. k := i;
  2056. if k < n then
  2057. Inc(k);
  2058. end
  2059. else
  2060. k := 0;
  2061. case k of
  2062. 0:
  2063. begin
  2064. lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
  2065. Result := lck.FDoScale;
  2066. if Result then
  2067. begin
  2068. f := lifeTime * lck.InvLifeTime;
  2069. sizeScale := Lerp(1, lck.SizeScale, f);
  2070. end;
  2071. end;
  2072. else
  2073. lck := TPFXLifeColor(FLifeColorsLookup.Items[k]);
  2074. lck1 := TPFXLifeColor(FLifeColorsLookup.Items[k - 1]);
  2075. Result := lck.FDoScale or lck1.FDoScale;
  2076. if Result then
  2077. begin
  2078. f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
  2079. sizeScale := Lerp(lck1.SizeScale, lck.SizeScale, f);
  2080. end;
  2081. end;
  2082. end;
  2083. end;
  2084. end;
  2085. function TgxLifeColoredPFXManager.ComputeRotateAngle(var lifeTime: Single; var rotateAngle: Single): Boolean;
  2086. var
  2087. i, k, n: Integer;
  2088. f: Single;
  2089. lck, lck1: TPFXLifeColor;
  2090. begin
  2091. with LifeColors do
  2092. begin
  2093. n := Count - 1;
  2094. if n < 0 then
  2095. Result := False
  2096. else
  2097. begin
  2098. if n > 0 then
  2099. begin
  2100. k := -1;
  2101. for i := 0 to n do
  2102. if Items[i].LifeTime < lifeTime then
  2103. k := i;
  2104. if k < n then
  2105. Inc(k);
  2106. end
  2107. else
  2108. k := 0;
  2109. case k of
  2110. 0:
  2111. begin
  2112. lck := LifeColors[k];
  2113. Result := lck.FDoRotate;
  2114. if Result then
  2115. begin
  2116. f := lifeTime * lck.InvLifeTime;
  2117. rotateAngle := Lerp(1, lck.rotateAngle, f);
  2118. end;
  2119. end;
  2120. else
  2121. lck := LifeColors[k];
  2122. lck1 := LifeColors[k - 1];
  2123. Result := lck.FDoRotate or lck1.FDoRotate;
  2124. if Result then
  2125. begin
  2126. f := (lifeTime - lck1.LifeTime) * lck1.InvIntervalRatio;
  2127. rotateAngle := Lerp(lck1.rotateAngle, lck.rotateAngle, f);
  2128. end;
  2129. end;
  2130. end;
  2131. end;
  2132. end;
  2133. procedure TgxLifeColoredPFXManager.RotateVertexBuf(buf: TgxAffineVectorList;
  2134. lifeTime: Single; const axis: TAffineVector; offsetAngle: Single);
  2135. var
  2136. rotateAngle: Single;
  2137. rotMatrix: TMatrix4f;
  2138. diff: Single;
  2139. lifeRotationApplied: Boolean;
  2140. begin
  2141. rotateAngle := 0;
  2142. lifeRotationApplied := ComputeRotateAngle(lifeTime, rotateAngle);
  2143. rotateAngle := rotateAngle + offsetAngle;
  2144. if lifeRotationApplied or (rotateAngle <> 0) then
  2145. begin
  2146. diff := DegToRad(rotateAngle);
  2147. rotMatrix := CreateRotationMatrix(axis, diff);
  2148. buf.TransformAsVectors(rotMatrix);
  2149. end;
  2150. end;
  2151. // ------------------
  2152. // ------------------ TgxCustomPFXManager ------------------
  2153. // ------------------
  2154. procedure TgxCustomPFXManager.DoProgress(const progressTime: TgxProgressTimes);
  2155. var
  2156. i: Integer;
  2157. list: PGLParticleArray;
  2158. curParticle: TgxParticle;
  2159. defaultProgress, killParticle, doPack: Boolean;
  2160. begin
  2161. if Assigned(FOnProgress) then
  2162. begin
  2163. defaultProgress := False;
  2164. FOnProgress(Self, progressTime, defaultProgress);
  2165. if defaultProgress then
  2166. inherited;
  2167. end
  2168. else
  2169. inherited;
  2170. if Assigned(FOnParticleProgress) then
  2171. begin
  2172. doPack := False;
  2173. list := Particles.List;
  2174. for i := 0 to Particles.ItemCount - 1 do
  2175. begin
  2176. killParticle := True;
  2177. curParticle := list^[i];
  2178. FOnParticleProgress(Self, progressTime, curParticle, killParticle);
  2179. if killParticle then
  2180. begin
  2181. curParticle.Free;
  2182. list^[i] := nil;
  2183. doPack := True;
  2184. end;
  2185. end;
  2186. if doPack then
  2187. Particles.Pack;
  2188. end;
  2189. end;
  2190. function TgxCustomPFXManager.TexturingMode: Cardinal;
  2191. begin
  2192. Result := 0;
  2193. end;
  2194. procedure TgxCustomPFXManager.InitializeRendering(var rci: TgxRenderContextInfo);
  2195. begin
  2196. inherited;
  2197. if Assigned(FOnInitializeRendering) then
  2198. FOnInitializeRendering(Self, rci);
  2199. end;
  2200. procedure TgxCustomPFXManager.BeginParticles(var rci: TgxRenderContextInfo);
  2201. begin
  2202. if Assigned(FOnBeginParticles) then
  2203. FOnBeginParticles(Self, rci);
  2204. end;
  2205. procedure TgxCustomPFXManager.RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle);
  2206. begin
  2207. if Assigned(FOnRenderParticle) then
  2208. FOnRenderParticle(Self, aParticle, rci);
  2209. end;
  2210. procedure TgxCustomPFXManager.EndParticles(var rci: TgxRenderContextInfo);
  2211. begin
  2212. if Assigned(FOnEndParticles) then
  2213. FOnEndParticles(Self, rci);
  2214. end;
  2215. procedure TgxCustomPFXManager.FinalizeRendering(var rci: TgxRenderContextInfo);
  2216. begin
  2217. if Assigned(FOnFinalizeRendering) then
  2218. FOnFinalizeRendering(Self, rci);
  2219. inherited;
  2220. end;
  2221. function TgxCustomPFXManager.ParticleCount: Integer;
  2222. begin
  2223. if Assigned(FOnGetParticleCountEvent) then
  2224. Result := FOnGetParticleCountEvent(Self)
  2225. else
  2226. Result := FParticles.FItemList.Count;
  2227. end;
  2228. // ------------------
  2229. // ------------------ TgxPolygonPFXManager ------------------
  2230. // ------------------
  2231. constructor TgxPolygonPFXManager.Create(aOwner: TComponent);
  2232. begin
  2233. inherited;
  2234. FNbSides := 6;
  2235. end;
  2236. destructor TgxPolygonPFXManager.Destroy;
  2237. begin
  2238. inherited Destroy;
  2239. end;
  2240. procedure TgxPolygonPFXManager.SetNbSides(const val: Integer);
  2241. begin
  2242. if val <> FNbSides then
  2243. begin
  2244. FNbSides := val;
  2245. if FNbSides < 3 then
  2246. FNbSides := 3;
  2247. NotifyChange(Self);
  2248. end;
  2249. end;
  2250. function TgxPolygonPFXManager.TexturingMode: Cardinal;
  2251. begin
  2252. Result := 0;
  2253. end;
  2254. procedure TgxPolygonPFXManager.InitializeRendering(var rci: TgxRenderContextInfo);
  2255. var
  2256. i: Integer;
  2257. matrix: TMatrix4f;
  2258. s, c: Single;
  2259. begin
  2260. inherited;
  2261. glGetFloatv(GL_MODELVIEW_MATRIX, @matrix);
  2262. for i := 0 to 2 do
  2263. begin
  2264. Fvx.V[i] := matrix.V[i].X * FParticleSize;
  2265. Fvy.V[i] := matrix.V[i].Y * FParticleSize;
  2266. end;
  2267. FVertices := TgxAffineVectorList.Create;
  2268. FVertices.Capacity := FNbSides;
  2269. for i := 0 to FNbSides - 1 do
  2270. begin
  2271. SinCos(i * c2PI / FNbSides, s, c);
  2272. FVertices.Add(VectorCombine(FVx, Fvy, c, s));
  2273. end;
  2274. FVertBuf := TgxAffineVectorList.Create;
  2275. FVertBuf.Count := FVertices.Count;
  2276. end;
  2277. procedure TgxPolygonPFXManager.BeginParticles(var rci: TgxRenderContextInfo);
  2278. begin
  2279. ApplyBlendingMode(rci);
  2280. end;
  2281. procedure TgxPolygonPFXManager.RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle);
  2282. var
  2283. i: Integer;
  2284. lifeTime, sizeScale: Single;
  2285. inner, outer: TgxColorVector;
  2286. pos: TAffineVector;
  2287. vertexList: PAffineVectorArray;
  2288. begin
  2289. lifeTime := FCurrentTime - aParticle.CreationTime;
  2290. ComputeColors(lifeTime, inner, outer);
  2291. pos := aParticle.Position;
  2292. vertexList := FVertBuf.List;
  2293. // copy vertices
  2294. for I := 0 to FVertBuf.Count - 1 do
  2295. vertexList[i] := FVertices[i];
  2296. // rotate vertices (if needed)
  2297. if FLifeRotations or (aParticle.FRotation <> 0) then
  2298. RotateVertexBuf(FVertBuf, lifeTime, AffineVectorMake(rci.cameraDirection), aParticle.FRotation);
  2299. // scale vertices (if needed) then translate to particle position
  2300. if FLifeScaling or (aParticle.FEffectScale <> 1) then
  2301. begin
  2302. if FLifeScaling and ComputeSizeScale(lifeTime, sizeScale) then
  2303. sizeScale := sizeScale * aParticle.FEffectScale
  2304. else
  2305. sizeScale := aParticle.FEffectScale;
  2306. for i := 0 to FVertBuf.Count - 1 do
  2307. vertexList^[i] := VectorCombine(vertexList^[i], pos, sizeScale, 1);
  2308. end
  2309. else
  2310. FVertBuf.Translate(pos);
  2311. glBegin(GL_TRIANGLE_FAN);
  2312. glColor4fv(@inner);
  2313. glVertex3fv(@pos);
  2314. glColor4fv(@outer);
  2315. for i := 0 to FVertBuf.Count - 1 do
  2316. glVertex3fv(@vertexList[i]);
  2317. glVertex3fv(@vertexList[0]);
  2318. glEnd;
  2319. end;
  2320. procedure TgxPolygonPFXManager.EndParticles(var rci: TgxRenderContextInfo);
  2321. begin
  2322. UnapplyBlendingMode(rci);
  2323. end;
  2324. procedure TgxPolygonPFXManager.FinalizeRendering(var rci: TgxRenderContextInfo);
  2325. begin
  2326. FVertBuf.Free;
  2327. FVertices.Free;
  2328. inherited;
  2329. end;
  2330. // ------------------
  2331. // ------------------ TgxBaseSpritePFXManager ------------------
  2332. // ------------------
  2333. constructor TgxBaseSpritePFXManager.Create(aOwner: TComponent);
  2334. begin
  2335. inherited;
  2336. FTexHandle := TgxTextureHandle.Create;
  2337. FSpritesPerTexture := sptOne;
  2338. FAspectRatio := 1;
  2339. end;
  2340. destructor TgxBaseSpritePFXManager.Destroy;
  2341. begin
  2342. FTexHandle.Free;
  2343. FShareSprites := nil;
  2344. inherited Destroy;
  2345. end;
  2346. procedure TgxBaseSpritePFXManager.SetSpritesPerTexture(const val: TSpritesPerTexture);
  2347. begin
  2348. if val <> FSpritesPerTexture then
  2349. begin
  2350. FSpritesPerTexture := val;
  2351. FTexHandle.DestroyHandle;
  2352. NotifyChange(Self);
  2353. end;
  2354. end;
  2355. procedure TgxBaseSpritePFXManager.SetColorMode(const val: TSpriteColorMode);
  2356. begin
  2357. if val <> FColorMode then
  2358. begin
  2359. FColorMode := val;
  2360. NotifyChange(Self);
  2361. end;
  2362. end;
  2363. procedure TgxBaseSpritePFXManager.SetAspectRatio(const val: Single);
  2364. begin
  2365. if FAspectRatio <> val then
  2366. begin
  2367. FAspectRatio := ClampValue(val, 1e-3, 1e3);
  2368. NotifyChange(Self);
  2369. end;
  2370. end;
  2371. function TgxBaseSpritePFXManager.StoreAspectRatio: Boolean;
  2372. begin
  2373. Result := (FAspectRatio <> 1);
  2374. end;
  2375. procedure TgxBaseSpritePFXManager.SetRotation(const val: Single);
  2376. begin
  2377. if FRotation <> val then
  2378. begin
  2379. FRotation := val;
  2380. NotifyChange(Self);
  2381. end;
  2382. end;
  2383. procedure TgxBaseSpritePFXManager.SetShareSprites(const val: TgxBaseSpritePFXManager);
  2384. begin
  2385. if FShareSprites <> val then
  2386. begin
  2387. if Assigned(FShareSprites) then
  2388. FShareSprites.RemoveFreeNotification(Self);
  2389. FShareSprites := val;
  2390. if Assigned(FShareSprites) then
  2391. FShareSprites.FreeNotification(Self);
  2392. end;
  2393. end;
  2394. procedure TgxBaseSpritePFXManager.BindTexture(var rci: TgxRenderContextInfo);
  2395. var
  2396. bmp32: TgxBitmap32;
  2397. tw, th, td, tf: Integer;
  2398. begin
  2399. if Assigned(FShareSprites) then
  2400. FShareSprites.BindTexture(rci)
  2401. else
  2402. begin
  2403. if FTexHandle.Handle = 0 then
  2404. begin
  2405. FTexHandle.AllocateHandle;
  2406. FTexHandle.Target := ttTexture2D;
  2407. rci.gxStates.TextureBinding[0, ttTexture2D] := FTexHandle.Handle;
  2408. glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  2409. rci.gxStates.UnpackAlignment := 4;
  2410. rci.gxStates.UnpackRowLength := 0;
  2411. rci.gxStates.UnpackSkipRows := 0;
  2412. rci.gxStates.UnpackSkipPixels := 0;
  2413. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  2414. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  2415. bmp32 := TgxBitmap32.Create;
  2416. try
  2417. tf := GL_RGBA;
  2418. PrepareImage(bmp32, tf);
  2419. bmp32.RegisterAsOpenRXTexture(
  2420. FTexHandle,
  2421. True,
  2422. tf, tw, th, td);
  2423. finally
  2424. bmp32.Free;
  2425. end;
  2426. end
  2427. else
  2428. begin
  2429. rci.gxStates.TextureBinding[0, ttTexture2D] := FTexHandle.Handle;
  2430. end;
  2431. end;
  2432. end;
  2433. function TgxBaseSpritePFXManager.TexturingMode: Cardinal;
  2434. begin
  2435. Result := GL_TEXTURE_2D;
  2436. end;
  2437. procedure TgxBaseSpritePFXManager.InitializeRendering(var rci: TgxRenderContextInfo);
  2438. var
  2439. i: Integer;
  2440. matrix: TMatrix4f;
  2441. s, c, w, h: Single;
  2442. begin
  2443. inherited;
  2444. glGetFloatv(GL_MODELVIEW_MATRIX, @matrix);
  2445. w := FParticleSize * Sqrt(FAspectRatio);
  2446. h := Sqr(FParticleSize) / w;
  2447. for i := 0 to 2 do
  2448. begin
  2449. Fvx.V[i] := matrix.V[i].X * w;
  2450. Fvy.V[i] := matrix.V[i].Y * h;
  2451. Fvz.V[i] := matrix.V[i].Z;
  2452. end;
  2453. FVertices := TgxAffineVectorList.Create;
  2454. for i := 0 to 3 do
  2455. begin
  2456. SinCos(i * cPIdiv2 + cPIdiv4, s, c);
  2457. FVertices.Add(VectorCombine(Fvx, Fvy, c, s));
  2458. end;
  2459. if FRotation <> 0 then
  2460. begin
  2461. matrix := CreateRotationMatrix(Fvz, -FRotation);
  2462. FVertices.TransformAsPoints(matrix);
  2463. end;
  2464. FVertBuf := TgxAffineVectorList.Create;
  2465. FVertBuf.Count := FVertices.Count;
  2466. end;
  2467. procedure TgxBaseSpritePFXManager.BeginParticles(var rci: TgxRenderContextInfo);
  2468. begin
  2469. BindTexture(rci);
  2470. if ColorMode = scmNone then
  2471. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
  2472. else
  2473. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  2474. ApplyBlendingMode(rci);
  2475. if ColorMode <> scmFade then
  2476. glBegin(GL_QUADS);
  2477. end;
  2478. procedure TgxBaseSpritePFXManager.RenderParticle(var rci: TgxRenderContextInfo; aParticle: TgxParticle);
  2479. type
  2480. TTexCoordsSet = array[0..3] of TTexPoint;
  2481. PTexCoordsSet = ^TTexCoordsSet;
  2482. const
  2483. cBaseTexCoordsSet: TTexCoordsSet = ((S: 1; T: 1), (S: 0; T: 1), (S: 0; T: 0), (S: 1; T: 0));
  2484. cTexCoordsSets: array[0..3] of TTexCoordsSet =
  2485. (((S: 1.0; T: 1.0), (S: 0.5; T: 1.0), (S: 0.5; T: 0.5), (S: 1.0; T: 0.5)),
  2486. ((S: 0.5; T: 1.0), (S: 0.0; T: 1.0), (S: 0.0; T: 0.5), (S: 0.5; T: 0.5)),
  2487. ((S: 1.0; T: 0.5), (S: 0.5; T: 0.5), (S: 0.5; T: 0.0), (S: 1.0; T: 0.0)),
  2488. ((S: 0.5; T: 0.5), (S: 0.0; T: 0.5), (S: 0.0; T: 0.0), (S: 0.5; T: 0.0)));
  2489. var
  2490. lifeTime, sizeScale: Single;
  2491. inner, outer: TgxColorVector;
  2492. pos: TAffineVector;
  2493. vertexList: PAffineVectorArray;
  2494. i: Integer;
  2495. tcs: PTexCoordsSet;
  2496. spt: TSpritesPerTexture;
  2497. procedure IssueVertices;
  2498. begin
  2499. glTexCoord2fv(@tcs[0]);
  2500. glVertex3fv(@vertexList[0]);
  2501. glTexCoord2fv(@tcs[1]);
  2502. glVertex3fv(@vertexList[1]);
  2503. glTexCoord2fv(@tcs[2]);
  2504. glVertex3fv(@vertexList[2]);
  2505. glTexCoord2fv(@tcs[3]);
  2506. glVertex3fv(@vertexList[3]);
  2507. end;
  2508. begin
  2509. lifeTime := FCurrentTime - aParticle.CreationTime;
  2510. if Assigned(ShareSprites) then
  2511. spt := ShareSprites.SpritesPerTexture
  2512. else
  2513. spt := SpritesPerTexture;
  2514. case spt of
  2515. sptFour: tcs := @cTexCoordsSets[(aParticle.ID and 3)];
  2516. else
  2517. tcs := @cBaseTexCoordsSet;
  2518. end;
  2519. pos := aParticle.Position;
  2520. vertexList := FVertBuf.List;
  2521. sizeScale := 1;
  2522. // copy vertices
  2523. for i := 0 to FVertBuf.Count - 1 do
  2524. vertexList^[i] := FVertices[i];
  2525. // rotate vertices (if needed)
  2526. if FLifeRotations or (aParticle.FRotation <> 0) then
  2527. RotateVertexBuf(FVertBuf, lifeTime, AffineVectorMake(rci.cameraDirection), aParticle.FRotation);
  2528. // scale vertices (if needed) then translate to particle position
  2529. if FLifeScaling or (aParticle.FEffectScale <> 1) then
  2530. begin
  2531. if FLifeScaling and ComputeSizeScale(lifeTime, sizeScale) then
  2532. sizeScale := sizeScale * aParticle.FEffectScale
  2533. else
  2534. sizeScale := aParticle.FEffectScale;
  2535. for i := 0 to FVertBuf.Count - 1 do
  2536. vertexList^[i] := VectorCombine(vertexList^[i], pos, sizeScale, 1);
  2537. end
  2538. else
  2539. FVertBuf.Translate(pos);
  2540. case ColorMode of
  2541. scmFade:
  2542. begin
  2543. ComputeColors(lifeTime, inner, outer);
  2544. glBegin(GL_TRIANGLE_FAN);
  2545. glColor4fv(@inner);
  2546. glTexCoord2f((tcs^[0].S + tcs^[2].S) * 0.5, (tcs^[0].T + tcs^[2].T) * 0.5);
  2547. glVertex3fv(@pos);
  2548. glColor4fv(@outer);
  2549. IssueVertices;
  2550. glTexCoord2fv(@tcs[0]);
  2551. glVertex3fv(@vertexList[0]);
  2552. glEnd;
  2553. end;
  2554. scmInner:
  2555. begin
  2556. ComputeInnerColor(lifeTime, inner);
  2557. glColor4fv(@inner);
  2558. IssueVertices;
  2559. end;
  2560. scmOuter:
  2561. begin
  2562. ComputeOuterColor(lifeTime, outer);
  2563. glColor4fv(@outer);
  2564. IssueVertices;
  2565. end;
  2566. scmNone:
  2567. begin
  2568. IssueVertices;
  2569. end;
  2570. else
  2571. Assert(False);
  2572. end;
  2573. end;
  2574. procedure TgxBaseSpritePFXManager.EndParticles(var rci: TgxRenderContextInfo);
  2575. begin
  2576. if ColorMode <> scmFade then
  2577. glEnd;
  2578. UnApplyBlendingMode(rci);
  2579. end;
  2580. procedure TgxBaseSpritePFXManager.FinalizeRendering(var rci: TgxRenderContextInfo);
  2581. begin
  2582. FVertBuf.Free;
  2583. FVertices.Free;
  2584. inherited;
  2585. end;
  2586. // ------------------
  2587. // ------------------ TgxCustomSpritePFXManager ------------------
  2588. // ------------------
  2589. constructor TgxCustomSpritePFXManager.Create(aOwner: TComponent);
  2590. begin
  2591. inherited;
  2592. FColorMode := scmInner;
  2593. FSpritesPerTexture := sptOne;
  2594. end;
  2595. destructor TgxCustomSpritePFXManager.Destroy;
  2596. begin
  2597. inherited Destroy;
  2598. end;
  2599. procedure TgxCustomSpritePFXManager.PrepareImage(bmp32: TgxBitmap32; var texFormat: Integer);
  2600. begin
  2601. if Assigned(FOnPrepareTextureImage) then
  2602. FOnPrepareTextureImage(Self, bmp32, texFormat);
  2603. end;
  2604. // ------------------
  2605. // ------------------ TgxPointLightPFXManager ------------------
  2606. // ------------------
  2607. constructor TgxPointLightPFXManager.Create(aOwner: TComponent);
  2608. begin
  2609. inherited;
  2610. FTexMapSize := 5;
  2611. FColorMode := scmInner;
  2612. end;
  2613. destructor TgxPointLightPFXManager.Destroy;
  2614. begin
  2615. inherited Destroy;
  2616. end;
  2617. procedure TgxPointLightPFXManager.SetTexMapSize(const val: Integer);
  2618. begin
  2619. if val <> FTexMapSize then
  2620. begin
  2621. FTexMapSize := val;
  2622. if FTexMapSize < 3 then
  2623. FTexMapSize := 3;
  2624. if FTexMapSize > 9 then
  2625. FTexMapSize := 9;
  2626. NotifyChange(Self);
  2627. end;
  2628. end;
  2629. procedure TgxPointLightPFXManager.PrepareImage(bmp32: TgxBitmap32; var texFormat: Integer);
  2630. var
  2631. s: Integer;
  2632. x, y, d, h2: Integer;
  2633. ih2, f, fy: Single;
  2634. scanLine1, scanLine2: PgxPixel32Array;
  2635. begin
  2636. s := (1 shl TexMapSize);
  2637. bmp32.Width := s;
  2638. bmp32.Height := s;
  2639. bmp32.Blank := false;
  2640. texFormat := GL_LUMINANCE_ALPHA;
  2641. h2 := s div 2;
  2642. ih2 := 1 / h2;
  2643. for y := 0 to h2 - 1 do
  2644. begin
  2645. fy := Sqr((y + 0.5 - h2) * ih2);
  2646. scanLine1 := bmp32.ScanLine[y];
  2647. scanLine2 := bmp32.ScanLine[s - 1 - y];
  2648. for x := 0 to h2 - 1 do
  2649. begin
  2650. f := Sqr((x + 0.5 - h2) * ih2) + fy;
  2651. if f < 1 then
  2652. begin
  2653. d := Trunc((1 - Sqrt(f)) * 256);
  2654. d := d + (d shl 8) + (d shl 16) + (d shl 24);
  2655. end
  2656. else
  2657. d := 0;
  2658. PInteger(@scanLine1[x])^ := d;
  2659. PInteger(@scanLine2[x])^ := d;
  2660. PInteger(@scanLine1[s - 1 - x])^ := d;
  2661. PInteger(@scanLine2[s - 1 - x])^ := d;
  2662. end;
  2663. end;
  2664. end;
  2665. // ------------------------------------------------------------------
  2666. initialization
  2667. // ------------------------------------------------------------------
  2668. RegisterClasses([TgxParticle, TgxParticleList,
  2669. TgxParticleFXEffect, TgxParticleFXRenderer,
  2670. TgxCustomPFXManager,
  2671. TgxPolygonPFXManager,
  2672. TgxCustomSpritePFXManager,
  2673. TgxPointLightPFXManager]);
  2674. RegisterXCollectionItemClass(TgxSourcePFXEffect);
  2675. finalization
  2676. UnregisterXCollectionItemClass(TgxSourcePFXEffect);
  2677. end.