particleSystem.js 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. import { ThinParticleSystem } from "./thinParticleSystem.js";
  2. import { SubEmitter, SubEmitterType } from "./subEmitter.js";
  3. import { Color3, Color4 } from "../Maths/math.color.js";
  4. import { Vector3 } from "../Maths/math.vector.js";
  5. import { AbstractEngine } from "../Engines/abstractEngine.js";
  6. import { GetClass } from "../Misc/typeStore.js";
  7. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  8. import { MeshParticleEmitter } from "./EmitterTypes/meshParticleEmitter.js";
  9. import { CustomParticleEmitter } from "./EmitterTypes/customParticleEmitter.js";
  10. import { BoxParticleEmitter } from "./EmitterTypes/boxParticleEmitter.js";
  11. import { PointParticleEmitter } from "./EmitterTypes/pointParticleEmitter.js";
  12. import { HemisphericParticleEmitter } from "./EmitterTypes/hemisphericParticleEmitter.js";
  13. import { SphereDirectedParticleEmitter, SphereParticleEmitter } from "./EmitterTypes/sphereParticleEmitter.js";
  14. import { CylinderDirectedParticleEmitter, CylinderParticleEmitter } from "./EmitterTypes/cylinderParticleEmitter.js";
  15. import { ConeParticleEmitter } from "./EmitterTypes/coneParticleEmitter.js";
  16. import { CreateConeEmitter, CreateCylinderEmitter, CreateDirectedCylinderEmitter, CreateDirectedSphereEmitter, CreateHemisphericEmitter, CreatePointEmitter, CreateSphereEmitter, } from "./particleSystem.functions.js";
  17. /**
  18. * This represents a particle system in Babylon.
  19. * 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.
  20. * Particles can take different shapes while emitted like box, sphere, cone or you can write your custom function.
  21. * @example https://doc.babylonjs.com/features/featuresDeepDive/particles/particle_system/particle_system_intro
  22. */
  23. export class ParticleSystem extends ThinParticleSystem {
  24. constructor() {
  25. super(...arguments);
  26. /**
  27. * @internal
  28. * If the particle systems emitter should be disposed when the particle system is disposed
  29. */
  30. this._disposeEmitterOnDispose = false;
  31. this._emitFromParticle = (particle) => {
  32. if (!this._subEmitters || this._subEmitters.length === 0) {
  33. return;
  34. }
  35. const templateIndex = Math.floor(Math.random() * this._subEmitters.length);
  36. this._subEmitters[templateIndex].forEach((subEmitter) => {
  37. if (subEmitter.type === SubEmitterType.END) {
  38. const subSystem = subEmitter.clone();
  39. particle._inheritParticleInfoToSubEmitter(subSystem);
  40. subSystem.particleSystem._rootParticleSystem = this;
  41. this.activeSubSystems.push(subSystem.particleSystem);
  42. subSystem.particleSystem.start();
  43. }
  44. });
  45. };
  46. }
  47. /**
  48. * Creates a Point Emitter for the particle system (emits directly from the emitter position)
  49. * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
  50. * @param direction2 Particles are emitted between the direction1 and direction2 from within the box
  51. * @returns the emitter
  52. */
  53. createPointEmitter(direction1, direction2) {
  54. const particleEmitter = CreatePointEmitter(direction1, direction2);
  55. this.particleEmitterType = particleEmitter;
  56. return particleEmitter;
  57. }
  58. /**
  59. * Creates a Hemisphere Emitter for the particle system (emits along the hemisphere radius)
  60. * @param radius The radius of the hemisphere to emit from
  61. * @param radiusRange The range of the hemisphere to emit from [0-1] 0 Surface Only, 1 Entire Radius
  62. * @returns the emitter
  63. */
  64. createHemisphericEmitter(radius = 1, radiusRange = 1) {
  65. const particleEmitter = CreateHemisphericEmitter(radius, radiusRange);
  66. this.particleEmitterType = particleEmitter;
  67. return particleEmitter;
  68. }
  69. /**
  70. * Creates a Sphere Emitter for the particle system (emits along the sphere radius)
  71. * @param radius The radius of the sphere to emit from
  72. * @param radiusRange The range of the sphere to emit from [0-1] 0 Surface Only, 1 Entire Radius
  73. * @returns the emitter
  74. */
  75. createSphereEmitter(radius = 1, radiusRange = 1) {
  76. const particleEmitter = CreateSphereEmitter(radius, radiusRange);
  77. this.particleEmitterType = particleEmitter;
  78. return particleEmitter;
  79. }
  80. /**
  81. * Creates a Directed Sphere Emitter for the particle system (emits between direction1 and direction2)
  82. * @param radius The radius of the sphere to emit from
  83. * @param direction1 Particles are emitted between the direction1 and direction2 from within the sphere
  84. * @param direction2 Particles are emitted between the direction1 and direction2 from within the sphere
  85. * @returns the emitter
  86. */
  87. createDirectedSphereEmitter(radius = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)) {
  88. const particleEmitter = CreateDirectedSphereEmitter(radius, direction1, direction2);
  89. this.particleEmitterType = particleEmitter;
  90. return particleEmitter;
  91. }
  92. /**
  93. * Creates a Cylinder Emitter for the particle system (emits from the cylinder to the particle position)
  94. * @param radius The radius of the emission cylinder
  95. * @param height The height of the emission cylinder
  96. * @param radiusRange The range of emission [0-1] 0 Surface only, 1 Entire Radius
  97. * @param directionRandomizer How much to randomize the particle direction [0-1]
  98. * @returns the emitter
  99. */
  100. createCylinderEmitter(radius = 1, height = 1, radiusRange = 1, directionRandomizer = 0) {
  101. const particleEmitter = CreateCylinderEmitter(radius, height, radiusRange, directionRandomizer);
  102. this.particleEmitterType = particleEmitter;
  103. return particleEmitter;
  104. }
  105. /**
  106. * Creates a Directed Cylinder Emitter for the particle system (emits between direction1 and direction2)
  107. * @param radius The radius of the cylinder to emit from
  108. * @param height The height of the emission cylinder
  109. * @param radiusRange the range of the emission cylinder [0-1] 0 Surface only, 1 Entire Radius (1 by default)
  110. * @param direction1 Particles are emitted between the direction1 and direction2 from within the cylinder
  111. * @param direction2 Particles are emitted between the direction1 and direction2 from within the cylinder
  112. * @returns the emitter
  113. */
  114. createDirectedCylinderEmitter(radius = 1, height = 1, radiusRange = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)) {
  115. const particleEmitter = CreateDirectedCylinderEmitter(radius, height, radiusRange, direction1, direction2);
  116. this.particleEmitterType = particleEmitter;
  117. return particleEmitter;
  118. }
  119. /**
  120. * Creates a Cone Emitter for the particle system (emits from the cone to the particle position)
  121. * @param radius The radius of the cone to emit from
  122. * @param angle The base angle of the cone
  123. * @returns the emitter
  124. */
  125. createConeEmitter(radius = 1, angle = Math.PI / 4) {
  126. const particleEmitter = CreateConeEmitter(radius, angle);
  127. this.particleEmitterType = particleEmitter;
  128. return particleEmitter;
  129. }
  130. /**
  131. * Creates a Box Emitter for the particle system. (emits between direction1 and direction2 from withing the box defined by minEmitBox and maxEmitBox)
  132. * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
  133. * @param direction2 Particles are emitted between the direction1 and direction2 from within the box
  134. * @param minEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
  135. * @param maxEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
  136. * @returns the emitter
  137. */
  138. createBoxEmitter(direction1, direction2, minEmitBox, maxEmitBox) {
  139. const particleEmitter = new BoxParticleEmitter();
  140. this.particleEmitterType = particleEmitter;
  141. this.direction1 = direction1;
  142. this.direction2 = direction2;
  143. this.minEmitBox = minEmitBox;
  144. this.maxEmitBox = maxEmitBox;
  145. return particleEmitter;
  146. }
  147. _prepareSubEmitterInternalArray() {
  148. this._subEmitters = new Array();
  149. if (this.subEmitters) {
  150. this.subEmitters.forEach((subEmitter) => {
  151. if (subEmitter instanceof ParticleSystem) {
  152. this._subEmitters.push([new SubEmitter(subEmitter)]);
  153. }
  154. else if (subEmitter instanceof SubEmitter) {
  155. this._subEmitters.push([subEmitter]);
  156. }
  157. else if (subEmitter instanceof Array) {
  158. this._subEmitters.push(subEmitter);
  159. }
  160. });
  161. }
  162. }
  163. _stopSubEmitters() {
  164. if (!this.activeSubSystems) {
  165. return;
  166. }
  167. this.activeSubSystems.forEach((subSystem) => {
  168. subSystem.stop(true);
  169. });
  170. this.activeSubSystems = [];
  171. }
  172. _removeFromRoot() {
  173. if (!this._rootParticleSystem) {
  174. return;
  175. }
  176. const index = this._rootParticleSystem.activeSubSystems.indexOf(this);
  177. if (index !== -1) {
  178. this._rootParticleSystem.activeSubSystems.splice(index, 1);
  179. }
  180. this._rootParticleSystem = null;
  181. }
  182. _preStart() {
  183. // Convert the subEmitters field to the constant type field _subEmitters
  184. this._prepareSubEmitterInternalArray();
  185. if (this._subEmitters && this._subEmitters.length != 0) {
  186. this.activeSubSystems = [];
  187. }
  188. }
  189. _postStop(stopSubEmitters) {
  190. if (stopSubEmitters) {
  191. this._stopSubEmitters();
  192. }
  193. }
  194. _prepareParticle(particle) {
  195. // Attach emitters
  196. if (this._subEmitters && this._subEmitters.length > 0) {
  197. const subEmitters = this._subEmitters[Math.floor(Math.random() * this._subEmitters.length)];
  198. particle._attachedSubEmitters = [];
  199. subEmitters.forEach((subEmitter) => {
  200. if (subEmitter.type === SubEmitterType.ATTACHED) {
  201. const newEmitter = subEmitter.clone();
  202. particle._attachedSubEmitters.push(newEmitter);
  203. newEmitter.particleSystem.start();
  204. }
  205. });
  206. }
  207. }
  208. /** @internal */
  209. _onDispose(disposeAttachedSubEmitters = false, disposeEndSubEmitters = false) {
  210. this._removeFromRoot();
  211. if (this.subEmitters && !this._subEmitters) {
  212. this._prepareSubEmitterInternalArray();
  213. }
  214. if (disposeAttachedSubEmitters) {
  215. this.particles?.forEach((particle) => {
  216. if (particle._attachedSubEmitters) {
  217. for (let i = particle._attachedSubEmitters.length - 1; i >= 0; i -= 1) {
  218. particle._attachedSubEmitters[i].dispose();
  219. }
  220. }
  221. });
  222. }
  223. if (disposeEndSubEmitters) {
  224. if (this.activeSubSystems) {
  225. for (let i = this.activeSubSystems.length - 1; i >= 0; i -= 1) {
  226. this.activeSubSystems[i].dispose();
  227. }
  228. }
  229. }
  230. if (this._subEmitters && this._subEmitters.length) {
  231. for (let index = 0; index < this._subEmitters.length; index++) {
  232. for (const subEmitter of this._subEmitters[index]) {
  233. subEmitter.dispose();
  234. }
  235. }
  236. this._subEmitters = [];
  237. this.subEmitters = [];
  238. }
  239. if (this._disposeEmitterOnDispose && this.emitter && this.emitter.dispose) {
  240. this.emitter.dispose(true);
  241. }
  242. }
  243. /**
  244. * @internal
  245. */
  246. static _Parse(parsedParticleSystem, particleSystem, sceneOrEngine, rootUrl) {
  247. let scene;
  248. if (sceneOrEngine instanceof AbstractEngine) {
  249. scene = null;
  250. }
  251. else {
  252. scene = sceneOrEngine;
  253. }
  254. const internalClass = GetClass("BABYLON.Texture");
  255. if (internalClass && scene) {
  256. // Texture
  257. if (parsedParticleSystem.texture) {
  258. particleSystem.particleTexture = internalClass.Parse(parsedParticleSystem.texture, scene, rootUrl);
  259. }
  260. else if (parsedParticleSystem.textureName) {
  261. particleSystem.particleTexture = new internalClass(rootUrl + parsedParticleSystem.textureName, scene, false, parsedParticleSystem.invertY !== undefined ? parsedParticleSystem.invertY : true);
  262. particleSystem.particleTexture.name = parsedParticleSystem.textureName;
  263. }
  264. }
  265. // Emitter
  266. if (!parsedParticleSystem.emitterId && parsedParticleSystem.emitterId !== 0 && parsedParticleSystem.emitter === undefined) {
  267. particleSystem.emitter = Vector3.Zero();
  268. }
  269. else if (parsedParticleSystem.emitterId && scene) {
  270. particleSystem.emitter = scene.getLastMeshById(parsedParticleSystem.emitterId);
  271. }
  272. else {
  273. particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
  274. }
  275. particleSystem.isLocal = !!parsedParticleSystem.isLocal;
  276. // Misc.
  277. if (parsedParticleSystem.renderingGroupId !== undefined) {
  278. particleSystem.renderingGroupId = parsedParticleSystem.renderingGroupId;
  279. }
  280. if (parsedParticleSystem.isBillboardBased !== undefined) {
  281. particleSystem.isBillboardBased = parsedParticleSystem.isBillboardBased;
  282. }
  283. if (parsedParticleSystem.billboardMode !== undefined) {
  284. particleSystem.billboardMode = parsedParticleSystem.billboardMode;
  285. }
  286. if (parsedParticleSystem.useLogarithmicDepth !== undefined) {
  287. particleSystem.useLogarithmicDepth = parsedParticleSystem.useLogarithmicDepth;
  288. }
  289. // Animations
  290. if (parsedParticleSystem.animations) {
  291. for (let animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
  292. const parsedAnimation = parsedParticleSystem.animations[animationIndex];
  293. const internalClass = GetClass("BABYLON.Animation");
  294. if (internalClass) {
  295. particleSystem.animations.push(internalClass.Parse(parsedAnimation));
  296. }
  297. }
  298. particleSystem.beginAnimationOnStart = parsedParticleSystem.beginAnimationOnStart;
  299. particleSystem.beginAnimationFrom = parsedParticleSystem.beginAnimationFrom;
  300. particleSystem.beginAnimationTo = parsedParticleSystem.beginAnimationTo;
  301. particleSystem.beginAnimationLoop = parsedParticleSystem.beginAnimationLoop;
  302. }
  303. if (parsedParticleSystem.autoAnimate && scene) {
  304. scene.beginAnimation(particleSystem, parsedParticleSystem.autoAnimateFrom, parsedParticleSystem.autoAnimateTo, parsedParticleSystem.autoAnimateLoop, parsedParticleSystem.autoAnimateSpeed || 1.0);
  305. }
  306. // Particle system
  307. particleSystem.startDelay = parsedParticleSystem.startDelay | 0;
  308. particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
  309. particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
  310. particleSystem.minSize = parsedParticleSystem.minSize;
  311. particleSystem.maxSize = parsedParticleSystem.maxSize;
  312. if (parsedParticleSystem.minScaleX) {
  313. particleSystem.minScaleX = parsedParticleSystem.minScaleX;
  314. particleSystem.maxScaleX = parsedParticleSystem.maxScaleX;
  315. particleSystem.minScaleY = parsedParticleSystem.minScaleY;
  316. particleSystem.maxScaleY = parsedParticleSystem.maxScaleY;
  317. }
  318. if (parsedParticleSystem.preWarmCycles !== undefined) {
  319. particleSystem.preWarmCycles = parsedParticleSystem.preWarmCycles;
  320. particleSystem.preWarmStepOffset = parsedParticleSystem.preWarmStepOffset;
  321. }
  322. if (parsedParticleSystem.minInitialRotation !== undefined) {
  323. particleSystem.minInitialRotation = parsedParticleSystem.minInitialRotation;
  324. particleSystem.maxInitialRotation = parsedParticleSystem.maxInitialRotation;
  325. }
  326. particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
  327. particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
  328. particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
  329. particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
  330. particleSystem.emitRate = parsedParticleSystem.emitRate;
  331. particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
  332. if (parsedParticleSystem.noiseStrength) {
  333. particleSystem.noiseStrength = Vector3.FromArray(parsedParticleSystem.noiseStrength);
  334. }
  335. particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
  336. particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
  337. particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
  338. particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
  339. particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
  340. particleSystem.blendMode = parsedParticleSystem.blendMode;
  341. if (parsedParticleSystem.colorGradients) {
  342. for (const colorGradient of parsedParticleSystem.colorGradients) {
  343. particleSystem.addColorGradient(colorGradient.gradient, Color4.FromArray(colorGradient.color1), colorGradient.color2 ? Color4.FromArray(colorGradient.color2) : undefined);
  344. }
  345. }
  346. if (parsedParticleSystem.rampGradients) {
  347. for (const rampGradient of parsedParticleSystem.rampGradients) {
  348. particleSystem.addRampGradient(rampGradient.gradient, Color3.FromArray(rampGradient.color));
  349. }
  350. particleSystem.useRampGradients = parsedParticleSystem.useRampGradients;
  351. }
  352. if (parsedParticleSystem.colorRemapGradients) {
  353. for (const colorRemapGradient of parsedParticleSystem.colorRemapGradients) {
  354. particleSystem.addColorRemapGradient(colorRemapGradient.gradient, colorRemapGradient.factor1 !== undefined ? colorRemapGradient.factor1 : colorRemapGradient.factor, colorRemapGradient.factor2);
  355. }
  356. }
  357. if (parsedParticleSystem.alphaRemapGradients) {
  358. for (const alphaRemapGradient of parsedParticleSystem.alphaRemapGradients) {
  359. particleSystem.addAlphaRemapGradient(alphaRemapGradient.gradient, alphaRemapGradient.factor1 !== undefined ? alphaRemapGradient.factor1 : alphaRemapGradient.factor, alphaRemapGradient.factor2);
  360. }
  361. }
  362. if (parsedParticleSystem.sizeGradients) {
  363. for (const sizeGradient of parsedParticleSystem.sizeGradients) {
  364. particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ? sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
  365. }
  366. }
  367. if (parsedParticleSystem.angularSpeedGradients) {
  368. for (const angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
  369. particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ? angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
  370. }
  371. }
  372. if (parsedParticleSystem.velocityGradients) {
  373. for (const velocityGradient of parsedParticleSystem.velocityGradients) {
  374. particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ? velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
  375. }
  376. }
  377. if (parsedParticleSystem.dragGradients) {
  378. for (const dragGradient of parsedParticleSystem.dragGradients) {
  379. particleSystem.addDragGradient(dragGradient.gradient, dragGradient.factor1 !== undefined ? dragGradient.factor1 : dragGradient.factor, dragGradient.factor2);
  380. }
  381. }
  382. if (parsedParticleSystem.emitRateGradients) {
  383. for (const emitRateGradient of parsedParticleSystem.emitRateGradients) {
  384. particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ? emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
  385. }
  386. }
  387. if (parsedParticleSystem.startSizeGradients) {
  388. for (const startSizeGradient of parsedParticleSystem.startSizeGradients) {
  389. particleSystem.addStartSizeGradient(startSizeGradient.gradient, startSizeGradient.factor1 !== undefined ? startSizeGradient.factor1 : startSizeGradient.factor, startSizeGradient.factor2);
  390. }
  391. }
  392. if (parsedParticleSystem.lifeTimeGradients) {
  393. for (const lifeTimeGradient of parsedParticleSystem.lifeTimeGradients) {
  394. particleSystem.addLifeTimeGradient(lifeTimeGradient.gradient, lifeTimeGradient.factor1 !== undefined ? lifeTimeGradient.factor1 : lifeTimeGradient.factor, lifeTimeGradient.factor2);
  395. }
  396. }
  397. if (parsedParticleSystem.limitVelocityGradients) {
  398. for (const limitVelocityGradient of parsedParticleSystem.limitVelocityGradients) {
  399. particleSystem.addLimitVelocityGradient(limitVelocityGradient.gradient, limitVelocityGradient.factor1 !== undefined ? limitVelocityGradient.factor1 : limitVelocityGradient.factor, limitVelocityGradient.factor2);
  400. }
  401. particleSystem.limitVelocityDamping = parsedParticleSystem.limitVelocityDamping;
  402. }
  403. if (parsedParticleSystem.noiseTexture && scene) {
  404. const internalClass = GetClass("BABYLON.ProceduralTexture");
  405. particleSystem.noiseTexture = internalClass.Parse(parsedParticleSystem.noiseTexture, scene, rootUrl);
  406. }
  407. // Emitter
  408. let emitterType;
  409. if (parsedParticleSystem.particleEmitterType) {
  410. switch (parsedParticleSystem.particleEmitterType.type) {
  411. case "SphereParticleEmitter":
  412. emitterType = new SphereParticleEmitter();
  413. break;
  414. case "SphereDirectedParticleEmitter":
  415. emitterType = new SphereDirectedParticleEmitter();
  416. break;
  417. case "ConeEmitter":
  418. case "ConeParticleEmitter":
  419. emitterType = new ConeParticleEmitter();
  420. break;
  421. case "CylinderParticleEmitter":
  422. emitterType = new CylinderParticleEmitter();
  423. break;
  424. case "CylinderDirectedParticleEmitter":
  425. emitterType = new CylinderDirectedParticleEmitter();
  426. break;
  427. case "HemisphericParticleEmitter":
  428. emitterType = new HemisphericParticleEmitter();
  429. break;
  430. case "PointParticleEmitter":
  431. emitterType = new PointParticleEmitter();
  432. break;
  433. case "MeshParticleEmitter":
  434. emitterType = new MeshParticleEmitter();
  435. break;
  436. case "CustomParticleEmitter":
  437. emitterType = new CustomParticleEmitter();
  438. break;
  439. case "BoxEmitter":
  440. case "BoxParticleEmitter":
  441. default:
  442. emitterType = new BoxParticleEmitter();
  443. break;
  444. }
  445. emitterType.parse(parsedParticleSystem.particleEmitterType, scene);
  446. }
  447. else {
  448. emitterType = new BoxParticleEmitter();
  449. emitterType.parse(parsedParticleSystem, scene);
  450. }
  451. particleSystem.particleEmitterType = emitterType;
  452. // Animation sheet
  453. particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
  454. particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
  455. particleSystem.spriteCellLoop = parsedParticleSystem.spriteCellLoop ?? true;
  456. particleSystem.spriteCellWidth = parsedParticleSystem.spriteCellWidth;
  457. particleSystem.spriteCellHeight = parsedParticleSystem.spriteCellHeight;
  458. particleSystem.spriteCellChangeSpeed = parsedParticleSystem.spriteCellChangeSpeed;
  459. particleSystem.spriteRandomStartCell = parsedParticleSystem.spriteRandomStartCell;
  460. particleSystem.disposeOnStop = parsedParticleSystem.disposeOnStop ?? false;
  461. particleSystem.manualEmitCount = parsedParticleSystem.manualEmitCount ?? -1;
  462. }
  463. /**
  464. * Parses a JSON object to create a particle system.
  465. * @param parsedParticleSystem The JSON object to parse
  466. * @param sceneOrEngine The scene or the engine to create the particle system in
  467. * @param rootUrl The root url to use to load external dependencies like texture
  468. * @param doNotStart Ignore the preventAutoStart attribute and does not start
  469. * @param capacity defines the system capacity (if null or undefined the sotred capacity will be used)
  470. * @returns the Parsed particle system
  471. */
  472. static Parse(parsedParticleSystem, sceneOrEngine, rootUrl, doNotStart = false, capacity) {
  473. const name = parsedParticleSystem.name;
  474. let custom = null;
  475. let program = null;
  476. let engine;
  477. let scene;
  478. if (sceneOrEngine instanceof AbstractEngine) {
  479. engine = sceneOrEngine;
  480. }
  481. else {
  482. scene = sceneOrEngine;
  483. engine = scene.getEngine();
  484. }
  485. if (parsedParticleSystem.customShader && engine.createEffectForParticles) {
  486. program = parsedParticleSystem.customShader;
  487. const defines = program.shaderOptions.defines.length > 0 ? program.shaderOptions.defines.join("\n") : "";
  488. custom = engine.createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
  489. }
  490. const particleSystem = new ParticleSystem(name, capacity || parsedParticleSystem.capacity, sceneOrEngine, custom, parsedParticleSystem.isAnimationSheetEnabled);
  491. particleSystem.customShader = program;
  492. particleSystem._rootUrl = rootUrl;
  493. if (parsedParticleSystem.id) {
  494. particleSystem.id = parsedParticleSystem.id;
  495. }
  496. // SubEmitters
  497. if (parsedParticleSystem.subEmitters) {
  498. particleSystem.subEmitters = [];
  499. for (const cell of parsedParticleSystem.subEmitters) {
  500. const cellArray = [];
  501. for (const sub of cell) {
  502. cellArray.push(SubEmitter.Parse(sub, sceneOrEngine, rootUrl));
  503. }
  504. particleSystem.subEmitters.push(cellArray);
  505. }
  506. }
  507. ParticleSystem._Parse(parsedParticleSystem, particleSystem, sceneOrEngine, rootUrl);
  508. if (parsedParticleSystem.textureMask) {
  509. particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
  510. }
  511. if (parsedParticleSystem.worldOffset) {
  512. particleSystem.worldOffset = Vector3.FromArray(parsedParticleSystem.worldOffset);
  513. }
  514. // Auto start
  515. if (parsedParticleSystem.preventAutoStart) {
  516. particleSystem.preventAutoStart = parsedParticleSystem.preventAutoStart;
  517. }
  518. if (!doNotStart && !particleSystem.preventAutoStart) {
  519. particleSystem.start();
  520. }
  521. return particleSystem;
  522. }
  523. /**
  524. * Serializes the particle system to a JSON object
  525. * @param serializeTexture defines if the texture must be serialized as well
  526. * @returns the JSON object
  527. */
  528. serialize(serializeTexture = false) {
  529. const serializationObject = {};
  530. ParticleSystem._Serialize(serializationObject, this, serializeTexture);
  531. serializationObject.textureMask = this.textureMask.asArray();
  532. serializationObject.customShader = this.customShader;
  533. serializationObject.preventAutoStart = this.preventAutoStart;
  534. serializationObject.worldOffset = this.worldOffset.asArray();
  535. // SubEmitters
  536. if (this.subEmitters) {
  537. serializationObject.subEmitters = [];
  538. if (!this._subEmitters) {
  539. this._prepareSubEmitterInternalArray();
  540. }
  541. for (const subs of this._subEmitters) {
  542. const cell = [];
  543. for (const sub of subs) {
  544. cell.push(sub.serialize(serializeTexture));
  545. }
  546. serializationObject.subEmitters.push(cell);
  547. }
  548. }
  549. return serializationObject;
  550. }
  551. /**
  552. * @internal
  553. */
  554. static _Serialize(serializationObject, particleSystem, serializeTexture) {
  555. serializationObject.name = particleSystem.name;
  556. serializationObject.id = particleSystem.id;
  557. serializationObject.capacity = particleSystem.getCapacity();
  558. serializationObject.disposeOnStop = particleSystem.disposeOnStop;
  559. serializationObject.manualEmitCount = particleSystem.manualEmitCount;
  560. // Emitter
  561. if (particleSystem.emitter.position) {
  562. const emitterMesh = particleSystem.emitter;
  563. serializationObject.emitterId = emitterMesh.id;
  564. }
  565. else {
  566. const emitterPosition = particleSystem.emitter;
  567. serializationObject.emitter = emitterPosition.asArray();
  568. }
  569. // Emitter
  570. if (particleSystem.particleEmitterType) {
  571. serializationObject.particleEmitterType = particleSystem.particleEmitterType.serialize();
  572. }
  573. if (particleSystem.particleTexture) {
  574. if (serializeTexture) {
  575. serializationObject.texture = particleSystem.particleTexture.serialize();
  576. }
  577. else {
  578. serializationObject.textureName = particleSystem.particleTexture.name;
  579. serializationObject.invertY = !!particleSystem.particleTexture._invertY;
  580. }
  581. }
  582. serializationObject.isLocal = particleSystem.isLocal;
  583. // Animations
  584. SerializationHelper.AppendSerializedAnimations(particleSystem, serializationObject);
  585. serializationObject.beginAnimationOnStart = particleSystem.beginAnimationOnStart;
  586. serializationObject.beginAnimationFrom = particleSystem.beginAnimationFrom;
  587. serializationObject.beginAnimationTo = particleSystem.beginAnimationTo;
  588. serializationObject.beginAnimationLoop = particleSystem.beginAnimationLoop;
  589. // Particle system
  590. serializationObject.startDelay = particleSystem.startDelay;
  591. serializationObject.renderingGroupId = particleSystem.renderingGroupId;
  592. serializationObject.isBillboardBased = particleSystem.isBillboardBased;
  593. serializationObject.billboardMode = particleSystem.billboardMode;
  594. serializationObject.minAngularSpeed = particleSystem.minAngularSpeed;
  595. serializationObject.maxAngularSpeed = particleSystem.maxAngularSpeed;
  596. serializationObject.minSize = particleSystem.minSize;
  597. serializationObject.maxSize = particleSystem.maxSize;
  598. serializationObject.minScaleX = particleSystem.minScaleX;
  599. serializationObject.maxScaleX = particleSystem.maxScaleX;
  600. serializationObject.minScaleY = particleSystem.minScaleY;
  601. serializationObject.maxScaleY = particleSystem.maxScaleY;
  602. serializationObject.minEmitPower = particleSystem.minEmitPower;
  603. serializationObject.maxEmitPower = particleSystem.maxEmitPower;
  604. serializationObject.minLifeTime = particleSystem.minLifeTime;
  605. serializationObject.maxLifeTime = particleSystem.maxLifeTime;
  606. serializationObject.emitRate = particleSystem.emitRate;
  607. serializationObject.gravity = particleSystem.gravity.asArray();
  608. serializationObject.noiseStrength = particleSystem.noiseStrength.asArray();
  609. serializationObject.color1 = particleSystem.color1.asArray();
  610. serializationObject.color2 = particleSystem.color2.asArray();
  611. serializationObject.colorDead = particleSystem.colorDead.asArray();
  612. serializationObject.updateSpeed = particleSystem.updateSpeed;
  613. serializationObject.targetStopDuration = particleSystem.targetStopDuration;
  614. serializationObject.blendMode = particleSystem.blendMode;
  615. serializationObject.preWarmCycles = particleSystem.preWarmCycles;
  616. serializationObject.preWarmStepOffset = particleSystem.preWarmStepOffset;
  617. serializationObject.minInitialRotation = particleSystem.minInitialRotation;
  618. serializationObject.maxInitialRotation = particleSystem.maxInitialRotation;
  619. serializationObject.startSpriteCellID = particleSystem.startSpriteCellID;
  620. serializationObject.spriteCellLoop = particleSystem.spriteCellLoop;
  621. serializationObject.endSpriteCellID = particleSystem.endSpriteCellID;
  622. serializationObject.spriteCellChangeSpeed = particleSystem.spriteCellChangeSpeed;
  623. serializationObject.spriteCellWidth = particleSystem.spriteCellWidth;
  624. serializationObject.spriteCellHeight = particleSystem.spriteCellHeight;
  625. serializationObject.spriteRandomStartCell = particleSystem.spriteRandomStartCell;
  626. serializationObject.isAnimationSheetEnabled = particleSystem.isAnimationSheetEnabled;
  627. serializationObject.useLogarithmicDepth = particleSystem.useLogarithmicDepth;
  628. const colorGradients = particleSystem.getColorGradients();
  629. if (colorGradients) {
  630. serializationObject.colorGradients = [];
  631. for (const colorGradient of colorGradients) {
  632. const serializedGradient = {
  633. gradient: colorGradient.gradient,
  634. color1: colorGradient.color1.asArray(),
  635. };
  636. if (colorGradient.color2) {
  637. serializedGradient.color2 = colorGradient.color2.asArray();
  638. }
  639. else {
  640. serializedGradient.color2 = colorGradient.color1.asArray();
  641. }
  642. serializationObject.colorGradients.push(serializedGradient);
  643. }
  644. }
  645. const rampGradients = particleSystem.getRampGradients();
  646. if (rampGradients) {
  647. serializationObject.rampGradients = [];
  648. for (const rampGradient of rampGradients) {
  649. const serializedGradient = {
  650. gradient: rampGradient.gradient,
  651. color: rampGradient.color.asArray(),
  652. };
  653. serializationObject.rampGradients.push(serializedGradient);
  654. }
  655. serializationObject.useRampGradients = particleSystem.useRampGradients;
  656. }
  657. const colorRemapGradients = particleSystem.getColorRemapGradients();
  658. if (colorRemapGradients) {
  659. serializationObject.colorRemapGradients = [];
  660. for (const colorRemapGradient of colorRemapGradients) {
  661. const serializedGradient = {
  662. gradient: colorRemapGradient.gradient,
  663. factor1: colorRemapGradient.factor1,
  664. };
  665. if (colorRemapGradient.factor2 !== undefined) {
  666. serializedGradient.factor2 = colorRemapGradient.factor2;
  667. }
  668. else {
  669. serializedGradient.factor2 = colorRemapGradient.factor1;
  670. }
  671. serializationObject.colorRemapGradients.push(serializedGradient);
  672. }
  673. }
  674. const alphaRemapGradients = particleSystem.getAlphaRemapGradients();
  675. if (alphaRemapGradients) {
  676. serializationObject.alphaRemapGradients = [];
  677. for (const alphaRemapGradient of alphaRemapGradients) {
  678. const serializedGradient = {
  679. gradient: alphaRemapGradient.gradient,
  680. factor1: alphaRemapGradient.factor1,
  681. };
  682. if (alphaRemapGradient.factor2 !== undefined) {
  683. serializedGradient.factor2 = alphaRemapGradient.factor2;
  684. }
  685. else {
  686. serializedGradient.factor2 = alphaRemapGradient.factor1;
  687. }
  688. serializationObject.alphaRemapGradients.push(serializedGradient);
  689. }
  690. }
  691. const sizeGradients = particleSystem.getSizeGradients();
  692. if (sizeGradients) {
  693. serializationObject.sizeGradients = [];
  694. for (const sizeGradient of sizeGradients) {
  695. const serializedGradient = {
  696. gradient: sizeGradient.gradient,
  697. factor1: sizeGradient.factor1,
  698. };
  699. if (sizeGradient.factor2 !== undefined) {
  700. serializedGradient.factor2 = sizeGradient.factor2;
  701. }
  702. else {
  703. serializedGradient.factor2 = sizeGradient.factor1;
  704. }
  705. serializationObject.sizeGradients.push(serializedGradient);
  706. }
  707. }
  708. const angularSpeedGradients = particleSystem.getAngularSpeedGradients();
  709. if (angularSpeedGradients) {
  710. serializationObject.angularSpeedGradients = [];
  711. for (const angularSpeedGradient of angularSpeedGradients) {
  712. const serializedGradient = {
  713. gradient: angularSpeedGradient.gradient,
  714. factor1: angularSpeedGradient.factor1,
  715. };
  716. if (angularSpeedGradient.factor2 !== undefined) {
  717. serializedGradient.factor2 = angularSpeedGradient.factor2;
  718. }
  719. else {
  720. serializedGradient.factor2 = angularSpeedGradient.factor1;
  721. }
  722. serializationObject.angularSpeedGradients.push(serializedGradient);
  723. }
  724. }
  725. const velocityGradients = particleSystem.getVelocityGradients();
  726. if (velocityGradients) {
  727. serializationObject.velocityGradients = [];
  728. for (const velocityGradient of velocityGradients) {
  729. const serializedGradient = {
  730. gradient: velocityGradient.gradient,
  731. factor1: velocityGradient.factor1,
  732. };
  733. if (velocityGradient.factor2 !== undefined) {
  734. serializedGradient.factor2 = velocityGradient.factor2;
  735. }
  736. else {
  737. serializedGradient.factor2 = velocityGradient.factor1;
  738. }
  739. serializationObject.velocityGradients.push(serializedGradient);
  740. }
  741. }
  742. const dragGradients = particleSystem.getDragGradients();
  743. if (dragGradients) {
  744. serializationObject.dragGradients = [];
  745. for (const dragGradient of dragGradients) {
  746. const serializedGradient = {
  747. gradient: dragGradient.gradient,
  748. factor1: dragGradient.factor1,
  749. };
  750. if (dragGradient.factor2 !== undefined) {
  751. serializedGradient.factor2 = dragGradient.factor2;
  752. }
  753. else {
  754. serializedGradient.factor2 = dragGradient.factor1;
  755. }
  756. serializationObject.dragGradients.push(serializedGradient);
  757. }
  758. }
  759. const emitRateGradients = particleSystem.getEmitRateGradients();
  760. if (emitRateGradients) {
  761. serializationObject.emitRateGradients = [];
  762. for (const emitRateGradient of emitRateGradients) {
  763. const serializedGradient = {
  764. gradient: emitRateGradient.gradient,
  765. factor1: emitRateGradient.factor1,
  766. };
  767. if (emitRateGradient.factor2 !== undefined) {
  768. serializedGradient.factor2 = emitRateGradient.factor2;
  769. }
  770. else {
  771. serializedGradient.factor2 = emitRateGradient.factor1;
  772. }
  773. serializationObject.emitRateGradients.push(serializedGradient);
  774. }
  775. }
  776. const startSizeGradients = particleSystem.getStartSizeGradients();
  777. if (startSizeGradients) {
  778. serializationObject.startSizeGradients = [];
  779. for (const startSizeGradient of startSizeGradients) {
  780. const serializedGradient = {
  781. gradient: startSizeGradient.gradient,
  782. factor1: startSizeGradient.factor1,
  783. };
  784. if (startSizeGradient.factor2 !== undefined) {
  785. serializedGradient.factor2 = startSizeGradient.factor2;
  786. }
  787. else {
  788. serializedGradient.factor2 = startSizeGradient.factor1;
  789. }
  790. serializationObject.startSizeGradients.push(serializedGradient);
  791. }
  792. }
  793. const lifeTimeGradients = particleSystem.getLifeTimeGradients();
  794. if (lifeTimeGradients) {
  795. serializationObject.lifeTimeGradients = [];
  796. for (const lifeTimeGradient of lifeTimeGradients) {
  797. const serializedGradient = {
  798. gradient: lifeTimeGradient.gradient,
  799. factor1: lifeTimeGradient.factor1,
  800. };
  801. if (lifeTimeGradient.factor2 !== undefined) {
  802. serializedGradient.factor2 = lifeTimeGradient.factor2;
  803. }
  804. else {
  805. serializedGradient.factor2 = lifeTimeGradient.factor1;
  806. }
  807. serializationObject.lifeTimeGradients.push(serializedGradient);
  808. }
  809. }
  810. const limitVelocityGradients = particleSystem.getLimitVelocityGradients();
  811. if (limitVelocityGradients) {
  812. serializationObject.limitVelocityGradients = [];
  813. for (const limitVelocityGradient of limitVelocityGradients) {
  814. const serializedGradient = {
  815. gradient: limitVelocityGradient.gradient,
  816. factor1: limitVelocityGradient.factor1,
  817. };
  818. if (limitVelocityGradient.factor2 !== undefined) {
  819. serializedGradient.factor2 = limitVelocityGradient.factor2;
  820. }
  821. else {
  822. serializedGradient.factor2 = limitVelocityGradient.factor1;
  823. }
  824. serializationObject.limitVelocityGradients.push(serializedGradient);
  825. }
  826. serializationObject.limitVelocityDamping = particleSystem.limitVelocityDamping;
  827. }
  828. if (particleSystem.noiseTexture) {
  829. serializationObject.noiseTexture = particleSystem.noiseTexture.serialize();
  830. }
  831. }
  832. // Clone
  833. /**
  834. * Clones the particle system.
  835. * @param name The name of the cloned object
  836. * @param newEmitter The new emitter to use
  837. * @param cloneTexture Also clone the textures if true
  838. * @returns the cloned particle system
  839. */
  840. clone(name, newEmitter, cloneTexture = false) {
  841. const custom = { ...this._customWrappers };
  842. let program = null;
  843. const engine = this._engine;
  844. if (engine.createEffectForParticles) {
  845. if (this.customShader != null) {
  846. program = this.customShader;
  847. const defines = program.shaderOptions.defines.length > 0 ? program.shaderOptions.defines.join("\n") : "";
  848. const effect = engine.createEffectForParticles(program.shaderPath.fragmentElement, program.shaderOptions.uniforms, program.shaderOptions.samplers, defines);
  849. if (!custom[0]) {
  850. this.setCustomEffect(effect, 0);
  851. }
  852. else {
  853. custom[0].effect = effect;
  854. }
  855. }
  856. }
  857. const serialization = this.serialize(cloneTexture);
  858. const result = ParticleSystem.Parse(serialization, this._scene || this._engine, this._rootUrl);
  859. result.name = name;
  860. result.customShader = program;
  861. result._customWrappers = custom;
  862. if (newEmitter === undefined) {
  863. newEmitter = this.emitter;
  864. }
  865. if (this.noiseTexture) {
  866. result.noiseTexture = this.noiseTexture.clone();
  867. }
  868. result.emitter = newEmitter;
  869. if (!this.preventAutoStart) {
  870. result.start();
  871. }
  872. return result;
  873. }
  874. }
  875. /**
  876. * Billboard mode will only apply to Y axis
  877. */
  878. ParticleSystem.BILLBOARDMODE_Y = 2;
  879. /**
  880. * Billboard mode will apply to all axes
  881. */
  882. ParticleSystem.BILLBOARDMODE_ALL = 7;
  883. /**
  884. * Special billboard mode where the particle will be biilboard to the camera but rotated to align with direction
  885. */
  886. ParticleSystem.BILLBOARDMODE_STRETCHED = 8;
  887. /**
  888. * Special billboard mode where the particle will be billboard to the camera but only around the axis of the direction of particle emission
  889. */
  890. ParticleSystem.BILLBOARDMODE_STRETCHED_LOCAL = 9;
  891. SubEmitter._ParseParticleSystem = ParticleSystem.Parse;
  892. //# sourceMappingURL=particleSystem.js.map