babylon.particleSystem.ts 95 KB

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