particleSystem.ts 112 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804
  1. import { Nullable } from "../types";
  2. import { FactorGradient, ColorGradient, Color3Gradient, GradientHelper } from "../Misc/gradients";
  3. import { Observable, Observer } from "../Misc/observable";
  4. import { Vector3, Matrix, TmpVectors, Vector4 } from "../Maths/math.vector";
  5. import { Scalar } from "../Maths/math.scalar";
  6. import { VertexBuffer } from "../Meshes/buffer";
  7. import { Buffer } from "../Meshes/buffer";
  8. import { Effect } from "../Materials/effect";
  9. import { ImageProcessingConfiguration } from "../Materials/imageProcessingConfiguration";
  10. import { RawTexture } from "../Materials/Textures/rawTexture";
  11. import { EngineStore } from "../Engines/engineStore";
  12. import { IDisposable } from "../scene";
  13. import { BoxParticleEmitter, IParticleEmitterType, HemisphericParticleEmitter, SphereParticleEmitter, SphereDirectedParticleEmitter, CylinderParticleEmitter, ConeParticleEmitter, PointParticleEmitter, MeshParticleEmitter, CylinderDirectedParticleEmitter } from "../Particles/EmitterTypes/index";
  14. import { IParticleSystem } from "./IParticleSystem";
  15. import { BaseParticleSystem } from "./baseParticleSystem";
  16. import { Particle } from "./particle";
  17. import { SubEmitter, SubEmitterType } from "./subEmitter";
  18. import { Constants } from "../Engines/constants";
  19. import { SerializationHelper } from "../Misc/decorators";
  20. import { _TypeStore } from '../Misc/typeStore';
  21. import { IAnimatable } from '../Animations/animatable.interface';
  22. import "../Shaders/particles.fragment";
  23. import "../Shaders/particles.vertex";
  24. import { DataBuffer } from '../Meshes/dataBuffer';
  25. import { Color4, Color3, TmpColors } from '../Maths/math.color';
  26. import { ISize } from '../Maths/math.size';
  27. import { BaseTexture } from '../Materials/Textures/baseTexture';
  28. import { ThinEngine } from '../Engines/thinEngine';
  29. import { ThinMaterialHelper } from '../Materials/thinMaterialHelper';
  30. import "../Engines/Extensions/engine.alpha";
  31. declare type AbstractMesh = import("../Meshes/abstractMesh").AbstractMesh;
  32. declare type ProceduralTexture = import("../Materials/Textures/Procedurals/proceduralTexture").ProceduralTexture;
  33. declare type Scene = import("../scene").Scene;
  34. /**
  35. * This represents a particle system in Babylon.
  36. * 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.
  37. * Particles can take different shapes while emitted like box, sphere, cone or you can write your custom function.
  38. * @example https://doc.babylonjs.com/babylon101/particles
  39. */
  40. export class ParticleSystem extends BaseParticleSystem implements IDisposable, IAnimatable, IParticleSystem {
  41. /**
  42. * Billboard mode will only apply to Y axis
  43. */
  44. public static readonly BILLBOARDMODE_Y = Constants.PARTICLES_BILLBOARDMODE_Y;
  45. /**
  46. * Billboard mode will apply to all axes
  47. */
  48. public static readonly BILLBOARDMODE_ALL = Constants.PARTICLES_BILLBOARDMODE_ALL;
  49. /**
  50. * Special billboard mode where the particle will be biilboard to the camera but rotated to align with direction
  51. */
  52. public static readonly BILLBOARDMODE_STRETCHED = Constants.PARTICLES_BILLBOARDMODE_STRETCHED;
  53. /**
  54. * This function can be defined to provide custom update for active particles.
  55. * This function will be called instead of regular update (age, position, color, etc.).
  56. * Do not forget that this function will be called on every frame so try to keep it simple and fast :)
  57. */
  58. public updateFunction: (particles: Particle[]) => void;
  59. private _emitterWorldMatrix: Matrix;
  60. /**
  61. * This function can be defined to specify initial direction for every new particle.
  62. * It by default use the emitterType defined function
  63. */
  64. public startDirectionFunction: (worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle, isLocal: boolean) => void;
  65. /**
  66. * This function can be defined to specify initial position for every new particle.
  67. * It by default use the emitterType defined function
  68. */
  69. public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle, isLocal: boolean) => void;
  70. /**
  71. * @hidden
  72. */
  73. public _inheritedVelocityOffset = new Vector3();
  74. /**
  75. * An event triggered when the system is disposed
  76. */
  77. public onDisposeObservable = new Observable<IParticleSystem>();
  78. /**
  79. * An event triggered when the system is stopped
  80. */
  81. public onStoppedObservable = new Observable<IParticleSystem>();
  82. private _onDisposeObserver: Nullable<Observer<IParticleSystem>>;
  83. /**
  84. * Sets a callback that will be triggered when the system is disposed
  85. */
  86. public set onDispose(callback: () => void) {
  87. if (this._onDisposeObserver) {
  88. this.onDisposeObservable.remove(this._onDisposeObserver);
  89. }
  90. this._onDisposeObserver = this.onDisposeObservable.add(callback);
  91. }
  92. private _particles = new Array<Particle>();
  93. private _epsilon: number;
  94. private _capacity: number;
  95. private _stockParticles = new Array<Particle>();
  96. private _newPartsExcess = 0;
  97. private _vertexData: Float32Array;
  98. private _vertexBuffer: Nullable<Buffer>;
  99. private _vertexBuffers: { [key: string]: VertexBuffer } = {};
  100. private _spriteBuffer: Nullable<Buffer>;
  101. private _indexBuffer: Nullable<DataBuffer>;
  102. private _effect: Effect;
  103. private _customEffect: { [blendMode: number] : Nullable<Effect> };
  104. private _cachedDefines: string;
  105. private _scaledColorStep = new Color4(0, 0, 0, 0);
  106. private _colorDiff = new Color4(0, 0, 0, 0);
  107. private _scaledDirection = Vector3.Zero();
  108. private _scaledGravity = Vector3.Zero();
  109. private _currentRenderId = -1;
  110. private _alive: boolean;
  111. private _useInstancing = false;
  112. private _vertexArrayObject: Nullable<WebGLVertexArrayObject>;
  113. private _started = false;
  114. private _stopped = false;
  115. private _actualFrame = 0;
  116. private _scaledUpdateSpeed: number;
  117. private _vertexBufferSize: number;
  118. /** @hidden */
  119. public _currentEmitRateGradient: Nullable<FactorGradient>;
  120. /** @hidden */
  121. public _currentEmitRate1 = 0;
  122. /** @hidden */
  123. public _currentEmitRate2 = 0;
  124. /** @hidden */
  125. public _currentStartSizeGradient: Nullable<FactorGradient>;
  126. /** @hidden */
  127. public _currentStartSize1 = 0;
  128. /** @hidden */
  129. public _currentStartSize2 = 0;
  130. private readonly _rawTextureWidth = 256;
  131. private _rampGradientsTexture: Nullable<RawTexture>;
  132. private _useRampGradients = false;
  133. /** Gets or sets a matrix to use to compute projection */
  134. public defaultProjectionMatrix: Matrix;
  135. /** Gets or sets a matrix to use to compute view */
  136. public defaultViewMatrix: Matrix;
  137. /** Gets or sets a boolean indicating that ramp gradients must be used
  138. * @see https://doc.babylonjs.com/babylon101/particles#ramp-gradients
  139. */
  140. public get useRampGradients(): boolean {
  141. return this._useRampGradients;
  142. }
  143. public set useRampGradients(value: boolean) {
  144. if (this._useRampGradients === value) {
  145. return;
  146. }
  147. this._useRampGradients = value;
  148. this._resetEffect();
  149. }
  150. // Sub-emitters
  151. /**
  152. * The Sub-emitters templates that will be used to generate the sub particle system to be associated with the system, this property is used by the root particle system only.
  153. * When a particle is spawned, an array will be chosen at random and all the emitters in that array will be attached to the particle. (Default: [])
  154. */
  155. public subEmitters: Array<ParticleSystem | SubEmitter | Array<SubEmitter>>;
  156. // the subEmitters field above converted to a constant type
  157. private _subEmitters: Array<Array<SubEmitter>>;
  158. /**
  159. * @hidden
  160. * If the particle systems emitter should be disposed when the particle system is disposed
  161. */
  162. public _disposeEmitterOnDispose = false;
  163. /**
  164. * The current active Sub-systems, this property is used by the root particle system only.
  165. */
  166. public activeSubSystems: Array<ParticleSystem>;
  167. /**
  168. * Specifies if the particles are updated in emitter local space or world space
  169. */
  170. public isLocal = false;
  171. private _rootParticleSystem: Nullable<ParticleSystem>;
  172. //end of Sub-emitter
  173. /**
  174. * Gets the current list of active particles
  175. */
  176. public get particles(): Particle[] {
  177. return this._particles;
  178. }
  179. /**
  180. * Gets the number of particles active at the same time.
  181. * @returns The number of active particles.
  182. */
  183. public getActiveCount() {
  184. return this._particles.length;
  185. }
  186. /**
  187. * Returns the string "ParticleSystem"
  188. * @returns a string containing the class name
  189. */
  190. public getClassName(): string {
  191. return "ParticleSystem";
  192. }
  193. /**
  194. * Gets a boolean indicating that the system is stopping
  195. * @returns true if the system is currently stopping
  196. */
  197. public isStopping() {
  198. return this._stopped && this.isAlive();
  199. }
  200. /**
  201. * Gets the custom effect used to render the particles
  202. * @param blendMode Blend mode for which the effect should be retrieved
  203. * @returns The effect
  204. */
  205. public getCustomEffect(blendMode: number = 0): Nullable<Effect> {
  206. return this._customEffect[blendMode] ?? this._customEffect[0];
  207. }
  208. /**
  209. * Sets the custom effect used to render the particles
  210. * @param effect The effect to set
  211. * @param blendMode Blend mode for which the effect should be set
  212. */
  213. public setCustomEffect(effect: Nullable<Effect>, blendMode: number = 0) {
  214. this._customEffect[blendMode] = effect;
  215. }
  216. /** @hidden */
  217. private _onBeforeDrawParticlesObservable: Nullable<Observable<Nullable<Effect>>> = null;
  218. /**
  219. * Observable that will be called just before the particles are drawn
  220. */
  221. public get onBeforeDrawParticlesObservable(): Observable<Nullable<Effect>> {
  222. if (!this._onBeforeDrawParticlesObservable) {
  223. this._onBeforeDrawParticlesObservable = new Observable<Nullable<Effect>>();
  224. }
  225. return this._onBeforeDrawParticlesObservable;
  226. }
  227. /**
  228. * Gets the name of the particle vertex shader
  229. */
  230. public get vertexShaderName(): string {
  231. return "particles";
  232. }
  233. /**
  234. * Instantiates a particle system.
  235. * 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.
  236. * @param name The name of the particle system
  237. * @param capacity The max number of particles alive at the same time
  238. * @param sceneOrEngine The scene the particle system belongs to or the engine to use if no scene
  239. * @param customEffect a custom effect used to change the way particles are rendered by default
  240. * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
  241. * @param epsilon Offset used to render the particles
  242. */
  243. constructor(name: string, capacity: number, sceneOrEngine: Scene | ThinEngine, customEffect: Nullable<Effect> = null, isAnimationSheetEnabled: boolean = false, epsilon: number = 0.01) {
  244. super(name);
  245. this._capacity = capacity;
  246. this._epsilon = epsilon;
  247. this._isAnimationSheetEnabled = isAnimationSheetEnabled;
  248. if (!sceneOrEngine || sceneOrEngine.getClassName() === "Scene") {
  249. this._scene = (sceneOrEngine as Scene) || EngineStore.LastCreatedScene;
  250. this._engine = this._scene.getEngine();
  251. this.uniqueId = this._scene.getUniqueId();
  252. this._scene.particleSystems.push(this);
  253. } else {
  254. this._engine = (sceneOrEngine as ThinEngine);
  255. this.defaultProjectionMatrix = Matrix.PerspectiveFovLH(0.8, 1, 0.1, 100);
  256. }
  257. if (this._engine.getCaps().vertexArrayObject) {
  258. this._vertexArrayObject = null;
  259. }
  260. // Setup the default processing configuration to the scene.
  261. this._attachImageProcessingConfiguration(null);
  262. this._customEffect = { 0: customEffect };
  263. this._useInstancing = this._engine.getCaps().instancedArrays;
  264. this._createIndexBuffer();
  265. this._createVertexBuffers();
  266. // Default emitter type
  267. this.particleEmitterType = new BoxParticleEmitter();
  268. // Update
  269. this.updateFunction = (particles: Particle[]): void => {
  270. let noiseTextureSize: Nullable<ISize> = null;
  271. let noiseTextureData: Nullable<Uint8Array> = null;
  272. if (this.noiseTexture) { // We need to get texture data back to CPU
  273. noiseTextureSize = this.noiseTexture.getSize();
  274. noiseTextureData = <Nullable<Uint8Array>>(this.noiseTexture.getContent());
  275. }
  276. for (var index = 0; index < particles.length; index++) {
  277. var particle = particles[index];
  278. let scaledUpdateSpeed = this._scaledUpdateSpeed;
  279. let previousAge = particle.age;
  280. particle.age += scaledUpdateSpeed;
  281. // Evaluate step to death
  282. if (particle.age > particle.lifeTime) {
  283. let diff = particle.age - previousAge;
  284. let oldDiff = particle.lifeTime - previousAge;
  285. scaledUpdateSpeed = (oldDiff * scaledUpdateSpeed) / diff;
  286. particle.age = particle.lifeTime;
  287. }
  288. let ratio = particle.age / particle.lifeTime;
  289. // Color
  290. if (this._colorGradients && this._colorGradients.length > 0) {
  291. GradientHelper.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
  292. if (currentGradient !== particle._currentColorGradient) {
  293. particle._currentColor1.copyFrom(particle._currentColor2);
  294. (<ColorGradient>nextGradient).getColorToRef(particle._currentColor2);
  295. particle._currentColorGradient = (<ColorGradient>currentGradient);
  296. }
  297. Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
  298. });
  299. }
  300. else {
  301. particle.colorStep.scaleToRef(scaledUpdateSpeed, this._scaledColorStep);
  302. particle.color.addInPlace(this._scaledColorStep);
  303. if (particle.color.a < 0) {
  304. particle.color.a = 0;
  305. }
  306. }
  307. // Angular speed
  308. if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {
  309. GradientHelper.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
  310. if (currentGradient !== particle._currentAngularSpeedGradient) {
  311. particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
  312. particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();
  313. particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
  314. }
  315. particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
  316. });
  317. }
  318. particle.angle += particle.angularSpeed * scaledUpdateSpeed;
  319. // Direction
  320. let directionScale = scaledUpdateSpeed;
  321. /// Velocity
  322. if (this._velocityGradients && this._velocityGradients.length > 0) {
  323. GradientHelper.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
  324. if (currentGradient !== particle._currentVelocityGradient) {
  325. particle._currentVelocity1 = particle._currentVelocity2;
  326. particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();
  327. particle._currentVelocityGradient = (<FactorGradient>currentGradient);
  328. }
  329. directionScale *= Scalar.Lerp(particle._currentVelocity1, particle._currentVelocity2, scale);
  330. });
  331. }
  332. particle.direction.scaleToRef(directionScale, this._scaledDirection);
  333. /// Limit velocity
  334. if (this._limitVelocityGradients && this._limitVelocityGradients.length > 0) {
  335. GradientHelper.GetCurrentGradient(ratio, this._limitVelocityGradients, (currentGradient, nextGradient, scale) => {
  336. if (currentGradient !== particle._currentLimitVelocityGradient) {
  337. particle._currentLimitVelocity1 = particle._currentLimitVelocity2;
  338. particle._currentLimitVelocity2 = (<FactorGradient>nextGradient).getFactor();
  339. particle._currentLimitVelocityGradient = (<FactorGradient>currentGradient);
  340. }
  341. let limitVelocity = Scalar.Lerp(particle._currentLimitVelocity1, particle._currentLimitVelocity2, scale);
  342. let currentVelocity = particle.direction.length();
  343. if (currentVelocity > limitVelocity) {
  344. particle.direction.scaleInPlace(this.limitVelocityDamping);
  345. }
  346. });
  347. }
  348. /// Drag
  349. if (this._dragGradients && this._dragGradients.length > 0) {
  350. GradientHelper.GetCurrentGradient(ratio, this._dragGradients, (currentGradient, nextGradient, scale) => {
  351. if (currentGradient !== particle._currentDragGradient) {
  352. particle._currentDrag1 = particle._currentDrag2;
  353. particle._currentDrag2 = (<FactorGradient>nextGradient).getFactor();
  354. particle._currentDragGradient = (<FactorGradient>currentGradient);
  355. }
  356. let drag = Scalar.Lerp(particle._currentDrag1, particle._currentDrag2, scale);
  357. this._scaledDirection.scaleInPlace(1.0 - drag);
  358. });
  359. }
  360. if (this.isLocal && particle._localPosition) {
  361. particle._localPosition!.addInPlace(this._scaledDirection);
  362. Vector3.TransformCoordinatesToRef(particle._localPosition!, this._emitterWorldMatrix, particle.position);
  363. } else {
  364. particle.position.addInPlace(this._scaledDirection);
  365. }
  366. // Noise
  367. if (noiseTextureData && noiseTextureSize && particle._randomNoiseCoordinates1) {
  368. let fetchedColorR = this._fetchR(particle._randomNoiseCoordinates1.x, particle._randomNoiseCoordinates1.y, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
  369. let fetchedColorG = this._fetchR(particle._randomNoiseCoordinates1.z, particle._randomNoiseCoordinates2.x, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
  370. let fetchedColorB = this._fetchR(particle._randomNoiseCoordinates2.y, particle._randomNoiseCoordinates2.z, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
  371. let force = TmpVectors.Vector3[0];
  372. let scaledForce = TmpVectors.Vector3[1];
  373. force.copyFromFloats((2 * fetchedColorR - 1) * this.noiseStrength.x, (2 * fetchedColorG - 1) * this.noiseStrength.y, (2 * fetchedColorB - 1) * this.noiseStrength.z);
  374. force.scaleToRef(scaledUpdateSpeed, scaledForce);
  375. particle.direction.addInPlace(scaledForce);
  376. }
  377. // Gravity
  378. this.gravity.scaleToRef(scaledUpdateSpeed, this._scaledGravity);
  379. particle.direction.addInPlace(this._scaledGravity);
  380. // Size
  381. if (this._sizeGradients && this._sizeGradients.length > 0) {
  382. GradientHelper.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
  383. if (currentGradient !== particle._currentSizeGradient) {
  384. particle._currentSize1 = particle._currentSize2;
  385. particle._currentSize2 = (<FactorGradient>nextGradient).getFactor();
  386. particle._currentSizeGradient = (<FactorGradient>currentGradient);
  387. }
  388. particle.size = Scalar.Lerp(particle._currentSize1, particle._currentSize2, scale);
  389. });
  390. }
  391. // Remap data
  392. if (this._useRampGradients) {
  393. if (this._colorRemapGradients && this._colorRemapGradients.length > 0) {
  394. GradientHelper.GetCurrentGradient(ratio, this._colorRemapGradients, (currentGradient, nextGradient, scale) => {
  395. let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
  396. let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
  397. particle.remapData.x = min;
  398. particle.remapData.y = max - min;
  399. });
  400. }
  401. if (this._alphaRemapGradients && this._alphaRemapGradients.length > 0) {
  402. GradientHelper.GetCurrentGradient(ratio, this._alphaRemapGradients, (currentGradient, nextGradient, scale) => {
  403. let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
  404. let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
  405. particle.remapData.z = min;
  406. particle.remapData.w = max - min;
  407. });
  408. }
  409. }
  410. if (this._isAnimationSheetEnabled) {
  411. particle.updateCellIndex();
  412. }
  413. // Update the position of the attached sub-emitters to match their attached particle
  414. particle._inheritParticleInfoToSubEmitters();
  415. if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
  416. this._emitFromParticle(particle);
  417. if (particle._attachedSubEmitters) {
  418. particle._attachedSubEmitters.forEach((subEmitter) => {
  419. subEmitter.particleSystem.disposeOnStop = true;
  420. subEmitter.particleSystem.stop();
  421. });
  422. particle._attachedSubEmitters = null;
  423. }
  424. this.recycleParticle(particle);
  425. index--;
  426. continue;
  427. }
  428. }
  429. };
  430. }
  431. private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number, factor2?: number) {
  432. let newGradient = new FactorGradient(gradient, factor, factor2);
  433. factorGradients.push(newGradient);
  434. factorGradients.sort((a, b) => {
  435. if (a.gradient < b.gradient) {
  436. return -1;
  437. } else if (a.gradient > b.gradient) {
  438. return 1;
  439. }
  440. return 0;
  441. });
  442. }
  443. private _removeFactorGradient(factorGradients: Nullable<FactorGradient[]>, gradient: number) {
  444. if (!factorGradients) {
  445. return;
  446. }
  447. let index = 0;
  448. for (var factorGradient of factorGradients) {
  449. if (factorGradient.gradient === gradient) {
  450. factorGradients.splice(index, 1);
  451. break;
  452. }
  453. index++;
  454. }
  455. }
  456. /**
  457. * Adds a new life time gradient
  458. * @param gradient defines the gradient to use (between 0 and 1)
  459. * @param factor defines the life time factor to affect to the specified gradient
  460. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  461. * @returns the current particle system
  462. */
  463. public addLifeTimeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  464. if (!this._lifeTimeGradients) {
  465. this._lifeTimeGradients = [];
  466. }
  467. this._addFactorGradient(this._lifeTimeGradients, gradient, factor, factor2);
  468. return this;
  469. }
  470. /**
  471. * Remove a specific life time gradient
  472. * @param gradient defines the gradient to remove
  473. * @returns the current particle system
  474. */
  475. public removeLifeTimeGradient(gradient: number): IParticleSystem {
  476. this._removeFactorGradient(this._lifeTimeGradients, gradient);
  477. return this;
  478. }
  479. /**
  480. * Adds a new size gradient
  481. * @param gradient defines the gradient to use (between 0 and 1)
  482. * @param factor defines the size factor to affect to the specified gradient
  483. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  484. * @returns the current particle system
  485. */
  486. public addSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  487. if (!this._sizeGradients) {
  488. this._sizeGradients = [];
  489. }
  490. this._addFactorGradient(this._sizeGradients, gradient, factor, factor2);
  491. return this;
  492. }
  493. /**
  494. * Remove a specific size gradient
  495. * @param gradient defines the gradient to remove
  496. * @returns the current particle system
  497. */
  498. public removeSizeGradient(gradient: number): IParticleSystem {
  499. this._removeFactorGradient(this._sizeGradients, gradient);
  500. return this;
  501. }
  502. /**
  503. * Adds a new color remap gradient
  504. * @param gradient defines the gradient to use (between 0 and 1)
  505. * @param min defines the color remap minimal range
  506. * @param max defines the color remap maximal range
  507. * @returns the current particle system
  508. */
  509. public addColorRemapGradient(gradient: number, min: number, max: number): IParticleSystem {
  510. if (!this._colorRemapGradients) {
  511. this._colorRemapGradients = [];
  512. }
  513. this._addFactorGradient(this._colorRemapGradients, gradient, min, max);
  514. return this;
  515. }
  516. /**
  517. * Remove a specific color remap gradient
  518. * @param gradient defines the gradient to remove
  519. * @returns the current particle system
  520. */
  521. public removeColorRemapGradient(gradient: number): IParticleSystem {
  522. this._removeFactorGradient(this._colorRemapGradients, gradient);
  523. return this;
  524. }
  525. /**
  526. * Adds a new alpha remap gradient
  527. * @param gradient defines the gradient to use (between 0 and 1)
  528. * @param min defines the alpha remap minimal range
  529. * @param max defines the alpha remap maximal range
  530. * @returns the current particle system
  531. */
  532. public addAlphaRemapGradient(gradient: number, min: number, max: number): IParticleSystem {
  533. if (!this._alphaRemapGradients) {
  534. this._alphaRemapGradients = [];
  535. }
  536. this._addFactorGradient(this._alphaRemapGradients, gradient, min, max);
  537. return this;
  538. }
  539. /**
  540. * Remove a specific alpha remap gradient
  541. * @param gradient defines the gradient to remove
  542. * @returns the current particle system
  543. */
  544. public removeAlphaRemapGradient(gradient: number): IParticleSystem {
  545. this._removeFactorGradient(this._alphaRemapGradients, gradient);
  546. return this;
  547. }
  548. /**
  549. * Adds a new angular speed gradient
  550. * @param gradient defines the gradient to use (between 0 and 1)
  551. * @param factor defines the angular speed to affect to the specified gradient
  552. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  553. * @returns the current particle system
  554. */
  555. public addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  556. if (!this._angularSpeedGradients) {
  557. this._angularSpeedGradients = [];
  558. }
  559. this._addFactorGradient(this._angularSpeedGradients, gradient, factor, factor2);
  560. return this;
  561. }
  562. /**
  563. * Remove a specific angular speed gradient
  564. * @param gradient defines the gradient to remove
  565. * @returns the current particle system
  566. */
  567. public removeAngularSpeedGradient(gradient: number): IParticleSystem {
  568. this._removeFactorGradient(this._angularSpeedGradients, gradient);
  569. return this;
  570. }
  571. /**
  572. * Adds a new velocity gradient
  573. * @param gradient defines the gradient to use (between 0 and 1)
  574. * @param factor defines the velocity to affect to the specified gradient
  575. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  576. * @returns the current particle system
  577. */
  578. public addVelocityGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  579. if (!this._velocityGradients) {
  580. this._velocityGradients = [];
  581. }
  582. this._addFactorGradient(this._velocityGradients, gradient, factor, factor2);
  583. return this;
  584. }
  585. /**
  586. * Remove a specific velocity gradient
  587. * @param gradient defines the gradient to remove
  588. * @returns the current particle system
  589. */
  590. public removeVelocityGradient(gradient: number): IParticleSystem {
  591. this._removeFactorGradient(this._velocityGradients, gradient);
  592. return this;
  593. }
  594. /**
  595. * Adds a new limit velocity gradient
  596. * @param gradient defines the gradient to use (between 0 and 1)
  597. * @param factor defines the limit velocity value to affect to the specified gradient
  598. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  599. * @returns the current particle system
  600. */
  601. public addLimitVelocityGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  602. if (!this._limitVelocityGradients) {
  603. this._limitVelocityGradients = [];
  604. }
  605. this._addFactorGradient(this._limitVelocityGradients, gradient, factor, factor2);
  606. return this;
  607. }
  608. /**
  609. * Remove a specific limit velocity gradient
  610. * @param gradient defines the gradient to remove
  611. * @returns the current particle system
  612. */
  613. public removeLimitVelocityGradient(gradient: number): IParticleSystem {
  614. this._removeFactorGradient(this._limitVelocityGradients, gradient);
  615. return this;
  616. }
  617. /**
  618. * Adds a new drag gradient
  619. * @param gradient defines the gradient to use (between 0 and 1)
  620. * @param factor defines the drag value to affect to the specified gradient
  621. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  622. * @returns the current particle system
  623. */
  624. public addDragGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  625. if (!this._dragGradients) {
  626. this._dragGradients = [];
  627. }
  628. this._addFactorGradient(this._dragGradients, gradient, factor, factor2);
  629. return this;
  630. }
  631. /**
  632. * Remove a specific drag gradient
  633. * @param gradient defines the gradient to remove
  634. * @returns the current particle system
  635. */
  636. public removeDragGradient(gradient: number): IParticleSystem {
  637. this._removeFactorGradient(this._dragGradients, gradient);
  638. return this;
  639. }
  640. /**
  641. * Adds a new emit rate gradient (please note that this will only work if you set the targetStopDuration property)
  642. * @param gradient defines the gradient to use (between 0 and 1)
  643. * @param factor defines the emit rate value to affect to the specified gradient
  644. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  645. * @returns the current particle system
  646. */
  647. public addEmitRateGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  648. if (!this._emitRateGradients) {
  649. this._emitRateGradients = [];
  650. }
  651. this._addFactorGradient(this._emitRateGradients, gradient, factor, factor2);
  652. return this;
  653. }
  654. /**
  655. * Remove a specific emit rate gradient
  656. * @param gradient defines the gradient to remove
  657. * @returns the current particle system
  658. */
  659. public removeEmitRateGradient(gradient: number): IParticleSystem {
  660. this._removeFactorGradient(this._emitRateGradients, gradient);
  661. return this;
  662. }
  663. /**
  664. * Adds a new start size gradient (please note that this will only work if you set the targetStopDuration property)
  665. * @param gradient defines the gradient to use (between 0 and 1)
  666. * @param factor defines the start size value to affect to the specified gradient
  667. * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
  668. * @returns the current particle system
  669. */
  670. public addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
  671. if (!this._startSizeGradients) {
  672. this._startSizeGradients = [];
  673. }
  674. this._addFactorGradient(this._startSizeGradients, gradient, factor, factor2);
  675. return this;
  676. }
  677. /**
  678. * Remove a specific start size gradient
  679. * @param gradient defines the gradient to remove
  680. * @returns the current particle system
  681. */
  682. public removeStartSizeGradient(gradient: number): IParticleSystem {
  683. this._removeFactorGradient(this._startSizeGradients, gradient);
  684. return this;
  685. }
  686. private _createRampGradientTexture() {
  687. if (!this._rampGradients || !this._rampGradients.length || this._rampGradientsTexture || !this._scene) {
  688. return;
  689. }
  690. let data = new Uint8Array(this._rawTextureWidth * 4);
  691. let tmpColor = TmpColors.Color3[0];
  692. for (var x = 0; x < this._rawTextureWidth; x++) {
  693. var ratio = x / this._rawTextureWidth;
  694. GradientHelper.GetCurrentGradient(ratio, this._rampGradients, (currentGradient, nextGradient, scale) => {
  695. Color3.LerpToRef((<Color3Gradient>currentGradient).color, (<Color3Gradient>nextGradient).color, scale, tmpColor);
  696. data[x * 4] = tmpColor.r * 255;
  697. data[x * 4 + 1] = tmpColor.g * 255;
  698. data[x * 4 + 2] = tmpColor.b * 255;
  699. data[x * 4 + 3] = 255;
  700. });
  701. }
  702. this._rampGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE);
  703. }
  704. /**
  705. * Gets the current list of ramp gradients.
  706. * You must use addRampGradient and removeRampGradient to udpate this list
  707. * @returns the list of ramp gradients
  708. */
  709. public getRampGradients(): Nullable<Array<Color3Gradient>> {
  710. return this._rampGradients;
  711. }
  712. /** Force the system to rebuild all gradients that need to be resync */
  713. public forceRefreshGradients() {
  714. this._syncRampGradientTexture();
  715. }
  716. private _syncRampGradientTexture() {
  717. if (!this._rampGradients) {
  718. return;
  719. }
  720. this._rampGradients.sort((a, b) => {
  721. if (a.gradient < b.gradient) {
  722. return -1;
  723. } else if (a.gradient > b.gradient) {
  724. return 1;
  725. }
  726. return 0;
  727. });
  728. if (this._rampGradientsTexture) {
  729. this._rampGradientsTexture.dispose();
  730. this._rampGradientsTexture = null;
  731. }
  732. this._createRampGradientTexture();
  733. }
  734. /**
  735. * Adds a new ramp gradient used to remap particle colors
  736. * @param gradient defines the gradient to use (between 0 and 1)
  737. * @param color defines the color to affect to the specified gradient
  738. * @returns the current particle system
  739. */
  740. public addRampGradient(gradient: number, color: Color3): ParticleSystem {
  741. if (!this._rampGradients) {
  742. this._rampGradients = [];
  743. }
  744. let rampGradient = new Color3Gradient(gradient, color);
  745. this._rampGradients.push(rampGradient);
  746. this._syncRampGradientTexture();
  747. return this;
  748. }
  749. /**
  750. * Remove a specific ramp gradient
  751. * @param gradient defines the gradient to remove
  752. * @returns the current particle system
  753. */
  754. public removeRampGradient(gradient: number): ParticleSystem {
  755. this._removeGradientAndTexture(gradient, this._rampGradients, this._rampGradientsTexture);
  756. this._rampGradientsTexture = null;
  757. if (this._rampGradients && this._rampGradients.length > 0) {
  758. this._createRampGradientTexture();
  759. }
  760. return this;
  761. }
  762. /**
  763. * Adds a new color gradient
  764. * @param gradient defines the gradient to use (between 0 and 1)
  765. * @param color1 defines the color to affect to the specified gradient
  766. * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
  767. * @returns this particle system
  768. */
  769. public addColorGradient(gradient: number, color1: Color4, color2?: Color4): IParticleSystem {
  770. if (!this._colorGradients) {
  771. this._colorGradients = [];
  772. }
  773. let colorGradient = new ColorGradient(gradient, color1, color2);
  774. this._colorGradients.push(colorGradient);
  775. this._colorGradients.sort((a, b) => {
  776. if (a.gradient < b.gradient) {
  777. return -1;
  778. } else if (a.gradient > b.gradient) {
  779. return 1;
  780. }
  781. return 0;
  782. });
  783. return this;
  784. }
  785. /**
  786. * Remove a specific color gradient
  787. * @param gradient defines the gradient to remove
  788. * @returns this particle system
  789. */
  790. public removeColorGradient(gradient: number): IParticleSystem {
  791. if (!this._colorGradients) {
  792. return this;
  793. }
  794. let index = 0;
  795. for (var colorGradient of this._colorGradients) {
  796. if (colorGradient.gradient === gradient) {
  797. this._colorGradients.splice(index, 1);
  798. break;
  799. }
  800. index++;
  801. }
  802. return this;
  803. }
  804. private _fetchR(u: number, v: number, width: number, height: number, pixels: Uint8Array): number {
  805. u = Math.abs(u) * 0.5 + 0.5;
  806. v = Math.abs(v) * 0.5 + 0.5;
  807. let wrappedU = ((u * width) % width) | 0;
  808. let wrappedV = ((v * height) % height) | 0;
  809. let position = (wrappedU + wrappedV * width) * 4;
  810. return pixels[position] / 255;
  811. }
  812. protected _reset() {
  813. this._resetEffect();
  814. }
  815. private _resetEffect() {
  816. if (this._vertexBuffer) {
  817. this._vertexBuffer.dispose();
  818. this._vertexBuffer = null;
  819. }
  820. if (this._spriteBuffer) {
  821. this._spriteBuffer.dispose();
  822. this._spriteBuffer = null;
  823. }
  824. if (this._vertexArrayObject) {
  825. this._engine.releaseVertexArrayObject(this._vertexArrayObject);
  826. this._vertexArrayObject = null;
  827. }
  828. this._createVertexBuffers();
  829. }
  830. private _createVertexBuffers() {
  831. this._vertexBufferSize = this._useInstancing ? 10 : 12;
  832. if (this._isAnimationSheetEnabled) {
  833. this._vertexBufferSize += 1;
  834. }
  835. if (!this._isBillboardBased || this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED) {
  836. this._vertexBufferSize += 3;
  837. }
  838. if (this._useRampGradients) {
  839. this._vertexBufferSize += 4;
  840. }
  841. let engine = this._engine;
  842. this._vertexData = new Float32Array(this._capacity * this._vertexBufferSize * (this._useInstancing ? 1 : 4));
  843. this._vertexBuffer = new Buffer(engine, this._vertexData, true, this._vertexBufferSize);
  844. let dataOffset = 0;
  845. var positions = this._vertexBuffer.createVertexBuffer(VertexBuffer.PositionKind, dataOffset, 3, this._vertexBufferSize, this._useInstancing);
  846. this._vertexBuffers[VertexBuffer.PositionKind] = positions;
  847. dataOffset += 3;
  848. var colors = this._vertexBuffer.createVertexBuffer(VertexBuffer.ColorKind, dataOffset, 4, this._vertexBufferSize, this._useInstancing);
  849. this._vertexBuffers[VertexBuffer.ColorKind] = colors;
  850. dataOffset += 4;
  851. var options = this._vertexBuffer.createVertexBuffer("angle", dataOffset, 1, this._vertexBufferSize, this._useInstancing);
  852. this._vertexBuffers["angle"] = options;
  853. dataOffset += 1;
  854. var size = this._vertexBuffer.createVertexBuffer("size", dataOffset, 2, this._vertexBufferSize, this._useInstancing);
  855. this._vertexBuffers["size"] = size;
  856. dataOffset += 2;
  857. if (this._isAnimationSheetEnabled) {
  858. var cellIndexBuffer = this._vertexBuffer.createVertexBuffer("cellIndex", dataOffset, 1, this._vertexBufferSize, this._useInstancing);
  859. this._vertexBuffers["cellIndex"] = cellIndexBuffer;
  860. dataOffset += 1;
  861. }
  862. if (!this._isBillboardBased || this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED) {
  863. var directionBuffer = this._vertexBuffer.createVertexBuffer("direction", dataOffset, 3, this._vertexBufferSize, this._useInstancing);
  864. this._vertexBuffers["direction"] = directionBuffer;
  865. dataOffset += 3;
  866. }
  867. if (this._useRampGradients) {
  868. var rampDataBuffer = this._vertexBuffer.createVertexBuffer("remapData", dataOffset, 4, this._vertexBufferSize, this._useInstancing);
  869. this._vertexBuffers["remapData"] = rampDataBuffer;
  870. dataOffset += 4;
  871. }
  872. var offsets: VertexBuffer;
  873. if (this._useInstancing) {
  874. var spriteData = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
  875. this._spriteBuffer = new Buffer(engine, spriteData, false, 2);
  876. offsets = this._spriteBuffer.createVertexBuffer("offset", 0, 2);
  877. } else {
  878. offsets = this._vertexBuffer.createVertexBuffer("offset", dataOffset, 2, this._vertexBufferSize, this._useInstancing);
  879. dataOffset += 2;
  880. }
  881. this._vertexBuffers["offset"] = offsets;
  882. }
  883. private _createIndexBuffer() {
  884. if (this._useInstancing) {
  885. return;
  886. }
  887. var indices = [];
  888. var index = 0;
  889. for (var count = 0; count < this._capacity; count++) {
  890. indices.push(index);
  891. indices.push(index + 1);
  892. indices.push(index + 2);
  893. indices.push(index);
  894. indices.push(index + 2);
  895. indices.push(index + 3);
  896. index += 4;
  897. }
  898. this._indexBuffer = this._engine.createIndexBuffer(indices);
  899. }
  900. /**
  901. * Gets the maximum number of particles active at the same time.
  902. * @returns The max number of active particles.
  903. */
  904. public getCapacity(): number {
  905. return this._capacity;
  906. }
  907. /**
  908. * Gets whether there are still active particles in the system.
  909. * @returns True if it is alive, otherwise false.
  910. */
  911. public isAlive(): boolean {
  912. return this._alive;
  913. }
  914. /**
  915. * Gets if the system has been started. (Note: this will still be true after stop is called)
  916. * @returns True if it has been started, otherwise false.
  917. */
  918. public isStarted(): boolean {
  919. return this._started;
  920. }
  921. private _prepareSubEmitterInternalArray() {
  922. this._subEmitters = new Array<Array<SubEmitter>>();
  923. if (this.subEmitters) {
  924. this.subEmitters.forEach((subEmitter) => {
  925. if (subEmitter instanceof ParticleSystem) {
  926. this._subEmitters.push([new SubEmitter(subEmitter)]);
  927. } else if (subEmitter instanceof SubEmitter) {
  928. this._subEmitters.push([subEmitter]);
  929. } else if (subEmitter instanceof Array) {
  930. this._subEmitters.push(subEmitter);
  931. }
  932. });
  933. }
  934. }
  935. /**
  936. * Starts the particle system and begins to emit
  937. * @param delay defines the delay in milliseconds before starting the system (this.startDelay by default)
  938. */
  939. public start(delay = this.startDelay): void {
  940. if (!this.targetStopDuration && this._hasTargetStopDurationDependantGradient()) {
  941. throw "Particle system started with a targetStopDuration dependant gradient (eg. startSizeGradients) but no targetStopDuration set";
  942. }
  943. if (delay) {
  944. setTimeout(() => {
  945. this.start(0);
  946. }, delay);
  947. return;
  948. }
  949. // Convert the subEmitters field to the constant type field _subEmitters
  950. this._prepareSubEmitterInternalArray();
  951. this._started = true;
  952. this._stopped = false;
  953. this._actualFrame = 0;
  954. if (this._subEmitters && this._subEmitters.length != 0) {
  955. this.activeSubSystems = new Array<ParticleSystem>();
  956. }
  957. // Reset emit gradient so it acts the same on every start
  958. if (this._emitRateGradients) {
  959. if (this._emitRateGradients.length > 0) {
  960. this._currentEmitRateGradient = this._emitRateGradients[0];
  961. this._currentEmitRate1 = this._currentEmitRateGradient.getFactor();
  962. this._currentEmitRate2 = this._currentEmitRate1;
  963. }
  964. if (this._emitRateGradients.length > 1) {
  965. this._currentEmitRate2 = this._emitRateGradients[1].getFactor();
  966. }
  967. }
  968. // Reset start size gradient so it acts the same on every start
  969. if (this._startSizeGradients) {
  970. if (this._startSizeGradients.length > 0) {
  971. this._currentStartSizeGradient = this._startSizeGradients[0];
  972. this._currentStartSize1 = this._currentStartSizeGradient.getFactor();
  973. this._currentStartSize2 = this._currentStartSize1;
  974. }
  975. if (this._startSizeGradients.length > 1) {
  976. this._currentStartSize2 = this._startSizeGradients[1].getFactor();
  977. }
  978. }
  979. if (this.preWarmCycles) {
  980. if (this.emitter?.getClassName().indexOf("Mesh") !== -1) {
  981. (this.emitter as any).computeWorldMatrix(true);
  982. }
  983. let noiseTextureAsProcedural = this.noiseTexture as ProceduralTexture;
  984. if (noiseTextureAsProcedural && noiseTextureAsProcedural.onGeneratedObservable) {
  985. noiseTextureAsProcedural.onGeneratedObservable.addOnce(() => {
  986. setTimeout(() => {
  987. for (var index = 0; index < this.preWarmCycles; index++) {
  988. this.animate(true);
  989. noiseTextureAsProcedural.render();
  990. }
  991. });
  992. });
  993. } else {
  994. for (var index = 0; index < this.preWarmCycles; index++) {
  995. this.animate(true);
  996. }
  997. }
  998. }
  999. // Animations
  1000. if (this.beginAnimationOnStart && this.animations && this.animations.length > 0 && this._scene) {
  1001. this._scene.beginAnimation(this, this.beginAnimationFrom, this.beginAnimationTo, this.beginAnimationLoop);
  1002. }
  1003. }
  1004. /**
  1005. * Stops the particle system.
  1006. * @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.
  1007. */
  1008. public stop(stopSubEmitters = true): void {
  1009. if (this._stopped) {
  1010. return;
  1011. }
  1012. this.onStoppedObservable.notifyObservers(this);
  1013. this._stopped = true;
  1014. if (stopSubEmitters) {
  1015. this._stopSubEmitters();
  1016. }
  1017. }
  1018. // animation sheet
  1019. /**
  1020. * Remove all active particles
  1021. */
  1022. public reset(): void {
  1023. this._stockParticles = [];
  1024. this._particles = [];
  1025. }
  1026. /**
  1027. * @hidden (for internal use only)
  1028. */
  1029. public _appendParticleVertex(index: number, particle: Particle, offsetX: number, offsetY: number): void {
  1030. var offset = index * this._vertexBufferSize;
  1031. this._vertexData[offset++] = particle.position.x + this.worldOffset.x;
  1032. this._vertexData[offset++] = particle.position.y + this.worldOffset.y;
  1033. this._vertexData[offset++] = particle.position.z + this.worldOffset.z;
  1034. this._vertexData[offset++] = particle.color.r;
  1035. this._vertexData[offset++] = particle.color.g;
  1036. this._vertexData[offset++] = particle.color.b;
  1037. this._vertexData[offset++] = particle.color.a;
  1038. this._vertexData[offset++] = particle.angle;
  1039. this._vertexData[offset++] = particle.scale.x * particle.size;
  1040. this._vertexData[offset++] = particle.scale.y * particle.size;
  1041. if (this._isAnimationSheetEnabled) {
  1042. this._vertexData[offset++] = particle.cellIndex;
  1043. }
  1044. if (!this._isBillboardBased) {
  1045. if (particle._initialDirection) {
  1046. let initialDirection = particle._initialDirection;
  1047. if (this.isLocal) {
  1048. Vector3.TransformNormalToRef(initialDirection, this._emitterWorldMatrix, TmpVectors.Vector3[0]);
  1049. initialDirection = TmpVectors.Vector3[0];
  1050. }
  1051. if (initialDirection.x === 0 && initialDirection.z === 0) {
  1052. initialDirection.x = 0.001;
  1053. }
  1054. this._vertexData[offset++] = initialDirection.x;
  1055. this._vertexData[offset++] = initialDirection.y;
  1056. this._vertexData[offset++] = initialDirection.z;
  1057. } else {
  1058. let direction = particle.direction;
  1059. if (this.isLocal) {
  1060. Vector3.TransformNormalToRef(direction, this._emitterWorldMatrix, TmpVectors.Vector3[0]);
  1061. direction = TmpVectors.Vector3[0];
  1062. }
  1063. if (direction.x === 0 && direction.z === 0) {
  1064. direction.x = 0.001;
  1065. }
  1066. this._vertexData[offset++] = direction.x;
  1067. this._vertexData[offset++] = direction.y;
  1068. this._vertexData[offset++] = direction.z;
  1069. }
  1070. } else if (this.billboardMode === ParticleSystem.BILLBOARDMODE_STRETCHED) {
  1071. this._vertexData[offset++] = particle.direction.x;
  1072. this._vertexData[offset++] = particle.direction.y;
  1073. this._vertexData[offset++] = particle.direction.z;
  1074. }
  1075. if (this._useRampGradients && particle.remapData) {
  1076. this._vertexData[offset++] = particle.remapData.x;
  1077. this._vertexData[offset++] = particle.remapData.y;
  1078. this._vertexData[offset++] = particle.remapData.z;
  1079. this._vertexData[offset++] = particle.remapData.w;
  1080. }
  1081. if (!this._useInstancing) {
  1082. if (this._isAnimationSheetEnabled) {
  1083. if (offsetX === 0) {
  1084. offsetX = this._epsilon;
  1085. }
  1086. else if (offsetX === 1) {
  1087. offsetX = 1 - this._epsilon;
  1088. }
  1089. if (offsetY === 0) {
  1090. offsetY = this._epsilon;
  1091. }
  1092. else if (offsetY === 1) {
  1093. offsetY = 1 - this._epsilon;
  1094. }
  1095. }
  1096. this._vertexData[offset++] = offsetX;
  1097. this._vertexData[offset++] = offsetY;
  1098. }
  1099. }
  1100. // start of sub system methods
  1101. /**
  1102. * "Recycles" one of the particle by copying it back to the "stock" of particles and removing it from the active list.
  1103. * Its lifetime will start back at 0.
  1104. */
  1105. public recycleParticle: (particle: Particle) => void = (particle) => {
  1106. // move particle from activeParticle list to stock particles
  1107. var lastParticle = <Particle>this._particles.pop();
  1108. if (lastParticle !== particle) {
  1109. lastParticle.copyTo(particle);
  1110. }
  1111. this._stockParticles.push(lastParticle);
  1112. }
  1113. private _stopSubEmitters(): void {
  1114. if (!this.activeSubSystems) {
  1115. return;
  1116. }
  1117. this.activeSubSystems.forEach((subSystem) => {
  1118. subSystem.stop(true);
  1119. });
  1120. this.activeSubSystems = new Array<ParticleSystem>();
  1121. }
  1122. private _createParticle: () => Particle = () => {
  1123. var particle: Particle;
  1124. if (this._stockParticles.length !== 0) {
  1125. particle = <Particle>this._stockParticles.pop();
  1126. particle._reset();
  1127. } else {
  1128. particle = new Particle(this);
  1129. }
  1130. // Attach emitters
  1131. if (this._subEmitters && this._subEmitters.length > 0) {
  1132. var subEmitters = this._subEmitters[Math.floor(Math.random() * this._subEmitters.length)];
  1133. particle._attachedSubEmitters = [];
  1134. subEmitters.forEach((subEmitter) => {
  1135. if (subEmitter.type === SubEmitterType.ATTACHED) {
  1136. var newEmitter = subEmitter.clone();
  1137. (<Array<SubEmitter>>particle._attachedSubEmitters).push(newEmitter);
  1138. newEmitter.particleSystem.start();
  1139. }
  1140. });
  1141. }
  1142. return particle;
  1143. }
  1144. private _removeFromRoot(): void {
  1145. if (!this._rootParticleSystem) {
  1146. return;
  1147. }
  1148. let index = this._rootParticleSystem.activeSubSystems.indexOf(this);
  1149. if (index !== -1) {
  1150. this._rootParticleSystem.activeSubSystems.splice(index, 1);
  1151. }
  1152. this._rootParticleSystem = null;
  1153. }
  1154. private _emitFromParticle: (particle: Particle) => void = (particle) => {
  1155. if (!this._subEmitters || this._subEmitters.length === 0) {
  1156. return;
  1157. }
  1158. var templateIndex = Math.floor(Math.random() * this._subEmitters.length);
  1159. this._subEmitters[templateIndex].forEach((subEmitter) => {
  1160. if (subEmitter.type === SubEmitterType.END) {
  1161. var subSystem = subEmitter.clone();
  1162. particle._inheritParticleInfoToSubEmitter(subSystem);
  1163. subSystem.particleSystem._rootParticleSystem = this;
  1164. this.activeSubSystems.push(subSystem.particleSystem);
  1165. subSystem.particleSystem.start();
  1166. }
  1167. });
  1168. }
  1169. // End of sub system methods
  1170. private _update(newParticles: number): void {
  1171. // Update current
  1172. this._alive = this._particles.length > 0;
  1173. if ((<AbstractMesh>this.emitter).position) {
  1174. var emitterMesh = (<AbstractMesh>this.emitter);
  1175. this._emitterWorldMatrix = emitterMesh.getWorldMatrix();
  1176. } else {
  1177. var emitterPosition = (<Vector3>this.emitter);
  1178. this._emitterWorldMatrix = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
  1179. }
  1180. this.updateFunction(this._particles);
  1181. // Add new ones
  1182. var particle: Particle;
  1183. for (var index = 0; index < newParticles; index++) {
  1184. if (this._particles.length === this._capacity) {
  1185. break;
  1186. }
  1187. particle = this._createParticle();
  1188. this._particles.push(particle);
  1189. // Life time
  1190. if (this.targetStopDuration && this._lifeTimeGradients && this._lifeTimeGradients.length > 0) {
  1191. let ratio = Scalar.Clamp(this._actualFrame / this.targetStopDuration);
  1192. GradientHelper.GetCurrentGradient(ratio, this._lifeTimeGradients, (currentGradient, nextGradient) => {
  1193. let factorGradient1 = (<FactorGradient>currentGradient);
  1194. let factorGradient2 = (<FactorGradient>nextGradient);
  1195. let lifeTime1 = factorGradient1.getFactor();
  1196. let lifeTime2 = factorGradient2.getFactor();
  1197. let gradient = (ratio - factorGradient1.gradient) / (factorGradient2.gradient - factorGradient1.gradient);
  1198. particle.lifeTime = Scalar.Lerp(lifeTime1, lifeTime2, gradient);
  1199. });
  1200. } else {
  1201. particle.lifeTime = Scalar.RandomRange(this.minLifeTime, this.maxLifeTime);
  1202. }
  1203. // Emitter
  1204. let emitPower = Scalar.RandomRange(this.minEmitPower, this.maxEmitPower);
  1205. if (this.startPositionFunction) {
  1206. this.startPositionFunction(this._emitterWorldMatrix, particle.position, particle, this.isLocal);
  1207. }
  1208. else {
  1209. this.particleEmitterType.startPositionFunction(this._emitterWorldMatrix, particle.position, particle, this.isLocal);
  1210. }
  1211. if (this.isLocal) {
  1212. if (!particle._localPosition) {
  1213. particle._localPosition = particle.position.clone();
  1214. } else {
  1215. particle._localPosition.copyFrom(particle.position);
  1216. }
  1217. Vector3.TransformCoordinatesToRef(particle._localPosition!, this._emitterWorldMatrix, particle.position);
  1218. }
  1219. if (this.startDirectionFunction) {
  1220. this.startDirectionFunction(this._emitterWorldMatrix, particle.direction, particle, this.isLocal);
  1221. }
  1222. else {
  1223. this.particleEmitterType.startDirectionFunction(this._emitterWorldMatrix, particle.direction, particle, this.isLocal);
  1224. }
  1225. if (emitPower === 0) {
  1226. if (!particle._initialDirection) {
  1227. particle._initialDirection = particle.direction.clone();
  1228. } else {
  1229. particle._initialDirection.copyFrom(particle.direction);
  1230. }
  1231. } else {
  1232. particle._initialDirection = null;
  1233. }
  1234. particle.direction.scaleInPlace(emitPower);
  1235. // Size
  1236. if (!this._sizeGradients || this._sizeGradients.length === 0) {
  1237. particle.size = Scalar.RandomRange(this.minSize, this.maxSize);
  1238. } else {
  1239. particle._currentSizeGradient = this._sizeGradients[0];
  1240. particle._currentSize1 = particle._currentSizeGradient.getFactor();
  1241. particle.size = particle._currentSize1;
  1242. if (this._sizeGradients.length > 1) {
  1243. particle._currentSize2 = this._sizeGradients[1].getFactor();
  1244. } else {
  1245. particle._currentSize2 = particle._currentSize1;
  1246. }
  1247. }
  1248. // Size and scale
  1249. particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
  1250. // Adjust scale by start size
  1251. if (this._startSizeGradients && this._startSizeGradients[0] && this.targetStopDuration) {
  1252. const ratio = this._actualFrame / this.targetStopDuration;
  1253. GradientHelper.GetCurrentGradient(ratio, this._startSizeGradients, (currentGradient, nextGradient, scale) => {
  1254. if (currentGradient !== this._currentStartSizeGradient) {
  1255. this._currentStartSize1 = this._currentStartSize2;
  1256. this._currentStartSize2 = (<FactorGradient>nextGradient).getFactor();
  1257. this._currentStartSizeGradient = (<FactorGradient>currentGradient);
  1258. }
  1259. var value = Scalar.Lerp(this._currentStartSize1, this._currentStartSize2, scale);
  1260. particle.scale.scaleInPlace(value);
  1261. });
  1262. }
  1263. // Angle
  1264. if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
  1265. particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
  1266. } else {
  1267. particle._currentAngularSpeedGradient = this._angularSpeedGradients[0];
  1268. particle.angularSpeed = particle._currentAngularSpeedGradient.getFactor();
  1269. particle._currentAngularSpeed1 = particle.angularSpeed;
  1270. if (this._angularSpeedGradients.length > 1) {
  1271. particle._currentAngularSpeed2 = this._angularSpeedGradients[1].getFactor();
  1272. } else {
  1273. particle._currentAngularSpeed2 = particle._currentAngularSpeed1;
  1274. }
  1275. }
  1276. particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
  1277. // Velocity
  1278. if (this._velocityGradients && this._velocityGradients.length > 0) {
  1279. particle._currentVelocityGradient = this._velocityGradients[0];
  1280. particle._currentVelocity1 = particle._currentVelocityGradient.getFactor();
  1281. if (this._velocityGradients.length > 1) {
  1282. particle._currentVelocity2 = this._velocityGradients[1].getFactor();
  1283. } else {
  1284. particle._currentVelocity2 = particle._currentVelocity1;
  1285. }
  1286. }
  1287. // Limit velocity
  1288. if (this._limitVelocityGradients && this._limitVelocityGradients.length > 0) {
  1289. particle._currentLimitVelocityGradient = this._limitVelocityGradients[0];
  1290. particle._currentLimitVelocity1 = particle._currentLimitVelocityGradient.getFactor();
  1291. if (this._limitVelocityGradients.length > 1) {
  1292. particle._currentLimitVelocity2 = this._limitVelocityGradients[1].getFactor();
  1293. } else {
  1294. particle._currentLimitVelocity2 = particle._currentLimitVelocity1;
  1295. }
  1296. }
  1297. // Drag
  1298. if (this._dragGradients && this._dragGradients.length > 0) {
  1299. particle._currentDragGradient = this._dragGradients[0];
  1300. particle._currentDrag1 = particle._currentDragGradient.getFactor();
  1301. if (this._dragGradients.length > 1) {
  1302. particle._currentDrag2 = this._dragGradients[1].getFactor();
  1303. } else {
  1304. particle._currentDrag2 = particle._currentDrag1;
  1305. }
  1306. }
  1307. // Color
  1308. if (!this._colorGradients || this._colorGradients.length === 0) {
  1309. var step = Scalar.RandomRange(0, 1.0);
  1310. Color4.LerpToRef(this.color1, this.color2, step, particle.color);
  1311. this.colorDead.subtractToRef(particle.color, this._colorDiff);
  1312. this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
  1313. } else {
  1314. particle._currentColorGradient = this._colorGradients[0];
  1315. particle._currentColorGradient.getColorToRef(particle.color);
  1316. particle._currentColor1.copyFrom(particle.color);
  1317. if (this._colorGradients.length > 1) {
  1318. this._colorGradients[1].getColorToRef(particle._currentColor2);
  1319. } else {
  1320. particle._currentColor2.copyFrom(particle.color);
  1321. }
  1322. }
  1323. // Sheet
  1324. if (this._isAnimationSheetEnabled) {
  1325. particle._initialStartSpriteCellID = this.startSpriteCellID;
  1326. particle._initialEndSpriteCellID = this.endSpriteCellID;
  1327. }
  1328. // Inherited Velocity
  1329. particle.direction.addInPlace(this._inheritedVelocityOffset);
  1330. // Ramp
  1331. if (this._useRampGradients) {
  1332. particle.remapData = new Vector4(0, 1, 0, 1);
  1333. }
  1334. // Noise texture coordinates
  1335. if (this.noiseTexture) {
  1336. if (particle._randomNoiseCoordinates1) {
  1337. particle._randomNoiseCoordinates1.copyFromFloats(Math.random(), Math.random(), Math.random());
  1338. particle._randomNoiseCoordinates2.copyFromFloats(Math.random(), Math.random(), Math.random());
  1339. } else {
  1340. particle._randomNoiseCoordinates1 = new Vector3(Math.random(), Math.random(), Math.random());
  1341. particle._randomNoiseCoordinates2 = new Vector3(Math.random(), Math.random(), Math.random());
  1342. }
  1343. }
  1344. // Update the position of the attached sub-emitters to match their attached particle
  1345. particle._inheritParticleInfoToSubEmitters();
  1346. }
  1347. }
  1348. /** @hidden */
  1349. public static _GetAttributeNamesOrOptions(isAnimationSheetEnabled = false, isBillboardBased = false, useRampGradients = false): string[] {
  1350. var attributeNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "angle", "offset", "size"];
  1351. if (isAnimationSheetEnabled) {
  1352. attributeNamesOrOptions.push("cellIndex");
  1353. }
  1354. if (!isBillboardBased) {
  1355. attributeNamesOrOptions.push("direction");
  1356. }
  1357. if (useRampGradients) {
  1358. attributeNamesOrOptions.push("remapData");
  1359. }
  1360. return attributeNamesOrOptions;
  1361. }
  1362. /** @hidden */
  1363. public static _GetEffectCreationOptions(isAnimationSheetEnabled = false): string[] {
  1364. var effectCreationOption = ["invView", "view", "projection", "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "textureMask", "translationPivot", "eyePosition"];
  1365. if (isAnimationSheetEnabled) {
  1366. effectCreationOption.push("particlesInfos");
  1367. }
  1368. return effectCreationOption;
  1369. }
  1370. /**
  1371. * Fill the defines array according to the current settings of the particle system
  1372. * @param defines Array to be updated
  1373. * @param blendMode blend mode to take into account when updating the array
  1374. */
  1375. public fillDefines(defines: Array<string>, blendMode: number) {
  1376. if (this._scene) {
  1377. if (this._scene.clipPlane) {
  1378. defines.push("#define CLIPPLANE");
  1379. }
  1380. if (this._scene.clipPlane2) {
  1381. defines.push("#define CLIPPLANE2");
  1382. }
  1383. if (this._scene.clipPlane3) {
  1384. defines.push("#define CLIPPLANE3");
  1385. }
  1386. if (this._scene.clipPlane4) {
  1387. defines.push("#define CLIPPLANE4");
  1388. }
  1389. if (this._scene.clipPlane5) {
  1390. defines.push("#define CLIPPLANE5");
  1391. }
  1392. if (this._scene.clipPlane6) {
  1393. defines.push("#define CLIPPLANE6");
  1394. }
  1395. }
  1396. if (this._isAnimationSheetEnabled) {
  1397. defines.push("#define ANIMATESHEET");
  1398. }
  1399. if (blendMode === ParticleSystem.BLENDMODE_MULTIPLY) {
  1400. defines.push("#define BLENDMULTIPLYMODE");
  1401. }
  1402. if (this._useRampGradients) {
  1403. defines.push("#define RAMPGRADIENT");
  1404. }
  1405. if (this._isBillboardBased) {
  1406. defines.push("#define BILLBOARD");
  1407. switch (this.billboardMode) {
  1408. case ParticleSystem.BILLBOARDMODE_Y:
  1409. defines.push("#define BILLBOARDY");
  1410. break;
  1411. case ParticleSystem.BILLBOARDMODE_STRETCHED:
  1412. defines.push("#define BILLBOARDSTRETCHED");
  1413. break;
  1414. case ParticleSystem.BILLBOARDMODE_ALL:
  1415. defines.push("#define BILLBOARDMODE_ALL");
  1416. break;
  1417. default:
  1418. break;
  1419. }
  1420. }
  1421. if (this._imageProcessingConfiguration) {
  1422. this._imageProcessingConfiguration.prepareDefines(this._imageProcessingConfigurationDefines);
  1423. defines.push(this._imageProcessingConfigurationDefines.toString());
  1424. }
  1425. }
  1426. /**
  1427. * Fill the uniforms, attributes and samplers arrays according to the current settings of the particle system
  1428. * @param uniforms Uniforms array to fill
  1429. * @param attributes Attributes array to fill
  1430. * @param samplers Samplers array to fill
  1431. */
  1432. public fillUniformsAttributesAndSamplerNames(uniforms: Array<string>, attributes: Array<string>, samplers: Array<string>) {
  1433. attributes.push(...ParticleSystem._GetAttributeNamesOrOptions(this._isAnimationSheetEnabled, this._isBillboardBased && this.billboardMode !== ParticleSystem.BILLBOARDMODE_STRETCHED, this._useRampGradients));
  1434. uniforms.push(...ParticleSystem._GetEffectCreationOptions(this._isAnimationSheetEnabled));
  1435. samplers.push("diffuseSampler", "rampSampler");
  1436. if (this._imageProcessingConfiguration) {
  1437. ImageProcessingConfiguration.PrepareUniforms(uniforms, this._imageProcessingConfigurationDefines);
  1438. ImageProcessingConfiguration.PrepareSamplers(samplers, this._imageProcessingConfigurationDefines);
  1439. }
  1440. }
  1441. /** @hidden */
  1442. private _getEffect(blendMode: number): Effect {
  1443. const customEffect = this.getCustomEffect(blendMode);
  1444. if (customEffect) {
  1445. return customEffect;
  1446. }
  1447. var defines: Array<string> = [];
  1448. this.fillDefines(defines, blendMode);
  1449. // Effect
  1450. var join = defines.join("\n");
  1451. if (this._cachedDefines !== join) {
  1452. this._cachedDefines = join;
  1453. var attributesNamesOrOptions: Array<string> = [];
  1454. var effectCreationOption: Array<string> = [];
  1455. var samplers: Array<string> = [];
  1456. this.fillUniformsAttributesAndSamplerNames(effectCreationOption, attributesNamesOrOptions, samplers);
  1457. this._effect = this._engine.createEffect(
  1458. "particles",
  1459. attributesNamesOrOptions,
  1460. effectCreationOption,
  1461. samplers, join);
  1462. }
  1463. return this._effect;
  1464. }
  1465. /**
  1466. * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
  1467. * @param preWarmOnly will prevent the system from updating the vertex buffer (default is false)
  1468. */
  1469. public animate(preWarmOnly = false): void {
  1470. if (!this._started) {
  1471. return;
  1472. }
  1473. if (!preWarmOnly && this._scene) {
  1474. // Check
  1475. if (!this.isReady()) {
  1476. return;
  1477. }
  1478. if (this._currentRenderId === this._scene.getFrameId()) {
  1479. return;
  1480. }
  1481. this._currentRenderId = this._scene.getFrameId();
  1482. }
  1483. this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene?.getAnimationRatio() || 1);
  1484. // Determine the number of particles we need to create
  1485. var newParticles;
  1486. if (this.manualEmitCount > -1) {
  1487. newParticles = this.manualEmitCount;
  1488. this._newPartsExcess = 0;
  1489. this.manualEmitCount = 0;
  1490. } else {
  1491. let rate = this.emitRate;
  1492. if (this._emitRateGradients && this._emitRateGradients.length > 0 && this.targetStopDuration) {
  1493. const ratio = this._actualFrame / this.targetStopDuration;
  1494. GradientHelper.GetCurrentGradient(ratio, this._emitRateGradients, (currentGradient, nextGradient, scale) => {
  1495. if (currentGradient !== this._currentEmitRateGradient) {
  1496. this._currentEmitRate1 = this._currentEmitRate2;
  1497. this._currentEmitRate2 = (<FactorGradient>nextGradient).getFactor();
  1498. this._currentEmitRateGradient = (<FactorGradient>currentGradient);
  1499. }
  1500. rate = Scalar.Lerp(this._currentEmitRate1, this._currentEmitRate2, scale);
  1501. });
  1502. }
  1503. newParticles = ((rate * this._scaledUpdateSpeed) >> 0);
  1504. this._newPartsExcess += rate * this._scaledUpdateSpeed - newParticles;
  1505. }
  1506. if (this._newPartsExcess > 1.0) {
  1507. newParticles += this._newPartsExcess >> 0;
  1508. this._newPartsExcess -= this._newPartsExcess >> 0;
  1509. }
  1510. this._alive = false;
  1511. if (!this._stopped) {
  1512. this._actualFrame += this._scaledUpdateSpeed;
  1513. if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration) {
  1514. this.stop();
  1515. }
  1516. } else {
  1517. newParticles = 0;
  1518. }
  1519. this._update(newParticles);
  1520. // Stopped?
  1521. if (this._stopped) {
  1522. if (!this._alive) {
  1523. this._started = false;
  1524. if (this.onAnimationEnd) {
  1525. this.onAnimationEnd();
  1526. }
  1527. if (this.disposeOnStop && this._scene) {
  1528. this._scene._toBeDisposed.push(this);
  1529. }
  1530. }
  1531. }
  1532. if (!preWarmOnly) {
  1533. // Update VBO
  1534. var offset = 0;
  1535. for (var index = 0; index < this._particles.length; index++) {
  1536. var particle = this._particles[index];
  1537. this._appendParticleVertices(offset, particle);
  1538. offset += this._useInstancing ? 1 : 4;
  1539. }
  1540. if (this._vertexBuffer) {
  1541. this._vertexBuffer.update(this._vertexData);
  1542. }
  1543. }
  1544. if (this.manualEmitCount === 0 && this.disposeOnStop) {
  1545. this.stop();
  1546. }
  1547. }
  1548. private _appendParticleVertices(offset: number, particle: Particle) {
  1549. this._appendParticleVertex(offset++, particle, 0, 0);
  1550. if (!this._useInstancing) {
  1551. this._appendParticleVertex(offset++, particle, 1, 0);
  1552. this._appendParticleVertex(offset++, particle, 1, 1);
  1553. this._appendParticleVertex(offset++, particle, 0, 1);
  1554. }
  1555. }
  1556. /**
  1557. * Rebuilds the particle system.
  1558. */
  1559. public rebuild(): void {
  1560. this._createIndexBuffer();
  1561. if (this._vertexBuffer) {
  1562. this._vertexBuffer._rebuild();
  1563. }
  1564. for (var key in this._vertexBuffers) {
  1565. this._vertexBuffers[key]._rebuild();
  1566. }
  1567. }
  1568. /**
  1569. * Is this system ready to be used/rendered
  1570. * @return true if the system is ready
  1571. */
  1572. public isReady(): boolean {
  1573. if (!this.emitter || this._imageProcessingConfiguration && !this._imageProcessingConfiguration.isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
  1574. return false;
  1575. }
  1576. if (this.blendMode !== ParticleSystem.BLENDMODE_MULTIPLYADD) {
  1577. if (!this._getEffect(this.blendMode).isReady()) {
  1578. return false;
  1579. }
  1580. } else {
  1581. if (!this._getEffect(ParticleSystem.BLENDMODE_MULTIPLY).isReady()) {
  1582. return false;
  1583. }
  1584. if (!this._getEffect(ParticleSystem.BLENDMODE_ADD).isReady()) {
  1585. return false;
  1586. }
  1587. }
  1588. return true;
  1589. }
  1590. private _render(blendMode: number) {
  1591. var effect = this._getEffect(blendMode);
  1592. var engine = this._engine;
  1593. // Render
  1594. engine.enableEffect(effect);
  1595. var viewMatrix = this.defaultViewMatrix ?? this._scene!.getViewMatrix();
  1596. effect.setTexture("diffuseSampler", this.particleTexture);
  1597. effect.setMatrix("view", viewMatrix);
  1598. effect.setMatrix("projection", this.defaultProjectionMatrix ?? this._scene!.getProjectionMatrix());
  1599. if (this._isAnimationSheetEnabled && this.particleTexture) {
  1600. var baseSize = this.particleTexture.getBaseSize();
  1601. effect.setFloat3("particlesInfos", this.spriteCellWidth / baseSize.width, this.spriteCellHeight / baseSize.height, this.spriteCellWidth / baseSize.width);
  1602. }
  1603. effect.setVector2("translationPivot", this.translationPivot);
  1604. effect.setFloat4("textureMask", this.textureMask.r, this.textureMask.g, this.textureMask.b, this.textureMask.a);
  1605. if (this._isBillboardBased && this._scene) {
  1606. var camera = this._scene.activeCamera!;
  1607. effect.setVector3("eyePosition", camera.globalPosition);
  1608. }
  1609. if (this._rampGradientsTexture) {
  1610. if (!this._rampGradients || !this._rampGradients.length) {
  1611. this._rampGradientsTexture.dispose();
  1612. this._rampGradientsTexture = null;
  1613. }
  1614. effect.setTexture("rampSampler", this._rampGradientsTexture);
  1615. }
  1616. const defines = effect.defines;
  1617. if (this._scene) {
  1618. if (this._scene.clipPlane || this._scene.clipPlane2 || this._scene.clipPlane3 || this._scene.clipPlane4 || this._scene.clipPlane5 || this._scene.clipPlane6) {
  1619. ThinMaterialHelper.BindClipPlane(effect, this._scene);
  1620. }
  1621. }
  1622. if (defines.indexOf("#define BILLBOARDMODE_ALL") >= 0) {
  1623. viewMatrix.invertToRef(TmpVectors.Matrix[0]);
  1624. effect.setMatrix("invView", TmpVectors.Matrix[0]);
  1625. }
  1626. if (this._vertexArrayObject !== undefined) {
  1627. if (!this._vertexArrayObject) {
  1628. this._vertexArrayObject = this._engine.recordVertexArrayObject(this._vertexBuffers, this._indexBuffer, effect);
  1629. }
  1630. this._engine.bindVertexArrayObject(this._vertexArrayObject, this._indexBuffer);
  1631. } else {
  1632. engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
  1633. }
  1634. // image processing
  1635. if (this._imageProcessingConfiguration && !this._imageProcessingConfiguration.applyByPostProcess) {
  1636. this._imageProcessingConfiguration.bind(effect);
  1637. }
  1638. // Draw order
  1639. switch (blendMode) {
  1640. case ParticleSystem.BLENDMODE_ADD:
  1641. engine.setAlphaMode(Constants.ALPHA_ADD);
  1642. break;
  1643. case ParticleSystem.BLENDMODE_ONEONE:
  1644. engine.setAlphaMode(Constants.ALPHA_ONEONE);
  1645. break;
  1646. case ParticleSystem.BLENDMODE_STANDARD:
  1647. engine.setAlphaMode(Constants.ALPHA_COMBINE);
  1648. break;
  1649. case ParticleSystem.BLENDMODE_MULTIPLY:
  1650. engine.setAlphaMode(Constants.ALPHA_MULTIPLY);
  1651. break;
  1652. }
  1653. if (this._onBeforeDrawParticlesObservable) {
  1654. this._onBeforeDrawParticlesObservable.notifyObservers(effect);
  1655. }
  1656. if (this._useInstancing) {
  1657. engine.drawArraysType(Constants.MATERIAL_TriangleFanDrawMode, 0, 4, this._particles.length);
  1658. } else {
  1659. engine.drawElementsType(Constants.MATERIAL_TriangleFillMode, 0, this._particles.length * 6);
  1660. }
  1661. return this._particles.length;
  1662. }
  1663. /**
  1664. * Renders the particle system in its current state.
  1665. * @returns the current number of particles
  1666. */
  1667. public render(): number {
  1668. // Check
  1669. if (!this.isReady() || !this._particles.length) {
  1670. return 0;
  1671. }
  1672. var engine = this._engine as any;
  1673. if (engine.setState) {
  1674. engine.setState(false);
  1675. if (this.forceDepthWrite) {
  1676. engine.setDepthWrite(true);
  1677. }
  1678. }
  1679. let outparticles = 0;
  1680. if (this.blendMode === ParticleSystem.BLENDMODE_MULTIPLYADD) {
  1681. outparticles = this._render(ParticleSystem.BLENDMODE_MULTIPLY) + this._render(ParticleSystem.BLENDMODE_ADD);
  1682. }
  1683. outparticles = this._render(this.blendMode);
  1684. this._engine.unbindInstanceAttributes();
  1685. this._engine.setAlphaMode(Constants.ALPHA_DISABLE);
  1686. return outparticles;
  1687. }
  1688. /**
  1689. * Disposes the particle system and free the associated resources
  1690. * @param disposeTexture defines if the particule texture must be disposed as well (true by default)
  1691. */
  1692. public dispose(disposeTexture = true): void {
  1693. if (this._vertexBuffer) {
  1694. this._vertexBuffer.dispose();
  1695. this._vertexBuffer = null;
  1696. }
  1697. if (this._spriteBuffer) {
  1698. this._spriteBuffer.dispose();
  1699. this._spriteBuffer = null;
  1700. }
  1701. if (this._indexBuffer) {
  1702. this._engine._releaseBuffer(this._indexBuffer);
  1703. this._indexBuffer = null;
  1704. }
  1705. if (this._vertexArrayObject) {
  1706. this._engine.releaseVertexArrayObject(this._vertexArrayObject);
  1707. this._vertexArrayObject = null;
  1708. }
  1709. if (disposeTexture && this.particleTexture) {
  1710. this.particleTexture.dispose();
  1711. this.particleTexture = null;
  1712. }
  1713. if (disposeTexture && this.noiseTexture) {
  1714. this.noiseTexture.dispose();
  1715. this.noiseTexture = null;
  1716. }
  1717. if (this._rampGradientsTexture) {
  1718. this._rampGradientsTexture.dispose();
  1719. this._rampGradientsTexture = null;
  1720. }
  1721. this._removeFromRoot();
  1722. if (this._subEmitters && this._subEmitters.length) {
  1723. for (var index = 0; index < this._subEmitters.length; index++) {
  1724. for (var subEmitter of this._subEmitters[index]) {
  1725. subEmitter.dispose();
  1726. }
  1727. }
  1728. this._subEmitters = [];
  1729. this.subEmitters = [];
  1730. }
  1731. if (this._disposeEmitterOnDispose && this.emitter && (this.emitter as AbstractMesh).dispose) {
  1732. (<AbstractMesh>this.emitter).dispose(true);
  1733. }
  1734. if (this._onBeforeDrawParticlesObservable) {
  1735. this._onBeforeDrawParticlesObservable.clear();
  1736. }
  1737. // Remove from scene
  1738. if (this._scene) {
  1739. var index = this._scene.particleSystems.indexOf(this);
  1740. if (index > -1) {
  1741. this._scene.particleSystems.splice(index, 1);
  1742. }
  1743. this._scene._activeParticleSystems.dispose();
  1744. }
  1745. // Callback
  1746. this.onDisposeObservable.notifyObservers(this);
  1747. this.onDisposeObservable.clear();
  1748. this.onStoppedObservable.clear();
  1749. this.reset();
  1750. }
  1751. // Clone
  1752. /**
  1753. * Clones the particle system.
  1754. * @param name The name of the cloned object
  1755. * @param newEmitter The new emitter to use
  1756. * @returns the cloned particle system
  1757. */
  1758. public clone(name: string, newEmitter: any): ParticleSystem {
  1759. var custom = { ...this._customEffect };
  1760. var program: any = null;
  1761. var engine = this._engine as any;
  1762. if (engine.createEffectForParticles) {
  1763. if (this.customShader != null) {
  1764. program = this.customShader;
  1765. var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
  1766. custom[0] = engine.createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
  1767. }
  1768. }
  1769. let serialization = this.serialize();
  1770. var result = ParticleSystem.Parse(serialization, this._scene || this._engine, "");
  1771. result.name = name;
  1772. result.customShader = program;
  1773. result._customEffect = custom;
  1774. if (newEmitter === undefined) {
  1775. newEmitter = this.emitter;
  1776. }
  1777. if (this.noiseTexture) {
  1778. result.noiseTexture = this.noiseTexture.clone();
  1779. }
  1780. result.emitter = newEmitter;
  1781. if (!this.preventAutoStart) {
  1782. result.start();
  1783. }
  1784. return result;
  1785. }
  1786. /**
  1787. * Serializes the particle system to a JSON object
  1788. * @param serializeTexture defines if the texture must be serialized as well
  1789. * @returns the JSON object
  1790. */
  1791. public serialize(serializeTexture = false): any {
  1792. var serializationObject: any = {};
  1793. ParticleSystem._Serialize(serializationObject, this, serializeTexture);
  1794. serializationObject.textureMask = this.textureMask.asArray();
  1795. serializationObject.customShader = this.customShader;
  1796. serializationObject.preventAutoStart = this.preventAutoStart;
  1797. // SubEmitters
  1798. if (this.subEmitters) {
  1799. serializationObject.subEmitters = [];
  1800. if (!this._subEmitters) {
  1801. this._prepareSubEmitterInternalArray();
  1802. }
  1803. for (var subs of this._subEmitters) {
  1804. let cell = [];
  1805. for (var sub of subs) {
  1806. cell.push(sub.serialize());
  1807. }
  1808. serializationObject.subEmitters.push(cell);
  1809. }
  1810. }
  1811. return serializationObject;
  1812. }
  1813. /** @hidden */
  1814. public static _Serialize(serializationObject: any, particleSystem: IParticleSystem, serializeTexture: boolean) {
  1815. serializationObject.name = particleSystem.name;
  1816. serializationObject.id = particleSystem.id;
  1817. serializationObject.capacity = particleSystem.getCapacity();
  1818. // Emitter
  1819. if ((<AbstractMesh>particleSystem.emitter).position) {
  1820. var emitterMesh = (<AbstractMesh>particleSystem.emitter);
  1821. serializationObject.emitterId = emitterMesh.id;
  1822. } else {
  1823. var emitterPosition = (<Vector3>particleSystem.emitter);
  1824. serializationObject.emitter = emitterPosition.asArray();
  1825. }
  1826. // Emitter
  1827. if (particleSystem.particleEmitterType) {
  1828. serializationObject.particleEmitterType = particleSystem.particleEmitterType.serialize();
  1829. }
  1830. if (particleSystem.particleTexture) {
  1831. if (serializeTexture) {
  1832. serializationObject.texture = particleSystem.particleTexture.serialize();
  1833. } else {
  1834. serializationObject.textureName = particleSystem.particleTexture.name;
  1835. serializationObject.invertY = !!(particleSystem.particleTexture as any)._invertY;
  1836. }
  1837. }
  1838. serializationObject.isLocal = particleSystem.isLocal;
  1839. // Animations
  1840. SerializationHelper.AppendSerializedAnimations(particleSystem, serializationObject);
  1841. serializationObject.beginAnimationOnStart = particleSystem.beginAnimationOnStart;
  1842. serializationObject.beginAnimationFrom = particleSystem.beginAnimationFrom;
  1843. serializationObject.beginAnimationTo = particleSystem.beginAnimationTo;
  1844. serializationObject.beginAnimationLoop = particleSystem.beginAnimationLoop;
  1845. // Particle system
  1846. serializationObject.startDelay = particleSystem.startDelay;
  1847. serializationObject.renderingGroupId = particleSystem.renderingGroupId;
  1848. serializationObject.isBillboardBased = particleSystem.isBillboardBased;
  1849. serializationObject.billboardMode = particleSystem.billboardMode;
  1850. serializationObject.minAngularSpeed = particleSystem.minAngularSpeed;
  1851. serializationObject.maxAngularSpeed = particleSystem.maxAngularSpeed;
  1852. serializationObject.minSize = particleSystem.minSize;
  1853. serializationObject.maxSize = particleSystem.maxSize;
  1854. serializationObject.minScaleX = particleSystem.minScaleX;
  1855. serializationObject.maxScaleX = particleSystem.maxScaleX;
  1856. serializationObject.minScaleY = particleSystem.minScaleY;
  1857. serializationObject.maxScaleY = particleSystem.maxScaleY;
  1858. serializationObject.minEmitPower = particleSystem.minEmitPower;
  1859. serializationObject.maxEmitPower = particleSystem.maxEmitPower;
  1860. serializationObject.minLifeTime = particleSystem.minLifeTime;
  1861. serializationObject.maxLifeTime = particleSystem.maxLifeTime;
  1862. serializationObject.emitRate = particleSystem.emitRate;
  1863. serializationObject.gravity = particleSystem.gravity.asArray();
  1864. serializationObject.noiseStrength = particleSystem.noiseStrength.asArray();
  1865. serializationObject.color1 = particleSystem.color1.asArray();
  1866. serializationObject.color2 = particleSystem.color2.asArray();
  1867. serializationObject.colorDead = particleSystem.colorDead.asArray();
  1868. serializationObject.updateSpeed = particleSystem.updateSpeed;
  1869. serializationObject.targetStopDuration = particleSystem.targetStopDuration;
  1870. serializationObject.blendMode = particleSystem.blendMode;
  1871. serializationObject.preWarmCycles = particleSystem.preWarmCycles;
  1872. serializationObject.preWarmStepOffset = particleSystem.preWarmStepOffset;
  1873. serializationObject.minInitialRotation = particleSystem.minInitialRotation;
  1874. serializationObject.maxInitialRotation = particleSystem.maxInitialRotation;
  1875. serializationObject.startSpriteCellID = particleSystem.startSpriteCellID;
  1876. serializationObject.endSpriteCellID = particleSystem.endSpriteCellID;
  1877. serializationObject.spriteCellChangeSpeed = particleSystem.spriteCellChangeSpeed;
  1878. serializationObject.spriteCellWidth = particleSystem.spriteCellWidth;
  1879. serializationObject.spriteCellHeight = particleSystem.spriteCellHeight;
  1880. serializationObject.spriteRandomStartCell = particleSystem.spriteRandomStartCell;
  1881. serializationObject.isAnimationSheetEnabled = particleSystem.isAnimationSheetEnabled;
  1882. let colorGradients = particleSystem.getColorGradients();
  1883. if (colorGradients) {
  1884. serializationObject.colorGradients = [];
  1885. for (var colorGradient of colorGradients) {
  1886. var serializedGradient: any = {
  1887. gradient: colorGradient.gradient,
  1888. color1: colorGradient.color1.asArray()
  1889. };
  1890. if (colorGradient.color2) {
  1891. serializedGradient.color2 = colorGradient.color2.asArray();
  1892. } else {
  1893. serializedGradient.color2 = colorGradient.color1.asArray();
  1894. }
  1895. serializationObject.colorGradients.push(serializedGradient);
  1896. }
  1897. }
  1898. let rampGradients = particleSystem.getRampGradients();
  1899. if (rampGradients) {
  1900. serializationObject.rampGradients = [];
  1901. for (var rampGradient of rampGradients) {
  1902. var serializedGradient: any = {
  1903. gradient: rampGradient.gradient,
  1904. color: rampGradient.color.asArray()
  1905. };
  1906. serializationObject.rampGradients.push(serializedGradient);
  1907. }
  1908. serializationObject.useRampGradients = particleSystem.useRampGradients;
  1909. }
  1910. let colorRemapGradients = particleSystem.getColorRemapGradients();
  1911. if (colorRemapGradients) {
  1912. serializationObject.colorRemapGradients = [];
  1913. for (var colorRemapGradient of colorRemapGradients) {
  1914. var serializedGradient: any = {
  1915. gradient: colorRemapGradient.gradient,
  1916. factor1: colorRemapGradient.factor1
  1917. };
  1918. if (colorRemapGradient.factor2 !== undefined) {
  1919. serializedGradient.factor2 = colorRemapGradient.factor2;
  1920. } else {
  1921. serializedGradient.factor2 = colorRemapGradient.factor1;
  1922. }
  1923. serializationObject.colorRemapGradients.push(serializedGradient);
  1924. }
  1925. }
  1926. let alphaRemapGradients = particleSystem.getAlphaRemapGradients();
  1927. if (alphaRemapGradients) {
  1928. serializationObject.alphaRemapGradients = [];
  1929. for (var alphaRemapGradient of alphaRemapGradients) {
  1930. var serializedGradient: any = {
  1931. gradient: alphaRemapGradient.gradient,
  1932. factor1: alphaRemapGradient.factor1
  1933. };
  1934. if (alphaRemapGradient.factor2 !== undefined) {
  1935. serializedGradient.factor2 = alphaRemapGradient.factor2;
  1936. } else {
  1937. serializedGradient.factor2 = alphaRemapGradient.factor1;
  1938. }
  1939. serializationObject.alphaRemapGradients.push(serializedGradient);
  1940. }
  1941. }
  1942. let sizeGradients = particleSystem.getSizeGradients();
  1943. if (sizeGradients) {
  1944. serializationObject.sizeGradients = [];
  1945. for (var sizeGradient of sizeGradients) {
  1946. var serializedGradient: any = {
  1947. gradient: sizeGradient.gradient,
  1948. factor1: sizeGradient.factor1
  1949. };
  1950. if (sizeGradient.factor2 !== undefined) {
  1951. serializedGradient.factor2 = sizeGradient.factor2;
  1952. } else {
  1953. serializedGradient.factor2 = sizeGradient.factor1;
  1954. }
  1955. serializationObject.sizeGradients.push(serializedGradient);
  1956. }
  1957. }
  1958. let angularSpeedGradients = particleSystem.getAngularSpeedGradients();
  1959. if (angularSpeedGradients) {
  1960. serializationObject.angularSpeedGradients = [];
  1961. for (var angularSpeedGradient of angularSpeedGradients) {
  1962. var serializedGradient: any = {
  1963. gradient: angularSpeedGradient.gradient,
  1964. factor1: angularSpeedGradient.factor1
  1965. };
  1966. if (angularSpeedGradient.factor2 !== undefined) {
  1967. serializedGradient.factor2 = angularSpeedGradient.factor2;
  1968. } else {
  1969. serializedGradient.factor2 = angularSpeedGradient.factor1;
  1970. }
  1971. serializationObject.angularSpeedGradients.push(serializedGradient);
  1972. }
  1973. }
  1974. let velocityGradients = particleSystem.getVelocityGradients();
  1975. if (velocityGradients) {
  1976. serializationObject.velocityGradients = [];
  1977. for (var velocityGradient of velocityGradients) {
  1978. var serializedGradient: any = {
  1979. gradient: velocityGradient.gradient,
  1980. factor1: velocityGradient.factor1
  1981. };
  1982. if (velocityGradient.factor2 !== undefined) {
  1983. serializedGradient.factor2 = velocityGradient.factor2;
  1984. } else {
  1985. serializedGradient.factor2 = velocityGradient.factor1;
  1986. }
  1987. serializationObject.velocityGradients.push(serializedGradient);
  1988. }
  1989. }
  1990. let dragGradients = particleSystem.getDragGradients();
  1991. if (dragGradients) {
  1992. serializationObject.dragGradients = [];
  1993. for (var dragGradient of dragGradients) {
  1994. var serializedGradient: any = {
  1995. gradient: dragGradient.gradient,
  1996. factor1: dragGradient.factor1
  1997. };
  1998. if (dragGradient.factor2 !== undefined) {
  1999. serializedGradient.factor2 = dragGradient.factor2;
  2000. } else {
  2001. serializedGradient.factor2 = dragGradient.factor1;
  2002. }
  2003. serializationObject.dragGradients.push(serializedGradient);
  2004. }
  2005. }
  2006. let emitRateGradients = particleSystem.getEmitRateGradients();
  2007. if (emitRateGradients) {
  2008. serializationObject.emitRateGradients = [];
  2009. for (var emitRateGradient of emitRateGradients) {
  2010. var serializedGradient: any = {
  2011. gradient: emitRateGradient.gradient,
  2012. factor1: emitRateGradient.factor1
  2013. };
  2014. if (emitRateGradient.factor2 !== undefined) {
  2015. serializedGradient.factor2 = emitRateGradient.factor2;
  2016. } else {
  2017. serializedGradient.factor2 = emitRateGradient.factor1;
  2018. }
  2019. serializationObject.emitRateGradients.push(serializedGradient);
  2020. }
  2021. }
  2022. let startSizeGradients = particleSystem.getStartSizeGradients();
  2023. if (startSizeGradients) {
  2024. serializationObject.startSizeGradients = [];
  2025. for (var startSizeGradient of startSizeGradients) {
  2026. var serializedGradient: any = {
  2027. gradient: startSizeGradient.gradient,
  2028. factor1: startSizeGradient.factor1
  2029. };
  2030. if (startSizeGradient.factor2 !== undefined) {
  2031. serializedGradient.factor2 = startSizeGradient.factor2;
  2032. } else {
  2033. serializedGradient.factor2 = startSizeGradient.factor1;
  2034. }
  2035. serializationObject.startSizeGradients.push(serializedGradient);
  2036. }
  2037. }
  2038. let lifeTimeGradients = particleSystem.getLifeTimeGradients();
  2039. if (lifeTimeGradients) {
  2040. serializationObject.lifeTimeGradients = [];
  2041. for (var lifeTimeGradient of lifeTimeGradients) {
  2042. var serializedGradient: any = {
  2043. gradient: lifeTimeGradient.gradient,
  2044. factor1: lifeTimeGradient.factor1
  2045. };
  2046. if (lifeTimeGradient.factor2 !== undefined) {
  2047. serializedGradient.factor2 = lifeTimeGradient.factor2;
  2048. } else {
  2049. serializedGradient.factor2 = lifeTimeGradient.factor1;
  2050. }
  2051. serializationObject.lifeTimeGradients.push(serializedGradient);
  2052. }
  2053. }
  2054. let limitVelocityGradients = particleSystem.getLimitVelocityGradients();
  2055. if (limitVelocityGradients) {
  2056. serializationObject.limitVelocityGradients = [];
  2057. for (var limitVelocityGradient of limitVelocityGradients) {
  2058. var serializedGradient: any = {
  2059. gradient: limitVelocityGradient.gradient,
  2060. factor1: limitVelocityGradient.factor1
  2061. };
  2062. if (limitVelocityGradient.factor2 !== undefined) {
  2063. serializedGradient.factor2 = limitVelocityGradient.factor2;
  2064. } else {
  2065. serializedGradient.factor2 = limitVelocityGradient.factor1;
  2066. }
  2067. serializationObject.limitVelocityGradients.push(serializedGradient);
  2068. }
  2069. serializationObject.limitVelocityDamping = particleSystem.limitVelocityDamping;
  2070. }
  2071. if (particleSystem.noiseTexture) {
  2072. serializationObject.noiseTexture = particleSystem.noiseTexture.serialize();
  2073. }
  2074. }
  2075. /** @hidden */
  2076. public static _Parse(parsedParticleSystem: any, particleSystem: IParticleSystem, sceneOrEngine: Scene | ThinEngine, rootUrl: string) {
  2077. let scene: Nullable<Scene>;
  2078. if (sceneOrEngine instanceof ThinEngine) {
  2079. scene = null;
  2080. } else {
  2081. scene = sceneOrEngine as Scene;
  2082. }
  2083. const internalClass = _TypeStore.GetClass("BABYLON.Texture");
  2084. if (internalClass && scene) {
  2085. // Texture
  2086. if (parsedParticleSystem.texture) {
  2087. particleSystem.particleTexture = internalClass.Parse(parsedParticleSystem.texture, scene, rootUrl) as BaseTexture;
  2088. } else if (parsedParticleSystem.textureName) {
  2089. particleSystem.particleTexture = new internalClass(rootUrl + parsedParticleSystem.textureName, scene, false, parsedParticleSystem.invertY !== undefined ? parsedParticleSystem.invertY : true);
  2090. particleSystem.particleTexture!.name = parsedParticleSystem.textureName;
  2091. }
  2092. }
  2093. // Emitter
  2094. if (!parsedParticleSystem.emitterId && parsedParticleSystem.emitterId !== 0 && parsedParticleSystem.emitter === undefined) {
  2095. particleSystem.emitter = Vector3.Zero();
  2096. }
  2097. else if (parsedParticleSystem.emitterId && scene) {
  2098. particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
  2099. } else {
  2100. particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
  2101. }
  2102. particleSystem.isLocal = !!parsedParticleSystem.isLocal;
  2103. // Misc.
  2104. if (parsedParticleSystem.renderingGroupId !== undefined) {
  2105. particleSystem.renderingGroupId = parsedParticleSystem.renderingGroupId;
  2106. }
  2107. if (parsedParticleSystem.isBillboardBased !== undefined) {
  2108. particleSystem.isBillboardBased = parsedParticleSystem.isBillboardBased;
  2109. }
  2110. if (parsedParticleSystem.billboardMode !== undefined) {
  2111. particleSystem.billboardMode = parsedParticleSystem.billboardMode;
  2112. }
  2113. // Animations
  2114. if (parsedParticleSystem.animations) {
  2115. for (var animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
  2116. var parsedAnimation = parsedParticleSystem.animations[animationIndex];
  2117. const internalClass = _TypeStore.GetClass("BABYLON.Animation");
  2118. if (internalClass) {
  2119. particleSystem.animations.push(internalClass.Parse(parsedAnimation));
  2120. }
  2121. }
  2122. particleSystem.beginAnimationOnStart = parsedParticleSystem.beginAnimationOnStart;
  2123. particleSystem.beginAnimationFrom = parsedParticleSystem.beginAnimationFrom;
  2124. particleSystem.beginAnimationTo = parsedParticleSystem.beginAnimationTo;
  2125. particleSystem.beginAnimationLoop = parsedParticleSystem.beginAnimationLoop;
  2126. }
  2127. if (parsedParticleSystem.autoAnimate && scene) {
  2128. scene.beginAnimation(particleSystem, parsedParticleSystem.autoAnimateFrom, parsedParticleSystem.autoAnimateTo, parsedParticleSystem.autoAnimateLoop, parsedParticleSystem.autoAnimateSpeed || 1.0);
  2129. }
  2130. // Particle system
  2131. particleSystem.startDelay = parsedParticleSystem.startDelay | 0;
  2132. particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
  2133. particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
  2134. particleSystem.minSize = parsedParticleSystem.minSize;
  2135. particleSystem.maxSize = parsedParticleSystem.maxSize;
  2136. if (parsedParticleSystem.minScaleX) {
  2137. particleSystem.minScaleX = parsedParticleSystem.minScaleX;
  2138. particleSystem.maxScaleX = parsedParticleSystem.maxScaleX;
  2139. particleSystem.minScaleY = parsedParticleSystem.minScaleY;
  2140. particleSystem.maxScaleY = parsedParticleSystem.maxScaleY;
  2141. }
  2142. if (parsedParticleSystem.preWarmCycles !== undefined) {
  2143. particleSystem.preWarmCycles = parsedParticleSystem.preWarmCycles;
  2144. particleSystem.preWarmStepOffset = parsedParticleSystem.preWarmStepOffset;
  2145. }
  2146. if (parsedParticleSystem.minInitialRotation !== undefined) {
  2147. particleSystem.minInitialRotation = parsedParticleSystem.minInitialRotation;
  2148. particleSystem.maxInitialRotation = parsedParticleSystem.maxInitialRotation;
  2149. }
  2150. particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
  2151. particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
  2152. particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
  2153. particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
  2154. particleSystem.emitRate = parsedParticleSystem.emitRate;
  2155. particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
  2156. if (parsedParticleSystem.noiseStrength) {
  2157. particleSystem.noiseStrength = Vector3.FromArray(parsedParticleSystem.noiseStrength);
  2158. }
  2159. particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
  2160. particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
  2161. particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
  2162. particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
  2163. particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
  2164. particleSystem.blendMode = parsedParticleSystem.blendMode;
  2165. if (parsedParticleSystem.colorGradients) {
  2166. for (var colorGradient of parsedParticleSystem.colorGradients) {
  2167. particleSystem.addColorGradient(colorGradient.gradient, Color4.FromArray(colorGradient.color1), colorGradient.color2 ? Color4.FromArray(colorGradient.color2) : undefined);
  2168. }
  2169. }
  2170. if (parsedParticleSystem.rampGradients) {
  2171. for (var rampGradient of parsedParticleSystem.rampGradients) {
  2172. particleSystem.addRampGradient(rampGradient.gradient, Color3.FromArray(rampGradient.color));
  2173. }
  2174. particleSystem.useRampGradients = parsedParticleSystem.useRampGradients;
  2175. }
  2176. if (parsedParticleSystem.colorRemapGradients) {
  2177. for (var colorRemapGradient of parsedParticleSystem.colorRemapGradients) {
  2178. particleSystem.addColorRemapGradient(colorRemapGradient.gradient, colorRemapGradient.factor1 !== undefined ? colorRemapGradient.factor1 : colorRemapGradient.factor, colorRemapGradient.factor2);
  2179. }
  2180. }
  2181. if (parsedParticleSystem.alphaRemapGradients) {
  2182. for (var alphaRemapGradient of parsedParticleSystem.alphaRemapGradients) {
  2183. particleSystem.addAlphaRemapGradient(alphaRemapGradient.gradient, alphaRemapGradient.factor1 !== undefined ? alphaRemapGradient.factor1 : alphaRemapGradient.factor, alphaRemapGradient.factor2);
  2184. }
  2185. }
  2186. if (parsedParticleSystem.sizeGradients) {
  2187. for (var sizeGradient of parsedParticleSystem.sizeGradients) {
  2188. particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ? sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
  2189. }
  2190. }
  2191. if (parsedParticleSystem.angularSpeedGradients) {
  2192. for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
  2193. particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ? angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
  2194. }
  2195. }
  2196. if (parsedParticleSystem.velocityGradients) {
  2197. for (var velocityGradient of parsedParticleSystem.velocityGradients) {
  2198. particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ? velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
  2199. }
  2200. }
  2201. if (parsedParticleSystem.dragGradients) {
  2202. for (var dragGradient of parsedParticleSystem.dragGradients) {
  2203. particleSystem.addDragGradient(dragGradient.gradient, dragGradient.factor1 !== undefined ? dragGradient.factor1 : dragGradient.factor, dragGradient.factor2);
  2204. }
  2205. }
  2206. if (parsedParticleSystem.emitRateGradients) {
  2207. for (var emitRateGradient of parsedParticleSystem.emitRateGradients) {
  2208. particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ? emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
  2209. }
  2210. }
  2211. if (parsedParticleSystem.startSizeGradients) {
  2212. for (var startSizeGradient of parsedParticleSystem.startSizeGradients) {
  2213. particleSystem.addStartSizeGradient(startSizeGradient.gradient, startSizeGradient.factor1 !== undefined ? startSizeGradient.factor1 : startSizeGradient.factor, startSizeGradient.factor2);
  2214. }
  2215. }
  2216. if (parsedParticleSystem.lifeTimeGradients) {
  2217. for (var lifeTimeGradient of parsedParticleSystem.lifeTimeGradients) {
  2218. particleSystem.addLifeTimeGradient(lifeTimeGradient.gradient, lifeTimeGradient.factor1 !== undefined ? lifeTimeGradient.factor1 : lifeTimeGradient.factor, lifeTimeGradient.factor2);
  2219. }
  2220. }
  2221. if (parsedParticleSystem.limitVelocityGradients) {
  2222. for (var limitVelocityGradient of parsedParticleSystem.limitVelocityGradients) {
  2223. particleSystem.addLimitVelocityGradient(limitVelocityGradient.gradient, limitVelocityGradient.factor1 !== undefined ? limitVelocityGradient.factor1 : limitVelocityGradient.factor, limitVelocityGradient.factor2);
  2224. }
  2225. particleSystem.limitVelocityDamping = parsedParticleSystem.limitVelocityDamping;
  2226. }
  2227. if (parsedParticleSystem.noiseTexture && scene) {
  2228. const internalClass = _TypeStore.GetClass("BABYLON.ProceduralTexture");
  2229. particleSystem.noiseTexture = internalClass.Parse(parsedParticleSystem.noiseTexture, scene, rootUrl);
  2230. }
  2231. // Emitter
  2232. let emitterType: IParticleEmitterType;
  2233. if (parsedParticleSystem.particleEmitterType) {
  2234. switch (parsedParticleSystem.particleEmitterType.type) {
  2235. case "SphereParticleEmitter":
  2236. emitterType = new SphereParticleEmitter();
  2237. break;
  2238. case "SphereDirectedParticleEmitter":
  2239. emitterType = new SphereDirectedParticleEmitter();
  2240. break;
  2241. case "ConeEmitter":
  2242. case "ConeParticleEmitter":
  2243. emitterType = new ConeParticleEmitter();
  2244. break;
  2245. case "CylinderParticleEmitter":
  2246. emitterType = new CylinderParticleEmitter();
  2247. break;
  2248. case "CylinderDirectedParticleEmitter":
  2249. emitterType = new CylinderDirectedParticleEmitter();
  2250. break;
  2251. case "HemisphericParticleEmitter":
  2252. emitterType = new HemisphericParticleEmitter();
  2253. break;
  2254. case "PointParticleEmitter":
  2255. emitterType = new PointParticleEmitter();
  2256. break;
  2257. case "MeshParticleEmitter":
  2258. emitterType = new MeshParticleEmitter();
  2259. break;
  2260. case "BoxEmitter":
  2261. case "BoxParticleEmitter":
  2262. default:
  2263. emitterType = new BoxParticleEmitter();
  2264. break;
  2265. }
  2266. emitterType.parse(parsedParticleSystem.particleEmitterType, scene);
  2267. } else {
  2268. emitterType = new BoxParticleEmitter();
  2269. emitterType.parse(parsedParticleSystem, scene);
  2270. }
  2271. particleSystem.particleEmitterType = emitterType;
  2272. // Animation sheet
  2273. particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
  2274. particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
  2275. particleSystem.spriteCellWidth = parsedParticleSystem.spriteCellWidth;
  2276. particleSystem.spriteCellHeight = parsedParticleSystem.spriteCellHeight;
  2277. particleSystem.spriteCellChangeSpeed = parsedParticleSystem.spriteCellChangeSpeed;
  2278. particleSystem.spriteRandomStartCell = parsedParticleSystem.spriteRandomStartCell;
  2279. }
  2280. /**
  2281. * Parses a JSON object to create a particle system.
  2282. * @param parsedParticleSystem The JSON object to parse
  2283. * @param sceneOrEngine The scene or the engine to create the particle system in
  2284. * @param rootUrl The root url to use to load external dependencies like texture
  2285. * @param doNotStart Ignore the preventAutoStart attribute and does not start
  2286. * @returns the Parsed particle system
  2287. */
  2288. public static Parse(parsedParticleSystem: any, sceneOrEngine: Scene | ThinEngine, rootUrl: string, doNotStart = false): ParticleSystem {
  2289. var name = parsedParticleSystem.name;
  2290. var custom: Nullable<Effect> = null;
  2291. var program: any = null;
  2292. let engine: ThinEngine;
  2293. let scene: Nullable<Scene>;
  2294. if (sceneOrEngine instanceof ThinEngine) {
  2295. engine = sceneOrEngine;
  2296. } else {
  2297. scene = sceneOrEngine as Scene;
  2298. engine = scene.getEngine();
  2299. }
  2300. if (parsedParticleSystem.customShader && (engine as any).createEffectForParticles) {
  2301. program = parsedParticleSystem.customShader;
  2302. var defines: string = (program.shaderOptions.defines.length > 0) ? program.shaderOptions.defines.join("\n") : "";
  2303. custom = (engine as any).createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
  2304. }
  2305. var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, sceneOrEngine, custom, parsedParticleSystem.isAnimationSheetEnabled);
  2306. particleSystem.customShader = program;
  2307. if (parsedParticleSystem.id) {
  2308. particleSystem.id = parsedParticleSystem.id;
  2309. }
  2310. // SubEmitters
  2311. if (parsedParticleSystem.subEmitters) {
  2312. particleSystem.subEmitters = [];
  2313. for (var cell of parsedParticleSystem.subEmitters) {
  2314. let cellArray = [];
  2315. for (var sub of cell) {
  2316. cellArray.push(SubEmitter.Parse(sub, sceneOrEngine, rootUrl));
  2317. }
  2318. particleSystem.subEmitters.push(cellArray);
  2319. }
  2320. }
  2321. ParticleSystem._Parse(parsedParticleSystem, particleSystem, sceneOrEngine, rootUrl);
  2322. if (parsedParticleSystem.textureMask) {
  2323. particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
  2324. }
  2325. // Auto start
  2326. if (parsedParticleSystem.preventAutoStart) {
  2327. particleSystem.preventAutoStart = parsedParticleSystem.preventAutoStart;
  2328. }
  2329. if (!doNotStart && !particleSystem.preventAutoStart) {
  2330. particleSystem.start();
  2331. }
  2332. return particleSystem;
  2333. }
  2334. }
  2335. SubEmitter._ParseParticleSystem = ParticleSystem.Parse;