babylon.particleSystem.ts 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984
  1. module BABYLON {
  2. /**
  3. * This represents a particle system in Babylon.
  4. * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
  5. * Particles can take different shapes while emitted like box, sphere, cone or you can write your custom function.
  6. * @example https://doc.babylonjs.com/babylon101/particles
  7. */
  8. export class ParticleSystem implements IDisposable, IAnimatable, IParticleSystem {
  9. /**
  10. * Source color is added to the destination color without alpha affecting the result.
  11. */
  12. public static BLENDMODE_ONEONE = 0;
  13. /**
  14. * Blend current color and particle color using particle’s alpha.
  15. */
  16. public static BLENDMODE_STANDARD = 1;
  17. /**
  18. * Add current color and particle color multiplied by particle’s alpha.
  19. */
  20. public static BLENDMODE_ADD = 2;
  21. /**
  22. * List of animations used by the particle system.
  23. */
  24. public animations: Animation[] = [];
  25. /**
  26. * The id of the Particle system.
  27. */
  28. public id: string;
  29. /**
  30. * The friendly name of the Particle system.
  31. */
  32. public name: string;
  33. /**
  34. * The rendering group used by the Particle system to chose when to render.
  35. */
  36. public renderingGroupId = 0;
  37. /**
  38. * The emitter represents the Mesh or position we are attaching the particle system to.
  39. */
  40. public emitter: Nullable<AbstractMesh | Vector3> = null;
  41. /**
  42. * The maximum number of particles to emit per frame
  43. */
  44. public emitRate = 10;
  45. /**
  46. * If you want to launch only a few particles at once, that can be done, as well.
  47. */
  48. public manualEmitCount = -1;
  49. /**
  50. * The overall motion speed (0.01 is default update speed, faster updates = faster animation)
  51. */
  52. public updateSpeed = 0.01;
  53. /**
  54. * The amount of time the particle system is running (depends of the overall update speed).
  55. */
  56. public targetStopDuration = 0;
  57. /**
  58. * Specifies whether the particle system will be disposed once it reaches the end of the animation.
  59. */
  60. public disposeOnStop = false;
  61. /**
  62. * Minimum power of emitting particles.
  63. */
  64. public minEmitPower = 1;
  65. /**
  66. * Maximum power of emitting particles.
  67. */
  68. public maxEmitPower = 1;
  69. /**
  70. * Minimum life time of emitting particles.
  71. */
  72. public minLifeTime = 1;
  73. /**
  74. * Maximum life time of emitting particles.
  75. */
  76. public maxLifeTime = 1;
  77. /**
  78. * Minimum Size of emitting particles.
  79. */
  80. public minSize = 1;
  81. /**
  82. * Maximum Size of emitting particles.
  83. */
  84. public maxSize = 1;
  85. /**
  86. * Minimum scale of emitting particles on X axis.
  87. */
  88. public minScaleX = 1;
  89. /**
  90. * Maximum scale of emitting particles on X axis.
  91. */
  92. public maxScaleX = 1;
  93. /**
  94. * Minimum scale of emitting particles on Y axis.
  95. */
  96. public minScaleY = 1;
  97. /**
  98. * Maximum scale of emitting particles on Y axis.
  99. */
  100. public maxScaleY = 1;
  101. /**
  102. * Gets or sets the minimal initial rotation in radians.
  103. */
  104. public minInitialRotation = 0;
  105. /**
  106. * Gets or sets the maximal initial rotation in radians.
  107. */
  108. public maxInitialRotation = 0;
  109. /**
  110. * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
  111. */
  112. public minAngularSpeed = 0;
  113. /**
  114. * Maximum angular speed of emitting particles (Z-axis rotation for each particle).
  115. */
  116. public maxAngularSpeed = 0;
  117. /**
  118. * The texture used to render each particle. (this can be a spritesheet)
  119. */
  120. public particleTexture: Nullable<Texture>;
  121. /**
  122. * The layer mask we are rendering the particles through.
  123. */
  124. public layerMask: number = 0x0FFFFFFF;
  125. /**
  126. * This can help using your own shader to render the particle system.
  127. * The according effect will be created
  128. */
  129. public customShader: any = null;
  130. /**
  131. * By default particle system starts as soon as they are created. This prevents the
  132. * automatic start to happen and let you decide when to start emitting particles.
  133. */
  134. public preventAutoStart: boolean = false;
  135. /**
  136. * Gets or sets a texture used to add random noise to particle positions
  137. */
  138. public noiseTexture: Nullable<BaseTexture>;
  139. /** Gets or sets the strength to apply to the noise value (default is (10, 10, 10)) */
  140. public noiseStrength = new Vector3(10, 10, 10);
  141. /**
  142. * This function can be defined to provide custom update for active particles.
  143. * This function will be called instead of regular update (age, position, color, etc.).
  144. * Do not forget that this function will be called on every frame so try to keep it simple and fast :)
  145. */
  146. public updateFunction: (particles: Particle[]) => void;
  147. /**
  148. * Callback triggered when the particle animation is ending.
  149. */
  150. public onAnimationEnd: Nullable<() => void> = null;
  151. /**
  152. * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE or ParticleSystem.BLENDMODE_STANDARD.
  153. */
  154. public blendMode = ParticleSystem.BLENDMODE_ONEONE;
  155. /**
  156. * Forces the particle to write their depth information to the depth buffer. This can help preventing other draw calls
  157. * to override the particles.
  158. */
  159. public forceDepthWrite = false;
  160. /**
  161. * You can use gravity if you want to give an orientation to your particles.
  162. */
  163. public gravity = Vector3.Zero();
  164. private _colorGradients: Nullable<Array<ColorGradient>> = null;
  165. private _sizeGradients: Nullable<Array<FactorGradient>> = null;
  166. private _lifeTimeGradients: Nullable<Array<FactorGradient>> = null;
  167. private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
  168. private _velocityGradients: Nullable<Array<FactorGradient>> = null;
  169. /**
  170. * Gets the current list of color gradients.
  171. * You must use addColorGradient and removeColorGradient to udpate this list
  172. * @returns the list of color gradients
  173. */
  174. public getColorGradients(): Nullable<Array<ColorGradient>> {
  175. return this._colorGradients;
  176. }
  177. /**
  178. * Gets the current list of size gradients.
  179. * You must use addSizeGradient and removeSizeGradient to udpate this list
  180. * @returns the list of size gradients
  181. */
  182. public getSizeGradients(): Nullable<Array<FactorGradient>> {
  183. return this._sizeGradients;
  184. }
  185. /**
  186. * Gets the current list of life time gradients.
  187. * You must use addLifeTimeGradient and removeLifeTimeGradient to udpate this list
  188. * @returns the list of life time gradients
  189. */
  190. public getLifeTimeGradients(): Nullable<Array<FactorGradient>> {
  191. return this._lifeTimeGradients;
  192. }
  193. /**
  194. * Gets the current list of angular speed gradients.
  195. * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
  196. * @returns the list of angular speed gradients
  197. */
  198. public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
  199. return this._angularSpeedGradients;
  200. }
  201. /**
  202. * Gets the current list of velocity gradients.
  203. * You must use addVelocityGradient and removeVelocityGradient to udpate this list
  204. * @returns the list of velocity gradients
  205. */
  206. public getVelocityGradients(): Nullable<Array<FactorGradient>> {
  207. return this._velocityGradients;
  208. }
  209. /**
  210. * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
  211. * This only works when particleEmitterTyps is a BoxParticleEmitter
  212. */
  213. public get direction1(): Vector3 {
  214. if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
  215. return (<BoxParticleEmitter>this.particleEmitterType).direction1;
  216. }
  217. return Vector3.Zero();
  218. }
  219. public set direction1(value: Vector3) {
  220. if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
  221. (<BoxParticleEmitter>this.particleEmitterType).direction1 = value;
  222. }
  223. }
  224. /**
  225. * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
  226. * This only works when particleEmitterTyps is a BoxParticleEmitter
  227. */
  228. public get direction2(): Vector3 {
  229. if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
  230. return (<BoxParticleEmitter>this.particleEmitterType).direction2;
  231. }
  232. return Vector3.Zero();
  233. }
  234. public set direction2(value: Vector3) {
  235. if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
  236. (<BoxParticleEmitter>this.particleEmitterType).direction2 = value;
  237. }
  238. }
  239. /**
  240. * Minimum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
  241. * This only works when particleEmitterTyps is a BoxParticleEmitter
  242. */
  243. public get minEmitBox(): Vector3 {
  244. if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
  245. return (<BoxParticleEmitter>this.particleEmitterType).minEmitBox;
  246. }
  247. return Vector3.Zero();
  248. }
  249. public set minEmitBox(value: Vector3) {
  250. if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
  251. (<BoxParticleEmitter>this.particleEmitterType).minEmitBox = value;
  252. }
  253. }
  254. /**
  255. * Maximum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
  256. * This only works when particleEmitterTyps is a BoxParticleEmitter
  257. */
  258. public get maxEmitBox(): Vector3 {
  259. if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
  260. return (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox;
  261. }
  262. return Vector3.Zero();
  263. }
  264. public set maxEmitBox(value: Vector3) {
  265. if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
  266. (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox = value;
  267. }
  268. }
  269. /**
  270. * Random color of each particle after it has been emitted, between color1 and color2 vectors
  271. */
  272. public color1 = new Color4(1.0, 1.0, 1.0, 1.0);
  273. /**
  274. * Random color of each particle after it has been emitted, between color1 and color2 vectors
  275. */
  276. public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
  277. /**
  278. * Color the particle will have at the end of its lifetime
  279. */
  280. public colorDead = new Color4(0, 0, 0, 1.0);
  281. /**
  282. * An optional mask to filter some colors out of the texture, or filter a part of the alpha channel
  283. */
  284. public textureMask = new Color4(1.0, 1.0, 1.0, 1.0);
  285. /**
  286. * The particle emitter type defines the emitter used by the particle system.
  287. * It can be for example box, sphere, or cone...
  288. */
  289. public particleEmitterType: IParticleEmitterType;
  290. /**
  291. * This function can be defined to specify initial direction for every new particle.
  292. * It by default use the emitterType defined function
  293. */
  294. public startDirectionFunction: (worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle) => void;
  295. /**
  296. * This function can be defined to specify initial position for every new particle.
  297. * It by default use the emitterType defined function
  298. */
  299. public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
  300. /**
  301. * If using a spritesheet (isAnimationSheetEnabled) defines the speed of the sprite loop (default is 1 meaning the animation will play once during the entire particle lifetime)
  302. */
  303. public spriteCellChangeSpeed = 1;
  304. /**
  305. * If using a spritesheet (isAnimationSheetEnabled) defines the first sprite cell to display
  306. */
  307. public startSpriteCellID = 0;
  308. /**
  309. * If using a spritesheet (isAnimationSheetEnabled) defines the last sprite cell to display
  310. */
  311. public endSpriteCellID = 0;
  312. /**
  313. * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell width to use
  314. */
  315. public spriteCellWidth = 0;
  316. /**
  317. * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell height to use
  318. */
  319. public spriteCellHeight = 0;
  320. /** Gets or sets a value indicating how many cycles (or frames) must be executed before first rendering (this value has to be set before starting the system). Default is 0 */
  321. public preWarmCycles = 0;
  322. /** Gets or sets a value indicating the time step multiplier to use in pre-warm mode (default is 1) */
  323. public preWarmStepOffset = 1;
  324. /** Gets or sets a Vector2 used to move the pivot (by default (0,0)) */
  325. public translationPivot = new Vector2(0, 0);
  326. /**
  327. * An event triggered when the system is disposed
  328. */
  329. public onDisposeObservable = new Observable<ParticleSystem>();
  330. private _onDisposeObserver: Nullable<Observer<ParticleSystem>>;
  331. /**
  332. * Sets a callback that will be triggered when the system is disposed
  333. */
  334. public set onDispose(callback: () => void) {
  335. if (this._onDisposeObserver) {
  336. this.onDisposeObservable.remove(this._onDisposeObserver);
  337. }
  338. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  339. }
  340. /**
  341. * Gets whether an animation sprite sheet is enabled or not on the particle system
  342. */
  343. public get isAnimationSheetEnabled(): boolean {
  344. return this._isAnimationSheetEnabled;
  345. }
  346. /**
  347. * Gets or sets a boolean indicating if the particles must be rendered as billboard or aligned with the direction
  348. */
  349. public get isBillboardBased(): boolean {
  350. return this._isBillboardBased;
  351. }
  352. public set isBillboardBased(value: boolean) {
  353. if (this._isBillboardBased === value) {
  354. return;
  355. }
  356. this._isBillboardBased = value;
  357. this._resetEffect();
  358. }
  359. /**
  360. * Gets or sets the billboard mode to use when isBillboardBased = true.
  361. * Only BABYLON.AbstractMesh.BILLBOARDMODE_ALL and AbstractMesh.BILLBOARDMODE_Y are supported so far
  362. */
  363. public billboardMode = AbstractMesh.BILLBOARDMODE_ALL;
  364. private _particles = new Array<Particle>();
  365. private _epsilon: number;
  366. private _capacity: number;
  367. private _scene: Scene;
  368. private _stockParticles = new Array<Particle>();
  369. private _newPartsExcess = 0;
  370. private _vertexData: Float32Array;
  371. private _vertexBuffer: Nullable<Buffer>;
  372. private _vertexBuffers: { [key: string]: VertexBuffer } = {};
  373. private _spriteBuffer: Nullable<Buffer>;
  374. private _indexBuffer: Nullable<WebGLBuffer>;
  375. private _effect: Effect;
  376. private _customEffect: Nullable<Effect>;
  377. private _cachedDefines: string;
  378. private _scaledColorStep = new Color4(0, 0, 0, 0);
  379. private _colorDiff = new Color4(0, 0, 0, 0);
  380. private _scaledDirection = Vector3.Zero();
  381. private _scaledGravity = Vector3.Zero();
  382. private _currentRenderId = -1;
  383. private _alive: boolean;
  384. private _useInstancing = false;
  385. private _started = false;
  386. private _stopped = false;
  387. private _actualFrame = 0;
  388. private _scaledUpdateSpeed: number;
  389. private _vertexBufferSize: number;
  390. private _isAnimationSheetEnabled: boolean;
  391. private _isBillboardBased = true;
  392. // end of sheet animation
  393. // Sub-emitters
  394. /**
  395. * this is the Sub-emitters templates that will be used to generate particle system when the particle dies, this property is used by the root particle system only.
  396. */
  397. public subEmitters: ParticleSystem[];
  398. /**
  399. * The current active Sub-systems, this property is used by the root particle system only.
  400. */
  401. public activeSubSystems: Array<ParticleSystem>;
  402. private _rootParticleSystem: ParticleSystem;
  403. //end of Sub-emitter
  404. /**
  405. * Gets the current list of active particles
  406. */
  407. public get particles(): Particle[] {
  408. return this._particles;
  409. }
  410. /**
  411. * Returns the string "ParticleSystem"
  412. * @returns a string containing the class name
  413. */
  414. public getClassName(): string {
  415. return "ParticleSystem";
  416. }
  417. /**
  418. * Instantiates a particle system.
  419. * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
  420. * @param name The name of the particle system
  421. * @param capacity The max number of particles alive at the same time
  422. * @param scene The scene the particle system belongs to
  423. * @param customEffect a custom effect used to change the way particles are rendered by default
  424. * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
  425. * @param epsilon Offset used to render the particles
  426. */
  427. constructor(name: string, capacity: number, scene: Scene, customEffect: Nullable<Effect> = null, isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
  428. this.id = name;
  429. this.name = name;
  430. this._capacity = capacity;
  431. this._epsilon = epsilon;
  432. this._isAnimationSheetEnabled = isAnimationSheetEnabled;
  433. this._scene = scene || Engine.LastCreatedScene;
  434. this._customEffect = customEffect;
  435. scene.particleSystems.push(this);
  436. this._useInstancing = this._scene.getEngine().getCaps().instancedArrays;
  437. this._createIndexBuffer();
  438. this._createVertexBuffers();
  439. // Default emitter type
  440. this.particleEmitterType = new BoxParticleEmitter();
  441. this.updateFunction = (particles: Particle[]): void => {
  442. let noiseTextureData: Nullable<Uint8Array> = null;
  443. let noiseTextureSize: Nullable<ISize> = null;
  444. if (this.noiseTexture) { // We need to get texture data back to CPU
  445. noiseTextureData = <Nullable<Uint8Array>>(this.noiseTexture.readPixels());
  446. noiseTextureSize = this.noiseTexture.getSize();
  447. }
  448. for (var index = 0; index < particles.length; index++) {
  449. var particle = particles[index];
  450. particle.age += this._scaledUpdateSpeed;
  451. if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
  452. this._emitFromParticle(particle);
  453. this.recycleParticle(particle);
  454. index--;
  455. continue;
  456. }
  457. else {
  458. let ratio = particle.age / particle.lifeTime;
  459. // Color
  460. if (this._colorGradients && this._colorGradients.length > 0) {
  461. Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
  462. if (currentGradient !== particle._currentColorGradient) {
  463. particle._currentColor1.copyFrom(particle._currentColor2);
  464. (<ColorGradient>nextGradient).getColorToRef(particle._currentColor2);
  465. particle._currentColorGradient = (<ColorGradient>currentGradient);
  466. }
  467. Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
  468. });
  469. }
  470. else {
  471. particle.colorStep.scaleToRef(this._scaledUpdateSpeed, this._scaledColorStep);
  472. particle.color.addInPlace(this._scaledColorStep);
  473. if (particle.color.a < 0) {
  474. particle.color.a = 0;
  475. }
  476. }
  477. // Angular speed
  478. if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {
  479. Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
  480. if (currentGradient !== particle._currentAngularSpeedGradient) {
  481. particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
  482. particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();
  483. particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
  484. }
  485. particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
  486. });
  487. }
  488. particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
  489. // Direction
  490. let directionScale = this._scaledUpdateSpeed;
  491. if (this._velocityGradients && this._velocityGradients.length > 0) {
  492. Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
  493. if (currentGradient !== particle._currentVelocityGradient) {
  494. particle._currentVelocity1 = particle._currentVelocity2;
  495. particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();
  496. particle._currentVelocityGradient = (<FactorGradient>currentGradient);
  497. }
  498. directionScale *= Scalar.Lerp(particle._currentVelocity1, particle._currentVelocity2, scale);
  499. });
  500. }
  501. particle.direction.scaleToRef(directionScale, this._scaledDirection);
  502. particle.position.addInPlace(this._scaledDirection);
  503. // Noise
  504. if (noiseTextureData && noiseTextureSize) {
  505. let fetchedColorR = Tools.FetchR(particle.position.y, particle.position.z, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
  506. let fetchedColorG = Tools.FetchR(particle.position.x + 0.33, particle.position.z + 0.33, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
  507. let fetchedColorB = Tools.FetchR(particle.position.x - 0.33, particle.position.y - 0.33, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
  508. let force = Tmp.Vector3[0];
  509. let scaledForce = Tmp.Vector3[1];
  510. force.copyFromFloats((2 * fetchedColorR - 1) * this.noiseStrength.x, (2 * fetchedColorG - 1) * this.noiseStrength.y, (2 * fetchedColorB - 1) * this.noiseStrength.z);
  511. force.scaleToRef(this._scaledUpdateSpeed, scaledForce);
  512. particle.direction.addInPlace(scaledForce);
  513. }
  514. // Gravity
  515. this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
  516. particle.direction.addInPlace(this._scaledGravity);
  517. // Size
  518. if (this._sizeGradients && this._sizeGradients.length > 0) {
  519. Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
  520. if (currentGradient !== particle._currentSizeGradient) {
  521. particle._currentSize1 = particle._currentSize2;
  522. particle._currentSize2 = (<FactorGradient>nextGradient).getFactor();
  523. particle._currentSizeGradient = (<FactorGradient>currentGradient);
  524. }
  525. particle.size = Scalar.Lerp(particle._currentSize1, particle._currentSize2, scale);
  526. });
  527. }
  528. if (this._isAnimationSheetEnabled) {
  529. particle.updateCellIndex();
  530. }
  531. }
  532. }
  533. }
  534. }
  535. private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number, factor2?: number) {
  536. let newGradient = new FactorGradient();
  537. newGradient.gradient = gradient;
  538. newGradient.factor1 = factor;
  539. newGradient.factor2 = factor2;
  540. factorGradients.push(newGradient);
  541. factorGradients.sort((a, b) => {
  542. if (a.gradient < b.gradient) {
  543. return -1;
  544. } else if (a.gradient > b.gradient) {
  545. return 1;
  546. }
  547. return 0;
  548. });
  549. }
  550. private _removeFactorGradient(factorGradients: Nullable<FactorGradient[]>, gradient: number) {
  551. if (!factorGradients) {
  552. return;
  553. }
  554. let index = 0;
  555. for (var factorGradient of factorGradients) {
  556. if (factorGradient.gradient === gradient) {
  557. factorGradients.splice(index, 1);
  558. break;
  559. }
  560. index++;
  561. }
  562. }
  563. /**
  564. * Adds a new life time gradient
  565. * @param gradient defines the gradient to use (between 0 and 1)
  566. * @param factor defines the life time factor to affect to the specified gradient
  567. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  568. * @returns the current particle system
  569. */
  570. public addLifeTimeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
  571. if (!this._lifeTimeGradients) {
  572. this._lifeTimeGradients = [];
  573. }
  574. this._addFactorGradient(this._lifeTimeGradients, gradient, factor, factor2);
  575. return this;
  576. }
  577. /**
  578. * Remove a specific life time gradient
  579. * @param gradient defines the gradient to remove
  580. * @returns the current particle system
  581. */
  582. public removeLifeTimeGradient(gradient: number): ParticleSystem {
  583. this._removeFactorGradient(this._lifeTimeGradients, gradient);
  584. return this;
  585. }
  586. /**
  587. * Adds a new size gradient
  588. * @param gradient defines the gradient to use (between 0 and 1)
  589. * @param factor defines the size factor to affect to the specified gradient
  590. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  591. * @returns the current particle system
  592. */
  593. public addSizeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
  594. if (!this._sizeGradients) {
  595. this._sizeGradients = [];
  596. }
  597. this._addFactorGradient(this._sizeGradients, gradient, factor, factor2);
  598. return this;
  599. }
  600. /**
  601. * Remove a specific size gradient
  602. * @param gradient defines the gradient to remove
  603. * @returns the current particle system
  604. */
  605. public removeSizeGradient(gradient: number): ParticleSystem {
  606. this._removeFactorGradient(this._sizeGradients, gradient);
  607. return this;
  608. }
  609. /**
  610. * Adds a new angular speed gradient
  611. * @param gradient defines the gradient to use (between 0 and 1)
  612. * @param factor defines the size factor to affect to the specified gradient
  613. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  614. * @returns the current particle system
  615. */
  616. public addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
  617. if (!this._angularSpeedGradients) {
  618. this._angularSpeedGradients = [];
  619. }
  620. this._addFactorGradient(this._angularSpeedGradients, gradient, factor, factor2);
  621. return this;
  622. }
  623. /**
  624. * Remove a specific angular speed gradient
  625. * @param gradient defines the gradient to remove
  626. * @returns the current particle system
  627. */
  628. public removeAngularSpeedGradient(gradient: number): ParticleSystem {
  629. this._removeFactorGradient(this._angularSpeedGradients, gradient);
  630. return this;
  631. }
  632. /**
  633. * Adds a new velocity gradient
  634. * @param gradient defines the gradient to use (between 0 and 1)
  635. * @param factor defines the size factor to affect to the specified gradient
  636. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  637. * @returns the current particle system
  638. */
  639. public addVelocityGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
  640. if (!this._velocityGradients) {
  641. this._velocityGradients = [];
  642. }
  643. this._addFactorGradient(this._velocityGradients, gradient, factor, factor2);
  644. return this;
  645. }
  646. /**
  647. * Remove a specific velocity gradient
  648. * @param gradient defines the gradient to remove
  649. * @returns the current particle system
  650. */
  651. public removeVelocityGradient(gradient: number): ParticleSystem {
  652. this._removeFactorGradient(this._velocityGradients, gradient);
  653. return this;
  654. }
  655. /**
  656. * Adds a new color gradient
  657. * @param gradient defines the gradient to use (between 0 and 1)
  658. * @param color defines the color to affect to the specified gradient
  659. * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
  660. */
  661. public addColorGradient(gradient: number, color: Color4, color2?: Color4): ParticleSystem {
  662. if (!this._colorGradients) {
  663. this._colorGradients = [];
  664. }
  665. let colorGradient = new ColorGradient();
  666. colorGradient.gradient = gradient;
  667. colorGradient.color1 = color;
  668. colorGradient.color2 = color2;
  669. this._colorGradients.push(colorGradient);
  670. this._colorGradients.sort((a, b) => {
  671. if (a.gradient < b.gradient) {
  672. return -1;
  673. } else if (a.gradient > b.gradient) {
  674. return 1;
  675. }
  676. return 0;
  677. });
  678. return this;
  679. }
  680. /**
  681. * Remove a specific color gradient
  682. * @param gradient defines the gradient to remove
  683. */
  684. public removeColorGradient(gradient: number): ParticleSystem {
  685. if (!this._colorGradients) {
  686. return this;
  687. }
  688. let index = 0;
  689. for (var colorGradient of this._colorGradients) {
  690. if (colorGradient.gradient === gradient) {
  691. this._colorGradients.splice(index, 1);
  692. break;
  693. }
  694. index++;
  695. }
  696. return this;
  697. }
  698. private _resetEffect() {
  699. if (this._vertexBuffer) {
  700. this._vertexBuffer.dispose();
  701. this._vertexBuffer = null;
  702. }
  703. if (this._spriteBuffer) {
  704. this._spriteBuffer.dispose();
  705. this._spriteBuffer = null;
  706. }
  707. this._createVertexBuffers();
  708. }
  709. private _createVertexBuffers() {
  710. this._vertexBufferSize = this._useInstancing ? 10 : 12;
  711. if (this._isAnimationSheetEnabled) {
  712. this._vertexBufferSize += 1;
  713. }
  714. if (!this._isBillboardBased) {
  715. this._vertexBufferSize += 3;
  716. }
  717. let engine = this._scene.getEngine();
  718. this._vertexData = new Float32Array(this._capacity * this._vertexBufferSize * (this._useInstancing ? 1 : 4));
  719. this._vertexBuffer = new Buffer(engine, this._vertexData, true, this._vertexBufferSize);
  720. let dataOffset = 0;
  721. var positions = this._vertexBuffer.createVertexBuffer(VertexBuffer.PositionKind, dataOffset, 3, this._vertexBufferSize, this._useInstancing);
  722. this._vertexBuffers[VertexBuffer.PositionKind] = positions;
  723. dataOffset += 3;
  724. var colors = this._vertexBuffer.createVertexBuffer(VertexBuffer.ColorKind, dataOffset, 4, this._vertexBufferSize, this._useInstancing);
  725. this._vertexBuffers[VertexBuffer.ColorKind] = colors;
  726. dataOffset += 4;
  727. var options = this._vertexBuffer.createVertexBuffer("angle", dataOffset, 1, this._vertexBufferSize, this._useInstancing);
  728. this._vertexBuffers["angle"] = options;
  729. dataOffset += 1;
  730. var size = this._vertexBuffer.createVertexBuffer("size", dataOffset, 2, this._vertexBufferSize, this._useInstancing);
  731. this._vertexBuffers["size"] = size;
  732. dataOffset += 2;
  733. if (this._isAnimationSheetEnabled) {
  734. var cellIndexBuffer = this._vertexBuffer.createVertexBuffer("cellIndex", dataOffset, 1, this._vertexBufferSize, this._useInstancing);
  735. this._vertexBuffers["cellIndex"] = cellIndexBuffer;
  736. dataOffset += 1;
  737. }
  738. if (!this._isBillboardBased) {
  739. var directionBuffer = this._vertexBuffer.createVertexBuffer("direction", dataOffset, 3, this._vertexBufferSize, this._useInstancing);
  740. this._vertexBuffers["direction"] = directionBuffer;
  741. dataOffset += 3;
  742. }
  743. var offsets: VertexBuffer;
  744. if (this._useInstancing) {
  745. var spriteData = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
  746. this._spriteBuffer = new Buffer(engine, spriteData, false, 2);
  747. offsets = this._spriteBuffer.createVertexBuffer("offset", 0, 2);
  748. } else {
  749. offsets = this._vertexBuffer.createVertexBuffer("offset", dataOffset, 2, this._vertexBufferSize, this._useInstancing);
  750. dataOffset += 2;
  751. }
  752. this._vertexBuffers["offset"] = offsets;
  753. }
  754. private _createIndexBuffer() {
  755. if (this._useInstancing) {
  756. return;
  757. }
  758. var indices = [];
  759. var index = 0;
  760. for (var count = 0; count < this._capacity; count++) {
  761. indices.push(index);
  762. indices.push(index + 1);
  763. indices.push(index + 2);
  764. indices.push(index);
  765. indices.push(index + 2);
  766. indices.push(index + 3);
  767. index += 4;
  768. }
  769. this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
  770. }
  771. /**
  772. * Gets the maximum number of particles active at the same time.
  773. * @returns The max number of active particles.
  774. */
  775. public getCapacity(): number {
  776. return this._capacity;
  777. }
  778. /**
  779. * Gets Wether there are still active particles in the system.
  780. * @returns True if it is alive, otherwise false.
  781. */
  782. public isAlive(): boolean {
  783. return this._alive;
  784. }
  785. /**
  786. * Gets Wether the system has been started.
  787. * @returns True if it has been started, otherwise false.
  788. */
  789. public isStarted(): boolean {
  790. return this._started;
  791. }
  792. /**
  793. * Starts the particle system and begins to emit
  794. * @param delay defines the delay in milliseconds before starting the system (0 by default)
  795. */
  796. public start(delay = 0): void {
  797. if (delay) {
  798. setTimeout(()=> {
  799. this.start(0);
  800. }, delay);
  801. return;
  802. }
  803. this._started = true;
  804. this._stopped = false;
  805. this._actualFrame = 0;
  806. if (this.subEmitters && this.subEmitters.length != 0) {
  807. this.activeSubSystems = new Array<ParticleSystem>();
  808. }
  809. if (this.preWarmCycles) {
  810. for (var index = 0; index < this.preWarmCycles; index++) {
  811. this.animate(true);
  812. }
  813. }
  814. }
  815. /**
  816. * Stops the particle system.
  817. * @param stopSubEmitters if true it will stop the current system and all created sub-Systems if false it will stop the current root system only, this param is used by the root particle system only. the default value is true.
  818. */
  819. public stop(stopSubEmitters = true): void {
  820. this._stopped = true;
  821. if (stopSubEmitters) {
  822. this._stopSubEmitters();
  823. }
  824. }
  825. // animation sheet
  826. /**
  827. * Remove all active particles
  828. */
  829. public reset(): void {
  830. this._stockParticles = [];
  831. this._particles = [];
  832. }
  833. /**
  834. * @hidden (for internal use only)
  835. */
  836. public _appendParticleVertex(index: number, particle: Particle, offsetX: number, offsetY: number): void {
  837. var offset = index * this._vertexBufferSize;
  838. this._vertexData[offset++] = particle.position.x;
  839. this._vertexData[offset++] = particle.position.y;
  840. this._vertexData[offset++] = particle.position.z;
  841. this._vertexData[offset++] = particle.color.r;
  842. this._vertexData[offset++] = particle.color.g;
  843. this._vertexData[offset++] = particle.color.b;
  844. this._vertexData[offset++] = particle.color.a;
  845. this._vertexData[offset++] = particle.angle;
  846. this._vertexData[offset++] = particle.scale.x * particle.size;
  847. this._vertexData[offset++] = particle.scale.y * particle.size;
  848. if (this._isAnimationSheetEnabled) {
  849. this._vertexData[offset++] = particle.cellIndex;
  850. }
  851. if (!this._isBillboardBased) {
  852. if (particle._initialDirection) {
  853. this._vertexData[offset++] = particle._initialDirection.x;
  854. this._vertexData[offset++] = particle._initialDirection.y;
  855. this._vertexData[offset++] = particle._initialDirection.z;
  856. } else {
  857. this._vertexData[offset++] = particle.direction.x;
  858. this._vertexData[offset++] = particle.direction.y;
  859. this._vertexData[offset++] = particle.direction.z;
  860. }
  861. }
  862. if (!this._useInstancing) {
  863. if (this._isAnimationSheetEnabled) {
  864. if (offsetX === 0)
  865. offsetX = this._epsilon;
  866. else if (offsetX === 1)
  867. offsetX = 1 - this._epsilon;
  868. if (offsetY === 0)
  869. offsetY = this._epsilon;
  870. else if (offsetY === 1)
  871. offsetY = 1 - this._epsilon;
  872. }
  873. this._vertexData[offset++] = offsetX;
  874. this._vertexData[offset++] = offsetY;
  875. }
  876. }
  877. // start of sub system methods
  878. /**
  879. * "Recycles" one of the particle by copying it back to the "stock" of particles and removing it from the active list.
  880. * Its lifetime will start back at 0.
  881. */
  882. public recycleParticle: (particle: Particle) => void = (particle) => {
  883. var lastParticle = <Particle>this._particles.pop();
  884. if (lastParticle !== particle) {
  885. lastParticle.copyTo(particle);
  886. }
  887. this._stockParticles.push(lastParticle);
  888. };
  889. private _stopSubEmitters(): void {
  890. if (!this.activeSubSystems) {
  891. return;
  892. }
  893. this.activeSubSystems.forEach(subSystem => {
  894. subSystem.stop(true);
  895. });
  896. this.activeSubSystems = new Array<ParticleSystem>();
  897. }
  898. private _createParticle: () => Particle = () => {
  899. var particle: Particle;
  900. if (this._stockParticles.length !== 0) {
  901. particle = <Particle>this._stockParticles.pop();
  902. particle.age = 0;
  903. particle._currentColorGradient = null;
  904. particle.cellIndex = this.startSpriteCellID;
  905. } else {
  906. particle = new Particle(this);
  907. }
  908. return particle;
  909. }
  910. private _removeFromRoot(): void {
  911. if (!this._rootParticleSystem){
  912. return;
  913. }
  914. let index = this._rootParticleSystem.activeSubSystems.indexOf(this);
  915. if (index !== -1) {
  916. this._rootParticleSystem.activeSubSystems.splice(index, 1);
  917. }
  918. }
  919. private _emitFromParticle: (particle: Particle) => void = (particle) => {
  920. if (!this.subEmitters || this.subEmitters.length === 0) {
  921. return;
  922. }
  923. var templateIndex = Math.floor(Math.random() * this.subEmitters.length);
  924. var subSystem = this.subEmitters[templateIndex].clone(this.name + "_sub", particle.position.clone());
  925. subSystem._rootParticleSystem = this;
  926. this.activeSubSystems.push(subSystem);
  927. subSystem.start();
  928. }
  929. // End of sub system methods
  930. private _update(newParticles: number): void {
  931. // Update current
  932. this._alive = this._particles.length > 0;
  933. this.updateFunction(this._particles);
  934. // Add new ones
  935. var worldMatrix;
  936. if ((<AbstractMesh>this.emitter).position) {
  937. var emitterMesh = (<AbstractMesh>this.emitter);
  938. worldMatrix = emitterMesh.getWorldMatrix();
  939. } else {
  940. var emitterPosition = (<Vector3>this.emitter);
  941. worldMatrix = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
  942. }
  943. var particle: Particle;
  944. for (var index = 0; index < newParticles; index++) {
  945. if (this._particles.length === this._capacity) {
  946. break;
  947. }
  948. particle = this._createParticle();
  949. this._particles.push(particle);
  950. // Emitter
  951. let emitPower = Scalar.RandomRange(this.minEmitPower, this.maxEmitPower);
  952. if (this.startPositionFunction) {
  953. this.startPositionFunction(worldMatrix, particle.position, particle);
  954. }
  955. else {
  956. this.particleEmitterType.startPositionFunction(worldMatrix, particle.position, particle);
  957. }
  958. if (this.startDirectionFunction) {
  959. this.startDirectionFunction(worldMatrix, particle.direction, particle);
  960. }
  961. else {
  962. this.particleEmitterType.startDirectionFunction(worldMatrix, particle.direction, particle);
  963. }
  964. if (emitPower === 0) {
  965. if (!particle._initialDirection) {
  966. particle._initialDirection = particle.direction.clone();
  967. } else {
  968. particle._initialDirection.copyFrom(particle.direction);
  969. }
  970. } else {
  971. particle._initialDirection = null;
  972. }
  973. particle.direction.scaleInPlace(emitPower);
  974. // Life time
  975. if (this.targetStopDuration && this._lifeTimeGradients && this._lifeTimeGradients.length > 0) {
  976. let ratio = Scalar.Clamp(this._actualFrame / this.targetStopDuration);
  977. Tools.GetCurrentGradient(ratio, this._lifeTimeGradients, (currentGradient, nextGradient, scale) => {
  978. let factorGradient1 = (<FactorGradient>currentGradient);
  979. let factorGradient2 = (<FactorGradient>nextGradient);
  980. let lifeTime1 = factorGradient1.getFactor();
  981. let lifeTime2 = factorGradient2.getFactor();
  982. let gradient = (ratio - factorGradient1.gradient) / (factorGradient2.gradient - factorGradient1.gradient);
  983. particle.lifeTime = Scalar.Lerp(lifeTime1, lifeTime2, gradient);
  984. });
  985. } else {
  986. particle.lifeTime = Scalar.RandomRange(this.minLifeTime, this.maxLifeTime);
  987. }
  988. // Size
  989. if (!this._sizeGradients || this._sizeGradients.length === 0) {
  990. particle.size = Scalar.RandomRange(this.minSize, this.maxSize);
  991. } else {
  992. particle._currentSizeGradient = this._sizeGradients[0];
  993. particle._currentSize1 = particle._currentSizeGradient.getFactor();
  994. particle.size = particle._currentSize1;
  995. if (this._sizeGradients.length > 1) {
  996. particle._currentSize2 = this._sizeGradients[1].getFactor();
  997. } else {
  998. particle._currentSize2 = particle._currentSize1;
  999. }
  1000. }
  1001. // Size and scale
  1002. particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
  1003. // Angle
  1004. if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
  1005. particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
  1006. } else {
  1007. particle._currentAngularSpeedGradient = this._angularSpeedGradients[0];
  1008. particle.angularSpeed = particle._currentAngularSpeedGradient.getFactor();
  1009. particle._currentAngularSpeed1 = particle.angularSpeed;
  1010. if (this._angularSpeedGradients.length > 1) {
  1011. particle._currentAngularSpeed2 = this._angularSpeedGradients[1].getFactor();
  1012. } else {
  1013. particle._currentAngularSpeed2 = particle._currentAngularSpeed1;
  1014. }
  1015. }
  1016. particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
  1017. // Velocity
  1018. if (this._velocityGradients && this._velocityGradients.length > 0) {
  1019. particle._currentVelocityGradient = this._velocityGradients[0];
  1020. particle._currentVelocity1 = particle._currentVelocityGradient.getFactor();
  1021. if (this._velocityGradients.length > 1) {
  1022. particle._currentVelocity2 = this._velocityGradients[1].getFactor();
  1023. } else {
  1024. particle._currentVelocity2 = particle._currentVelocity1;
  1025. }
  1026. }
  1027. // Color
  1028. if (!this._colorGradients || this._colorGradients.length === 0) {
  1029. var step = Scalar.RandomRange(0, 1.0);
  1030. Color4.LerpToRef(this.color1, this.color2, step, particle.color);
  1031. this.colorDead.subtractToRef(particle.color, this._colorDiff);
  1032. this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
  1033. } else {
  1034. particle._currentColorGradient = this._colorGradients[0];
  1035. particle._currentColorGradient.getColorToRef(particle.color);
  1036. particle._currentColor1.copyFrom(particle.color);
  1037. if (this._colorGradients.length > 1) {
  1038. this._colorGradients[1].getColorToRef(particle._currentColor2);
  1039. } else {
  1040. particle._currentColor2.copyFrom(particle.color);
  1041. }
  1042. }
  1043. // Sheet
  1044. if (this._isAnimationSheetEnabled) {
  1045. particle._initialStartSpriteCellID = this.startSpriteCellID;
  1046. particle._initialEndSpriteCellID = this.endSpriteCellID;
  1047. }
  1048. }
  1049. }
  1050. /** @hidden */
  1051. public static _GetAttributeNamesOrOptions(isAnimationSheetEnabled = false, isBillboardBased = false): string[] {
  1052. var attributeNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "angle", "offset", "size"];
  1053. if (isAnimationSheetEnabled) {
  1054. attributeNamesOrOptions.push("cellIndex");
  1055. }
  1056. if (!isBillboardBased) {
  1057. attributeNamesOrOptions.push("direction");
  1058. }
  1059. return attributeNamesOrOptions;
  1060. }
  1061. public static _GetEffectCreationOptions(isAnimationSheetEnabled = false): string[] {
  1062. var effectCreationOption = ["invView", "view", "projection", "vClipPlane", "textureMask", "translationPivot", "eyePosition"];
  1063. if (isAnimationSheetEnabled) {
  1064. effectCreationOption.push("particlesInfos")
  1065. }
  1066. return effectCreationOption;
  1067. }
  1068. private _getEffect(): Effect {
  1069. if (this._customEffect) {
  1070. return this._customEffect;
  1071. };
  1072. var defines = [];
  1073. if (this._scene.clipPlane) {
  1074. defines.push("#define CLIPPLANE");
  1075. }
  1076. if (this._isAnimationSheetEnabled) {
  1077. defines.push("#define ANIMATESHEET");
  1078. }
  1079. if (this._isBillboardBased) {
  1080. defines.push("#define BILLBOARD");
  1081. switch (this.billboardMode) {
  1082. case AbstractMesh.BILLBOARDMODE_Y:
  1083. defines.push("#define BILLBOARDY");
  1084. break;
  1085. case AbstractMesh.BILLBOARDMODE_ALL:
  1086. default:
  1087. break;
  1088. }
  1089. }
  1090. // Effect
  1091. var join = defines.join("\n");
  1092. if (this._cachedDefines !== join) {
  1093. this._cachedDefines = join;
  1094. var attributesNamesOrOptions = ParticleSystem._GetAttributeNamesOrOptions(this._isAnimationSheetEnabled, this._isBillboardBased);
  1095. var effectCreationOption = ParticleSystem._GetEffectCreationOptions(this._isAnimationSheetEnabled);
  1096. this._effect = this._scene.getEngine().createEffect(
  1097. "particles",
  1098. attributesNamesOrOptions,
  1099. effectCreationOption,
  1100. ["diffuseSampler"], join);
  1101. }
  1102. return this._effect;
  1103. }
  1104. /**
  1105. * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
  1106. * @param preWarmOnly will prevent the system from updating the vertex buffer (default is false)
  1107. */
  1108. public animate(preWarmOnly = false): void {
  1109. if (!this._started)
  1110. return;
  1111. if (!preWarmOnly) {
  1112. var effect = this._getEffect();
  1113. // Check
  1114. if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady())
  1115. return;
  1116. if (this._currentRenderId === this._scene.getRenderId()) {
  1117. return;
  1118. }
  1119. this._currentRenderId = this._scene.getRenderId();
  1120. }
  1121. this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene.getAnimationRatio());
  1122. // determine the number of particles we need to create
  1123. var newParticles;
  1124. if (this.manualEmitCount > -1) {
  1125. newParticles = this.manualEmitCount;
  1126. this._newPartsExcess = 0;
  1127. this.manualEmitCount = 0;
  1128. } else {
  1129. newParticles = ((this.emitRate * this._scaledUpdateSpeed) >> 0);
  1130. this._newPartsExcess += this.emitRate * this._scaledUpdateSpeed - newParticles;
  1131. }
  1132. if (this._newPartsExcess > 1.0) {
  1133. newParticles += this._newPartsExcess >> 0;
  1134. this._newPartsExcess -= this._newPartsExcess >> 0;
  1135. }
  1136. this._alive = false;
  1137. if (!this._stopped) {
  1138. this._actualFrame += this._scaledUpdateSpeed;
  1139. if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration)
  1140. this.stop();
  1141. } else {
  1142. newParticles = 0;
  1143. }
  1144. this._update(newParticles);
  1145. // Stopped?
  1146. if (this._stopped) {
  1147. if (!this._alive) {
  1148. this._started = false;
  1149. if (this.onAnimationEnd) {
  1150. this.onAnimationEnd();
  1151. }
  1152. if (this.disposeOnStop) {
  1153. this._scene._toBeDisposed.push(this);
  1154. }
  1155. }
  1156. }
  1157. if (!preWarmOnly) {
  1158. // Update VBO
  1159. var offset = 0;
  1160. for (var index = 0; index < this._particles.length; index++) {
  1161. var particle = this._particles[index];
  1162. this._appendParticleVertices(offset, particle);
  1163. offset += this._useInstancing ? 1 : 4;
  1164. }
  1165. if (this._vertexBuffer) {
  1166. this._vertexBuffer.update(this._vertexData);
  1167. }
  1168. }
  1169. if (this.manualEmitCount === 0 && this.disposeOnStop) {
  1170. this.stop();
  1171. }
  1172. }
  1173. private _appendParticleVertices(offset: number, particle: Particle) {
  1174. this._appendParticleVertex(offset++, particle, 0, 0);
  1175. if (!this._useInstancing) {
  1176. this._appendParticleVertex(offset++, particle, 1, 0);
  1177. this._appendParticleVertex(offset++, particle, 1, 1);
  1178. this._appendParticleVertex(offset++, particle, 0, 1);
  1179. }
  1180. }
  1181. /**
  1182. * Rebuilds the particle system.
  1183. */
  1184. public rebuild(): void {
  1185. this._createIndexBuffer();
  1186. if (this._vertexBuffer) {
  1187. this._vertexBuffer._rebuild();
  1188. }
  1189. }
  1190. /**
  1191. * Is this system ready to be used/rendered
  1192. * @return true if the system is ready
  1193. */
  1194. public isReady(): boolean {
  1195. var effect = this._getEffect();
  1196. if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
  1197. return false;
  1198. }
  1199. return true;
  1200. }
  1201. /**
  1202. * Renders the particle system in its current state.
  1203. * @returns the current number of particles
  1204. */
  1205. public render(): number {
  1206. var effect = this._getEffect();
  1207. // Check
  1208. if (!this.isReady() || !this._particles.length) {
  1209. return 0;
  1210. }
  1211. var engine = this._scene.getEngine();
  1212. // Render
  1213. engine.enableEffect(effect);
  1214. engine.setState(false);
  1215. var viewMatrix = this._scene.getViewMatrix();
  1216. effect.setTexture("diffuseSampler", this.particleTexture);
  1217. effect.setMatrix("view", viewMatrix);
  1218. effect.setMatrix("projection", this._scene.getProjectionMatrix());
  1219. if (this._isAnimationSheetEnabled && this.particleTexture) {
  1220. var baseSize = this.particleTexture.getBaseSize();
  1221. effect.setFloat3("particlesInfos", this.spriteCellWidth / baseSize.width, this.spriteCellHeight / baseSize.height, baseSize.width / this.spriteCellWidth);
  1222. }
  1223. effect.setVector2("translationPivot", this.translationPivot);
  1224. effect.setFloat4("textureMask", this.textureMask.r, this.textureMask.g, this.textureMask.b, this.textureMask.a);
  1225. if (this._isBillboardBased) {
  1226. var camera = this._scene.activeCamera!;
  1227. effect.setVector3("eyePosition", camera.globalPosition);
  1228. }
  1229. if (this._scene.clipPlane) {
  1230. var clipPlane = this._scene.clipPlane;
  1231. var invView = viewMatrix.clone();
  1232. invView.invert();
  1233. effect.setMatrix("invView", invView);
  1234. effect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
  1235. }
  1236. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
  1237. // Draw order
  1238. switch(this.blendMode)
  1239. {
  1240. case ParticleSystem.BLENDMODE_ADD:
  1241. engine.setAlphaMode(Engine.ALPHA_ADD);
  1242. break;
  1243. case ParticleSystem.BLENDMODE_ONEONE:
  1244. engine.setAlphaMode(Engine.ALPHA_ONEONE);
  1245. break;
  1246. case ParticleSystem.BLENDMODE_STANDARD:
  1247. engine.setAlphaMode(Engine.ALPHA_COMBINE);
  1248. break;
  1249. }
  1250. if (this.forceDepthWrite) {
  1251. engine.setDepthWrite(true);
  1252. }
  1253. if (this._useInstancing) {
  1254. engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._particles.length);
  1255. engine.unbindInstanceAttributes();
  1256. } else {
  1257. engine.drawElementsType(Material.TriangleFillMode, 0, this._particles.length * 6);
  1258. }
  1259. engine.setAlphaMode(Engine.ALPHA_DISABLE);
  1260. return this._particles.length;
  1261. }
  1262. /**
  1263. * Disposes the particle system and free the associated resources
  1264. * @param disposeTexture defines if the particule texture must be disposed as well (true by default)
  1265. */
  1266. public dispose(disposeTexture = true): void {
  1267. if (this._vertexBuffer) {
  1268. this._vertexBuffer.dispose();
  1269. this._vertexBuffer = null;
  1270. }
  1271. if (this._spriteBuffer) {
  1272. this._spriteBuffer.dispose();
  1273. this._spriteBuffer = null;
  1274. }
  1275. if (this._indexBuffer) {
  1276. this._scene.getEngine()._releaseBuffer(this._indexBuffer);
  1277. this._indexBuffer = null;
  1278. }
  1279. if (disposeTexture && this.particleTexture) {
  1280. this.particleTexture.dispose();
  1281. this.particleTexture = null;
  1282. }
  1283. if (disposeTexture && this.noiseTexture) {
  1284. this.noiseTexture.dispose();
  1285. this.noiseTexture = null;
  1286. }
  1287. this._removeFromRoot();
  1288. // Remove from scene
  1289. var index = this._scene.particleSystems.indexOf(this);
  1290. if (index > -1) {
  1291. this._scene.particleSystems.splice(index, 1);
  1292. }
  1293. // Callback
  1294. this.onDisposeObservable.notifyObservers(this);
  1295. this.onDisposeObservable.clear();
  1296. }
  1297. /**
  1298. * Creates a Sphere Emitter for the particle system. (emits along the sphere radius)
  1299. * @param radius The radius of the sphere to emit from
  1300. * @param radiusRange The range of the sphere to emit from [0-1] 0 Surface Only, 1 Entire Radius
  1301. * @returns the emitter
  1302. */
  1303. public createSphereEmitter(radius = 1, radiusRange = 1): SphereParticleEmitter {
  1304. var particleEmitter = new SphereParticleEmitter(radius, radiusRange);
  1305. this.particleEmitterType = particleEmitter;
  1306. return particleEmitter;
  1307. }
  1308. /**
  1309. * Creates a Directed Sphere Emitter for the particle system. (emits between direction1 and direction2)
  1310. * @param radius The radius of the sphere to emit from
  1311. * @param direction1 Particles are emitted between the direction1 and direction2 from within the sphere
  1312. * @param direction2 Particles are emitted between the direction1 and direction2 from within the sphere
  1313. * @returns the emitter
  1314. */
  1315. public createDirectedSphereEmitter(radius = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)): SphereDirectedParticleEmitter {
  1316. var particleEmitter = new SphereDirectedParticleEmitter(radius, direction1, direction2)
  1317. this.particleEmitterType = particleEmitter;
  1318. return particleEmitter;
  1319. }
  1320. /**
  1321. * Creates a Cone Emitter for the particle system. (emits from the cone to the particle position)
  1322. * @param radius The radius of the cone to emit from
  1323. * @param angle The base angle of the cone
  1324. * @returns the emitter
  1325. */
  1326. public createConeEmitter(radius = 1, angle = Math.PI / 4): ConeParticleEmitter {
  1327. var particleEmitter = new ConeParticleEmitter(radius, angle);
  1328. this.particleEmitterType = particleEmitter;
  1329. return particleEmitter;
  1330. }
  1331. // this method needs to be changed when breaking changes will be allowed to match the sphere and cone methods and properties direction1,2 and minEmitBox,maxEmitBox to be removed from the system.
  1332. /**
  1333. * Creates a Box Emitter for the particle system. (emits between direction1 and direction2 from withing the box defined by minEmitBox and maxEmitBox)
  1334. * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
  1335. * @param direction2 Particles are emitted between the direction1 and direction2 from within the box
  1336. * @param minEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
  1337. * @param maxEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
  1338. * @returns the emitter
  1339. */
  1340. public createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3): BoxParticleEmitter {
  1341. var particleEmitter = new BoxParticleEmitter();
  1342. this.particleEmitterType = particleEmitter;
  1343. this.direction1 = direction1;
  1344. this.direction2 = direction2;
  1345. this.minEmitBox = minEmitBox;
  1346. this.maxEmitBox = maxEmitBox;
  1347. return particleEmitter;
  1348. }
  1349. // Clone
  1350. /**
  1351. * Clones the particle system.
  1352. * @param name The name of the cloned object
  1353. * @param newEmitter The new emitter to use
  1354. * @returns the cloned particle system
  1355. */
  1356. public clone(name: string, newEmitter: any): ParticleSystem {
  1357. var custom: Nullable<Effect> = null;
  1358. var program: any = null;
  1359. if (this.customShader != null) {
  1360. program = this.customShader;
  1361. var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
  1362. custom = this._scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
  1363. } else if (this._customEffect) {
  1364. custom = this._customEffect;
  1365. }
  1366. var result = new ParticleSystem(name, this._capacity, this._scene, custom);
  1367. result.customShader = program;
  1368. Tools.DeepCopy(this, result, ["particles", "customShader"]);
  1369. if (newEmitter === undefined) {
  1370. newEmitter = this.emitter;
  1371. }
  1372. result.emitter = newEmitter;
  1373. if (this.particleTexture) {
  1374. result.particleTexture = new Texture(this.particleTexture.url, this._scene);
  1375. }
  1376. if (!this.preventAutoStart) {
  1377. result.start();
  1378. }
  1379. return result;
  1380. }
  1381. /**
  1382. * Serializes the particle system to a JSON object.
  1383. * @returns the JSON object
  1384. */
  1385. public serialize(): any {
  1386. var serializationObject: any = {};
  1387. ParticleSystem._Serialize(serializationObject, this);
  1388. serializationObject.textureMask = this.textureMask.asArray();
  1389. serializationObject.customShader = this.customShader;
  1390. serializationObject.preventAutoStart = this.preventAutoStart;
  1391. serializationObject.isAnimationSheetEnabled = this._isAnimationSheetEnabled;
  1392. return serializationObject;
  1393. }
  1394. /** @hidden */
  1395. public static _Serialize(serializationObject: any, particleSystem: IParticleSystem) {
  1396. serializationObject.name = particleSystem.name;
  1397. serializationObject.id = particleSystem.id;
  1398. serializationObject.capacity = particleSystem.getCapacity();
  1399. // Emitter
  1400. if ((<AbstractMesh>particleSystem.emitter).position) {
  1401. var emitterMesh = (<AbstractMesh>particleSystem.emitter);
  1402. serializationObject.emitterId = emitterMesh.id;
  1403. } else {
  1404. var emitterPosition = (<Vector3>particleSystem.emitter);
  1405. serializationObject.emitter = emitterPosition.asArray();
  1406. }
  1407. // Emitter
  1408. if (particleSystem.particleEmitterType) {
  1409. serializationObject.particleEmitterType = particleSystem.particleEmitterType.serialize();
  1410. }
  1411. if (particleSystem.particleTexture) {
  1412. serializationObject.textureName = particleSystem.particleTexture.name;
  1413. }
  1414. // Animations
  1415. Animation.AppendSerializedAnimations(particleSystem, serializationObject);
  1416. // Particle system
  1417. serializationObject.renderingGroupId = particleSystem.renderingGroupId;
  1418. serializationObject.isBillboardBased = particleSystem.isBillboardBased;
  1419. serializationObject.minAngularSpeed = particleSystem.minAngularSpeed;
  1420. serializationObject.maxAngularSpeed = particleSystem.maxAngularSpeed;
  1421. serializationObject.minSize = particleSystem.minSize;
  1422. serializationObject.maxSize = particleSystem.maxSize;
  1423. serializationObject.minScaleX = particleSystem.minScaleX;
  1424. serializationObject.maxScaleX = particleSystem.maxScaleX;
  1425. serializationObject.minScaleY = particleSystem.minScaleY;
  1426. serializationObject.maxScaleY = particleSystem.maxScaleY;
  1427. serializationObject.minEmitPower = particleSystem.minEmitPower;
  1428. serializationObject.maxEmitPower = particleSystem.maxEmitPower;
  1429. serializationObject.minLifeTime = particleSystem.minLifeTime;
  1430. serializationObject.maxLifeTime = particleSystem.maxLifeTime;
  1431. serializationObject.emitRate = particleSystem.emitRate;
  1432. serializationObject.gravity = particleSystem.gravity.asArray();
  1433. serializationObject.noiseStrength = particleSystem.noiseStrength.asArray();
  1434. serializationObject.color1 = particleSystem.color1.asArray();
  1435. serializationObject.color2 = particleSystem.color2.asArray();
  1436. serializationObject.colorDead = particleSystem.colorDead.asArray();
  1437. serializationObject.updateSpeed = particleSystem.updateSpeed;
  1438. serializationObject.targetStopDuration = particleSystem.targetStopDuration;
  1439. serializationObject.blendMode = particleSystem.blendMode;
  1440. serializationObject.preWarmCycles = particleSystem.preWarmCycles;
  1441. serializationObject.preWarmStepOffset = particleSystem.preWarmStepOffset;
  1442. serializationObject.minInitialRotation = particleSystem.minInitialRotation;
  1443. serializationObject.maxInitialRotation = particleSystem.maxInitialRotation;
  1444. serializationObject.startSpriteCellID = particleSystem.startSpriteCellID;
  1445. serializationObject.endSpriteCellID = particleSystem.endSpriteCellID;
  1446. serializationObject.spriteCellChangeSpeed = particleSystem.spriteCellChangeSpeed;
  1447. serializationObject.spriteCellWidth = particleSystem.spriteCellWidth;
  1448. serializationObject.spriteCellHeight = particleSystem.spriteCellHeight;
  1449. let colorGradients = particleSystem.getColorGradients();
  1450. if (colorGradients) {
  1451. serializationObject.colorGradients = [];
  1452. for (var colorGradient of colorGradients) {
  1453. var serializedGradient: any = {
  1454. gradient: colorGradient.gradient,
  1455. color1: colorGradient.color1.asArray()
  1456. };
  1457. if (colorGradient.color2) {
  1458. serializedGradient.color2 = colorGradient.color2.asArray();
  1459. }
  1460. serializationObject.colorGradients.push(serializedGradient);
  1461. }
  1462. }
  1463. let sizeGradients = particleSystem.getSizeGradients();
  1464. if (sizeGradients) {
  1465. serializationObject.sizeGradients = [];
  1466. for (var sizeGradient of sizeGradients) {
  1467. var serializedGradient: any = {
  1468. gradient: sizeGradient.gradient,
  1469. factor1: sizeGradient.factor1
  1470. };
  1471. if (sizeGradient.factor2 !== undefined) {
  1472. serializedGradient.factor2 = sizeGradient.factor2;
  1473. }
  1474. serializationObject.sizeGradients.push(serializedGradient);
  1475. }
  1476. }
  1477. let angularSpeedGradients = particleSystem.getAngularSpeedGradients();
  1478. if (angularSpeedGradients) {
  1479. serializationObject.angularSpeedGradients = [];
  1480. for (var angularSpeedGradient of angularSpeedGradients) {
  1481. var serializedGradient: any = {
  1482. gradient: angularSpeedGradient.gradient,
  1483. factor1: angularSpeedGradient.factor1
  1484. };
  1485. if (angularSpeedGradient.factor2 !== undefined) {
  1486. serializedGradient.factor2 = angularSpeedGradient.factor2;
  1487. }
  1488. serializationObject.angularSpeedGradients.push(serializedGradient);
  1489. }
  1490. }
  1491. let velocityGradients = particleSystem.getVelocityGradients();
  1492. if (velocityGradients) {
  1493. serializationObject.velocityGradients = [];
  1494. for (var velocityGradient of velocityGradients) {
  1495. var serializedGradient: any = {
  1496. gradient: velocityGradient.gradient,
  1497. factor1: velocityGradient.factor1
  1498. };
  1499. if (velocityGradient.factor2 !== undefined) {
  1500. serializedGradient.factor2 = velocityGradient.factor2;
  1501. }
  1502. serializationObject.velocityGradients.push(serializedGradient);
  1503. }
  1504. }
  1505. if (particleSystem.noiseTexture && particleSystem.noiseTexture instanceof ProceduralTexture) {
  1506. const noiseTexture = particleSystem.noiseTexture as ProceduralTexture;
  1507. serializationObject.noiseTexture = noiseTexture.serialize();
  1508. }
  1509. }
  1510. /** @hidden */
  1511. public static _Parse(parsedParticleSystem: any, particleSystem: IParticleSystem, scene: Scene, rootUrl: string) {
  1512. // Texture
  1513. if (parsedParticleSystem.textureName) {
  1514. particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
  1515. particleSystem.particleTexture.name = parsedParticleSystem.textureName;
  1516. }
  1517. // Emitter
  1518. if (parsedParticleSystem.emitterId === undefined) {
  1519. particleSystem.emitter = Vector3.Zero();
  1520. }
  1521. else if (parsedParticleSystem.emitterId) {
  1522. particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
  1523. } else {
  1524. particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
  1525. }
  1526. // Misc.
  1527. if (parsedParticleSystem.renderingGroupId !== undefined) {
  1528. particleSystem.renderingGroupId = parsedParticleSystem.renderingGroupId;
  1529. }
  1530. if (parsedParticleSystem.isBillboardBased !== undefined) {
  1531. particleSystem.isBillboardBased = parsedParticleSystem.isBillboardBased;
  1532. }
  1533. // Animations
  1534. if (parsedParticleSystem.animations) {
  1535. for (var animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
  1536. var parsedAnimation = parsedParticleSystem.animations[animationIndex];
  1537. particleSystem.animations.push(Animation.Parse(parsedAnimation));
  1538. }
  1539. }
  1540. if (parsedParticleSystem.autoAnimate) {
  1541. scene.beginAnimation(particleSystem, parsedParticleSystem.autoAnimateFrom, parsedParticleSystem.autoAnimateTo, parsedParticleSystem.autoAnimateLoop, parsedParticleSystem.autoAnimateSpeed || 1.0);
  1542. }
  1543. // Particle system
  1544. particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
  1545. particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
  1546. particleSystem.minSize = parsedParticleSystem.minSize;
  1547. particleSystem.maxSize = parsedParticleSystem.maxSize;
  1548. if (parsedParticleSystem.minScaleX) {
  1549. particleSystem.minScaleX = parsedParticleSystem.minScaleX;
  1550. particleSystem.maxScaleX = parsedParticleSystem.maxScaleX;
  1551. particleSystem.minScaleY = parsedParticleSystem.minScaleY;
  1552. particleSystem.maxScaleY = parsedParticleSystem.maxScaleY;
  1553. }
  1554. if (parsedParticleSystem.preWarmCycles !== undefined) {
  1555. particleSystem.preWarmCycles = parsedParticleSystem.preWarmCycles;
  1556. particleSystem.preWarmStepOffset = parsedParticleSystem.preWarmStepOffset;
  1557. }
  1558. if (parsedParticleSystem.minInitialRotation !== undefined) {
  1559. particleSystem.minInitialRotation = parsedParticleSystem.minInitialRotation;
  1560. particleSystem.maxInitialRotation = parsedParticleSystem.maxInitialRotation;
  1561. }
  1562. particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
  1563. particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
  1564. particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
  1565. particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
  1566. particleSystem.emitRate = parsedParticleSystem.emitRate;
  1567. particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
  1568. if (parsedParticleSystem.noiseStrength) {
  1569. particleSystem.noiseStrength = Vector3.FromArray(parsedParticleSystem.noiseStrength);
  1570. }
  1571. particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
  1572. particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
  1573. particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
  1574. particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
  1575. particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
  1576. particleSystem.blendMode = parsedParticleSystem.blendMode;
  1577. if (parsedParticleSystem.colorGradients) {
  1578. for (var colorGradient of parsedParticleSystem.colorGradients) {
  1579. particleSystem.addColorGradient(colorGradient.gradient, Color4.FromArray(colorGradient.color1), colorGradient.color2 ? Color4.FromArray(colorGradient.color2) : undefined);
  1580. }
  1581. }
  1582. if (parsedParticleSystem.sizeGradients) {
  1583. for (var sizeGradient of parsedParticleSystem.sizeGradients) {
  1584. particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ? sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
  1585. }
  1586. }
  1587. if (parsedParticleSystem.angularSpeedGradients) {
  1588. for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
  1589. particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ? angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
  1590. }
  1591. }
  1592. if (parsedParticleSystem.velocityGradients) {
  1593. for (var velocityGradient of parsedParticleSystem.velocityGradients) {
  1594. particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ? velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
  1595. }
  1596. }
  1597. if (parsedParticleSystem.noiseTexture) {
  1598. particleSystem.noiseTexture = ProceduralTexture.Parse(parsedParticleSystem.noiseTexture, scene, rootUrl);
  1599. }
  1600. // Emitter
  1601. let emitterType: IParticleEmitterType;
  1602. if (parsedParticleSystem.particleEmitterType) {
  1603. switch (parsedParticleSystem.particleEmitterType.type) {
  1604. case "SphereParticleEmitter":
  1605. emitterType = new SphereParticleEmitter();
  1606. break;
  1607. case "SphereDirectedParticleEmitter":
  1608. emitterType = new SphereDirectedParticleEmitter();
  1609. break;
  1610. case "ConeEmitter":
  1611. case "ConeParticleEmitter":
  1612. emitterType = new ConeParticleEmitter();
  1613. break;
  1614. case "BoxEmitter":
  1615. case "BoxParticleEmitter":
  1616. default:
  1617. emitterType = new BoxParticleEmitter();
  1618. break;
  1619. }
  1620. emitterType.parse(parsedParticleSystem.particleEmitterType);
  1621. } else {
  1622. emitterType = new BoxParticleEmitter();
  1623. emitterType.parse(parsedParticleSystem);
  1624. }
  1625. particleSystem.particleEmitterType = emitterType;
  1626. // Animation sheet
  1627. particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
  1628. particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
  1629. particleSystem.spriteCellWidth = parsedParticleSystem.spriteCellWidth;
  1630. particleSystem.spriteCellHeight = parsedParticleSystem.spriteCellHeight;
  1631. particleSystem.spriteCellChangeSpeed = parsedParticleSystem.spriteCellChangeSpeed;
  1632. }
  1633. /**
  1634. * Parses a JSON object to create a particle system.
  1635. * @param parsedParticleSystem The JSON object to parse
  1636. * @param scene The scene to create the particle system in
  1637. * @param rootUrl The root url to use to load external dependencies like texture
  1638. * @returns the Parsed particle system
  1639. */
  1640. public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): ParticleSystem {
  1641. var name = parsedParticleSystem.name;
  1642. var custom: Nullable<Effect> = null;
  1643. var program: any = null;
  1644. if (parsedParticleSystem.customShader) {
  1645. program = parsedParticleSystem.customShader;
  1646. var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
  1647. custom = scene.getEngine().createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
  1648. }
  1649. var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, scene, custom, parsedParticleSystem.isAnimationSheetEnabled);
  1650. particleSystem.customShader = program;
  1651. if (parsedParticleSystem.id) {
  1652. particleSystem.id = parsedParticleSystem.id;
  1653. }
  1654. // Auto start
  1655. if (parsedParticleSystem.preventAutoStart) {
  1656. particleSystem.preventAutoStart = parsedParticleSystem.preventAutoStart;
  1657. }
  1658. ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
  1659. particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
  1660. if (!particleSystem.preventAutoStart) {
  1661. particleSystem.start();
  1662. }
  1663. return particleSystem;
  1664. }
  1665. }
  1666. }