GLS.ParticleFX.pas 89 KB

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