babylon.particleSystem.ts 86 KB

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