babylon.gpuParticleSystem.ts 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. module BABYLON {
  2. /**
  3. * This represents a GPU particle system in Babylon
  4. * This is the fastest particle system in Babylon as it uses the GPU to update the individual particle data
  5. * @see https://www.babylonjs-playground.com/#PU4WYI#4
  6. */
  7. export class GPUParticleSystem implements IDisposable, IParticleSystem, IAnimatable {
  8. /**
  9. * The id of the Particle system.
  10. */
  11. public id: string;
  12. /**
  13. * The friendly name of the Particle system.
  14. */
  15. public name: string;
  16. /**
  17. * The emitter represents the Mesh or position we are attaching the particle system to.
  18. */
  19. public emitter: Nullable<AbstractMesh | Vector3> = null;
  20. /**
  21. * The rendering group used by the Particle system to chose when to render.
  22. */
  23. public renderingGroupId = 0;
  24. /**
  25. * The layer mask we are rendering the particles through.
  26. */
  27. public layerMask: number = 0x0FFFFFFF;
  28. private _capacity: number;
  29. private _activeCount: number;
  30. private _currentActiveCount: number;
  31. private _renderEffect: Effect;
  32. private _updateEffect: Effect;
  33. private _buffer0: Buffer;
  34. private _buffer1: Buffer;
  35. private _spriteBuffer: Buffer;
  36. private _updateVAO: Array<WebGLVertexArrayObject>;
  37. private _renderVAO: Array<WebGLVertexArrayObject>;
  38. private _targetIndex = 0;
  39. private _sourceBuffer: Buffer;
  40. private _targetBuffer: Buffer;
  41. private _scene: Scene;
  42. private _engine: Engine;
  43. private _currentRenderId = -1;
  44. private _started = false;
  45. private _stopped = false;
  46. private _timeDelta = 0;
  47. private _randomTexture: RawTexture;
  48. private readonly _attributesStrideSize = 16;
  49. private _updateEffectOptions: EffectCreationOptions;
  50. private _randomTextureSize: number;
  51. private _actualFrame = 0;
  52. /**
  53. * List of animations used by the particle system.
  54. */
  55. public animations: Animation[] = [];
  56. /**
  57. * Gets a boolean indicating if the GPU particles can be rendered on current browser
  58. */
  59. public static get IsSupported(): boolean {
  60. if (!Engine.LastCreatedEngine) {
  61. return false;
  62. }
  63. return Engine.LastCreatedEngine.webGLVersion > 1;
  64. }
  65. /**
  66. * An event triggered when the system is disposed.
  67. */
  68. public onDisposeObservable = new Observable<GPUParticleSystem>();
  69. /**
  70. * The overall motion speed (0.01 is default update speed, faster updates = faster animation)
  71. */
  72. public updateSpeed = 0.01;
  73. /**
  74. * The amount of time the particle system is running (depends of the overall update speed).
  75. */
  76. public targetStopDuration = 0;
  77. /**
  78. * The texture used to render each particle. (this can be a spritesheet)
  79. */
  80. public particleTexture: Nullable<Texture>;
  81. /**
  82. * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE or ParticleSystem.BLENDMODE_STANDARD.
  83. */
  84. public blendMode = ParticleSystem.BLENDMODE_ONEONE;
  85. /**
  86. * Minimum life time of emitting particles.
  87. */
  88. public minLifeTime = 1;
  89. /**
  90. * Maximum life time of emitting particles.
  91. */
  92. public maxLifeTime = 1;
  93. /**
  94. * Minimum Size of emitting particles.
  95. */
  96. public minSize = 1;
  97. /**
  98. * Maximum Size of emitting particles.
  99. */
  100. public maxSize = 1;
  101. /**
  102. * Random color of each particle after it has been emitted, between color1 and color2 vectors.
  103. */
  104. public color1 = new Color4(1.0, 1.0, 1.0, 1.0);
  105. /**
  106. * Random color of each particle after it has been emitted, between color1 and color2 vectors.
  107. */
  108. public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
  109. /**
  110. * Color the particle will have at the end of its lifetime.
  111. */
  112. public colorDead = new Color4(0, 0, 0, 0);
  113. /**
  114. * The maximum number of particles to emit per frame until we reach the activeParticleCount value
  115. */
  116. public emitRate = 100;
  117. /**
  118. * You can use gravity if you want to give an orientation to your particles.
  119. */
  120. public gravity = Vector3.Zero();
  121. /**
  122. * Minimum power of emitting particles.
  123. */
  124. public minEmitPower = 1;
  125. /**
  126. * Maximum power of emitting particles.
  127. */
  128. public maxEmitPower = 1;
  129. /**
  130. * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
  131. */
  132. public minAngularSpeed = 0;
  133. /**
  134. * Maximum angular speed of emitting particles (Z-axis rotation for each particle).
  135. */
  136. public maxAngularSpeed = 0;
  137. /**
  138. * The particle emitter type defines the emitter used by the particle system.
  139. * It can be for example box, sphere, or cone...
  140. */
  141. public particleEmitterType: Nullable<IParticleEmitterType>;
  142. /**
  143. * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
  144. * This only works when particleEmitterTyps is a BoxParticleEmitter
  145. */
  146. public get direction1(): Vector3 {
  147. if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
  148. return (<BoxParticleEmitter>this.particleEmitterType).direction1;
  149. }
  150. return Vector3.Zero();
  151. }
  152. public set direction1(value: Vector3) {
  153. if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
  154. (<BoxParticleEmitter>this.particleEmitterType).direction1 = value;
  155. }
  156. }
  157. /**
  158. * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
  159. * This only works when particleEmitterTyps is a BoxParticleEmitter
  160. */
  161. public get direction2(): Vector3 {
  162. if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
  163. return (<BoxParticleEmitter>this.particleEmitterType).direction2;
  164. }
  165. return Vector3.Zero();
  166. }
  167. public set direction2(value: Vector3) {
  168. if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
  169. (<BoxParticleEmitter>this.particleEmitterType).direction2 = value;
  170. }
  171. }
  172. /**
  173. * Minimum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
  174. * This only works when particleEmitterTyps is a BoxParticleEmitter
  175. */
  176. public get minEmitBox(): Vector3 {
  177. if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
  178. return (<BoxParticleEmitter>this.particleEmitterType).minEmitBox;
  179. }
  180. return Vector3.Zero();
  181. }
  182. public set minEmitBox(value: Vector3) {
  183. if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
  184. (<BoxParticleEmitter>this.particleEmitterType).minEmitBox = value;
  185. }
  186. }
  187. /**
  188. * Maximum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
  189. * This only works when particleEmitterTyps is a BoxParticleEmitter
  190. */
  191. public get maxEmitBox(): Vector3 {
  192. if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
  193. return (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox;
  194. }
  195. return Vector3.Zero();
  196. }
  197. public set maxEmitBox(value: Vector3) {
  198. if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
  199. (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox = value;
  200. }
  201. }
  202. /**
  203. * Gets the maximum number of particles active at the same time.
  204. * @returns The max number of active particles.
  205. */
  206. public getCapacity(): number {
  207. return this._capacity;
  208. }
  209. /**
  210. * Forces the particle to write their depth information to the depth buffer. This can help preventing other draw calls
  211. * to override the particles.
  212. */
  213. public forceDepthWrite = false;
  214. /**
  215. * Gets or set the number of active particles
  216. */
  217. public get activeParticleCount(): number {
  218. return this._activeCount;
  219. }
  220. public set activeParticleCount(value: number) {
  221. this._activeCount = Math.min(value, this._capacity);
  222. }
  223. /**
  224. * Is this system ready to be used/rendered
  225. * @return true if the system is ready
  226. */
  227. public isReady(): boolean {
  228. if (!this._updateEffect) {
  229. this._recreateUpdateEffect();
  230. this._recreateRenderEffect();
  231. return false;
  232. }
  233. if (!this.emitter || !this._updateEffect.isReady() || !this._renderEffect.isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
  234. return false;
  235. }
  236. return true;
  237. }
  238. /**
  239. * Gets Wether the system has been started.
  240. * @returns True if it has been started, otherwise false.
  241. */
  242. public isStarted(): boolean {
  243. return this._started;
  244. }
  245. /**
  246. * Starts the particle system and begins to emit.
  247. */
  248. public start(): void {
  249. this._started = true;
  250. this._stopped = false;
  251. }
  252. /**
  253. * Stops the particle system.
  254. */
  255. public stop(): void {
  256. this._stopped = true;
  257. }
  258. /**
  259. * Remove all active particles
  260. */
  261. public reset(): void {
  262. this._releaseBuffers();
  263. this._releaseVAOs();
  264. this._currentActiveCount = 0;
  265. this._targetIndex = 0;
  266. }
  267. /**
  268. * Returns the string "GPUParticleSystem"
  269. * @returns a string containing the class name
  270. */
  271. public getClassName(): string {
  272. return "GPUParticleSystem";
  273. }
  274. /**
  275. * Instantiates a GPU particle system.
  276. * 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.
  277. * @param name The name of the particle system
  278. * @param capacity The max number of particles alive at the same time
  279. * @param scene The scene the particle system belongs to
  280. */
  281. constructor(name: string, options: Partial<{
  282. capacity: number,
  283. randomTextureSize: number
  284. }>, scene: Scene) {
  285. this.id = name;
  286. this.name = name;
  287. this._scene = scene || Engine.LastCreatedScene;
  288. this._engine = this._scene.getEngine();
  289. let fullOptions = {
  290. capacity: 50000,
  291. randomTextureSize: this._engine.getCaps().maxTextureSize,
  292. ...options
  293. };
  294. this._capacity = fullOptions.capacity;
  295. this._activeCount = fullOptions.capacity;
  296. this._currentActiveCount = 0;
  297. this._scene.particleSystems.push(this);
  298. this._updateEffectOptions = {
  299. attributes: ["position", "age", "life", "seed", "size", "color", "direction", "angle"],
  300. uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity", "emitPower",
  301. "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor",
  302. "angleRange"],
  303. uniformBuffersNames: [],
  304. samplers:["randomSampler"],
  305. defines: "",
  306. fallbacks: null,
  307. onCompiled: null,
  308. onError: null,
  309. indexParameters: null,
  310. maxSimultaneousLights: 0,
  311. transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outSize", "outColor", "outDirection", "outAngle"]
  312. };
  313. // Random data
  314. var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
  315. var d = [];
  316. for (var i = 0; i < maxTextureSize; ++i) {
  317. d.push(Math.random());
  318. d.push(Math.random());
  319. d.push(Math.random());
  320. d.push(Math.random());
  321. }
  322. this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT)
  323. this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
  324. this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
  325. this._randomTextureSize = maxTextureSize;
  326. this.particleEmitterType = new BoxParticleEmitter();
  327. }
  328. private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {
  329. let updateVertexBuffers: {[key: string]: VertexBuffer} = {};
  330. updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
  331. updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
  332. updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
  333. updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 1);
  334. updateVertexBuffers["size"] = source.createVertexBuffer("size", 6, 1);
  335. updateVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4);
  336. updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 11, 3);
  337. updateVertexBuffers["angle"] = source.createVertexBuffer("angle", 14, 2);
  338. let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
  339. this._engine.bindArrayBuffer(null);
  340. return vao;
  341. }
  342. private _createRenderVAO(source: Buffer, spriteSource: Buffer): WebGLVertexArrayObject {
  343. let renderVertexBuffers: {[key: string]: VertexBuffer} = {};
  344. renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, this._attributesStrideSize, true);
  345. renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
  346. renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
  347. renderVertexBuffers["size"] = source.createVertexBuffer("size", 6, 1, this._attributesStrideSize, true);
  348. renderVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4, this._attributesStrideSize, true);
  349. renderVertexBuffers["angle"] = source.createVertexBuffer("angle", 14, 2, this._attributesStrideSize, true);
  350. renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
  351. renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
  352. let vao = this._engine.recordVertexArrayObject(renderVertexBuffers, null, this._renderEffect);
  353. this._engine.bindArrayBuffer(null);
  354. return vao;
  355. }
  356. private _initialize(force = false): void {
  357. if (this._buffer0 && !force) {
  358. return;
  359. }
  360. let engine = this._scene.getEngine();
  361. var data = new Array<float>();
  362. for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
  363. // position
  364. data.push(0.0);
  365. data.push(0.0);
  366. data.push(0.0);
  367. // Age and life
  368. data.push(0.0); // create the particle as a dead one to create a new one at start
  369. data.push(0.0);
  370. // Seed
  371. data.push(Math.random());
  372. // Size
  373. data.push(0.0);
  374. // color
  375. data.push(0.0);
  376. data.push(0.0);
  377. data.push(0.0);
  378. data.push(0.0);
  379. // direction
  380. data.push(0.0);
  381. data.push(0.0);
  382. data.push(0.0);
  383. // angle
  384. data.push(0.0);
  385. data.push(0.0);
  386. }
  387. // Sprite data
  388. var spriteData = new Float32Array([0.5, 0.5, 1, 1,
  389. -0.5, 0.5, 0, 1,
  390. -0.5, -0.5, 0, 0,
  391. 0.5, -0.5, 1, 0]);
  392. // Buffers
  393. this._buffer0 = new Buffer(engine, data, false, this._attributesStrideSize);
  394. this._buffer1 = new Buffer(engine, data, false, this._attributesStrideSize);
  395. this._spriteBuffer = new Buffer(engine, spriteData, false, 4);
  396. // Update VAO
  397. this._updateVAO = [];
  398. this._updateVAO.push(this._createUpdateVAO(this._buffer0));
  399. this._updateVAO.push(this._createUpdateVAO(this._buffer1));
  400. // Render VAO
  401. this._renderVAO = [];
  402. this._renderVAO.push(this._createRenderVAO(this._buffer1, this._spriteBuffer));
  403. this._renderVAO.push(this._createRenderVAO(this._buffer0, this._spriteBuffer));
  404. // Links
  405. this._sourceBuffer = this._buffer0;
  406. this._targetBuffer = this._buffer1;
  407. }
  408. /** @hidden */
  409. public _recreateUpdateEffect() {
  410. let defines = this.particleEmitterType ? this.particleEmitterType.getEffectDefines() : "";
  411. if (this._updateEffect && this._updateEffectOptions.defines === defines) {
  412. return;
  413. }
  414. this._updateEffectOptions.defines = defines;
  415. this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());
  416. }
  417. /** @hidden */
  418. public _recreateRenderEffect() {
  419. let defines = "";
  420. if (this._scene.clipPlane) {
  421. defines = "\n#define CLIPPLANE";
  422. }
  423. if (this._renderEffect && this._renderEffect.defines === defines) {
  424. return;
  425. }
  426. this._renderEffect = new Effect("gpuRenderParticles",
  427. ["position", "age", "life", "size", "color", "offset", "uv", "angle"],
  428. ["view", "projection", "colorDead", "invView", "vClipPlane"],
  429. ["textureSampler"], this._scene.getEngine(), defines);
  430. }
  431. /**
  432. * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
  433. */
  434. public animate(): void {
  435. this._timeDelta = this.updateSpeed * this._scene.getAnimationRatio();
  436. this._actualFrame += this._timeDelta;
  437. if (!this._stopped) {
  438. if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration) {
  439. this.stop();
  440. }
  441. }
  442. }
  443. /**
  444. * Renders the particle system in its current state.
  445. * @returns the current number of particles
  446. */
  447. public render(): number {
  448. if (!this._started) {
  449. return 0;
  450. }
  451. this._recreateUpdateEffect();
  452. this._recreateRenderEffect();
  453. if (!this.isReady()) {
  454. return 0;
  455. }
  456. if (this._currentRenderId === this._scene.getRenderId()) {
  457. return 0;
  458. }
  459. this._currentRenderId = this._scene.getRenderId();
  460. // Get everything ready to render
  461. this. _initialize();
  462. this._currentActiveCount = Math.min(this._activeCount, this._currentActiveCount + (this.emitRate * this._timeDelta) | 0);
  463. // Enable update effect
  464. this._engine.enableEffect(this._updateEffect);
  465. this._engine.setState(false);
  466. this._updateEffect.setFloat("currentCount", this._currentActiveCount);
  467. this._updateEffect.setFloat("timeDelta", this._timeDelta);
  468. this._updateEffect.setFloat("stopFactor", this._stopped ? 0 : 1);
  469. this._updateEffect.setFloat3("generalRandoms", Math.random(), Math.random(), Math.random());
  470. this._updateEffect.setTexture("randomSampler", this._randomTexture);
  471. this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
  472. this._updateEffect.setFloat2("emitPower", this.minEmitPower, this.maxEmitPower);
  473. this._updateEffect.setDirectColor4("color1", this.color1);
  474. this._updateEffect.setDirectColor4("color2", this.color2);
  475. this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
  476. this._updateEffect.setFloat2("angleRange", this.minAngularSpeed, this.maxAngularSpeed);
  477. this._updateEffect.setVector3("gravity", this.gravity);
  478. if (this.particleEmitterType) {
  479. this.particleEmitterType.applyToShader(this._updateEffect);
  480. }
  481. let emitterWM: Matrix;
  482. if ((<AbstractMesh>this.emitter).position) {
  483. var emitterMesh = (<AbstractMesh>this.emitter);
  484. emitterWM = emitterMesh.getWorldMatrix();
  485. } else {
  486. var emitterPosition = (<Vector3>this.emitter);
  487. emitterWM = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
  488. }
  489. this._updateEffect.setMatrix("emitterWM", emitterWM);
  490. // Bind source VAO
  491. this._engine.bindVertexArrayObject(this._updateVAO[this._targetIndex], null);
  492. // Update
  493. this._engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
  494. this._engine.setRasterizerState(false);
  495. this._engine.beginTransformFeedback();
  496. this._engine.drawArraysType(Material.PointListDrawMode, 0, this._currentActiveCount);
  497. this._engine.endTransformFeedback();
  498. this._engine.setRasterizerState(true);
  499. this._engine.bindTransformFeedbackBuffer(null);
  500. // Enable render effect
  501. this._engine.enableEffect(this._renderEffect);
  502. let viewMatrix = this._scene.getViewMatrix();
  503. this._renderEffect.setMatrix("view", viewMatrix);
  504. this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
  505. this._renderEffect.setTexture("textureSampler", this.particleTexture);
  506. this._renderEffect.setDirectColor4("colorDead", this.colorDead);
  507. if (this._scene.clipPlane) {
  508. var clipPlane = this._scene.clipPlane;
  509. var invView = viewMatrix.clone();
  510. invView.invert();
  511. this._renderEffect.setMatrix("invView", invView);
  512. this._renderEffect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
  513. }
  514. // Draw order
  515. if (this.blendMode === ParticleSystem.BLENDMODE_ONEONE) {
  516. this._engine.setAlphaMode(Engine.ALPHA_ONEONE);
  517. } else {
  518. this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
  519. }
  520. if (this.forceDepthWrite) {
  521. this._engine.setDepthWrite(true);
  522. }
  523. // Bind source VAO
  524. this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
  525. // Render
  526. this._engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._currentActiveCount);
  527. this._engine.setAlphaMode(Engine.ALPHA_DISABLE);
  528. // Switch VAOs
  529. this._targetIndex++;
  530. if (this._targetIndex === 2) {
  531. this._targetIndex = 0;
  532. }
  533. // Switch buffers
  534. let tmpBuffer = this._sourceBuffer;
  535. this._sourceBuffer = this._targetBuffer;
  536. this._targetBuffer = tmpBuffer;
  537. return this._currentActiveCount;
  538. }
  539. /**
  540. * Rebuilds the particle system
  541. */
  542. public rebuild(): void {
  543. this._initialize(true);
  544. }
  545. private _releaseBuffers() {
  546. if (this._buffer0) {
  547. this._buffer0.dispose();
  548. (<any>this._buffer0) = null;
  549. }
  550. if (this._buffer1) {
  551. this._buffer1.dispose();
  552. (<any>this._buffer1) = null;
  553. }
  554. if (this._spriteBuffer) {
  555. this._spriteBuffer.dispose();
  556. (<any>this._spriteBuffer) = null;
  557. }
  558. }
  559. private _releaseVAOs() {
  560. if (!this._updateVAO) {
  561. return;
  562. }
  563. for (var index = 0; index < this._updateVAO.length; index++) {
  564. this._engine.releaseVertexArrayObject(this._updateVAO[index]);
  565. }
  566. this._updateVAO = [];
  567. for (var index = 0; index < this._renderVAO.length; index++) {
  568. this._engine.releaseVertexArrayObject(this._renderVAO[index]);
  569. }
  570. this._renderVAO = [];
  571. }
  572. /**
  573. * Disposes the particle system and free the associated resources
  574. * @param disposeTexture defines if the particule texture must be disposed as well (true by default)
  575. */
  576. public dispose(disposeTexture = true): void {
  577. var index = this._scene.particleSystems.indexOf(this);
  578. if (index > -1) {
  579. this._scene.particleSystems.splice(index, 1);
  580. }
  581. this._releaseBuffers();
  582. this._releaseVAOs();
  583. if (this._randomTexture) {
  584. this._randomTexture.dispose();
  585. (<any>this._randomTexture) = null;
  586. }
  587. if (disposeTexture && this.particleTexture) {
  588. this.particleTexture.dispose();
  589. this.particleTexture = null;
  590. }
  591. // Callback
  592. this.onDisposeObservable.notifyObservers(this);
  593. this.onDisposeObservable.clear();
  594. }
  595. /**
  596. * Clones the particle system.
  597. * @param name The name of the cloned object
  598. * @param newEmitter The new emitter to use
  599. * @returns the cloned particle system
  600. */
  601. public clone(name: string, newEmitter: any): Nullable<GPUParticleSystem> {
  602. var result = new GPUParticleSystem(name, {capacity: this._capacity, randomTextureSize: this._randomTextureSize}, this._scene);
  603. Tools.DeepCopy(this, result);
  604. if (newEmitter === undefined) {
  605. newEmitter = this.emitter;
  606. }
  607. result.emitter = newEmitter;
  608. if (this.particleTexture) {
  609. result.particleTexture = new Texture(this.particleTexture.url, this._scene);
  610. }
  611. return result;
  612. }
  613. /**
  614. * Serializes the particle system to a JSON object.
  615. * @returns the JSON object
  616. */
  617. public serialize(): any {
  618. var serializationObject: any = {};
  619. serializationObject.name = this.name;
  620. serializationObject.id = this.id;
  621. // Emitter
  622. if ((<AbstractMesh>this.emitter).position) {
  623. var emitterMesh = (<AbstractMesh>this.emitter);
  624. serializationObject.emitterId = emitterMesh.id;
  625. } else {
  626. var emitterPosition = (<Vector3>this.emitter);
  627. serializationObject.emitter = emitterPosition.asArray();
  628. }
  629. serializationObject.capacity = this.getCapacity();
  630. if (this.particleTexture) {
  631. serializationObject.textureName = this.particleTexture.name;
  632. }
  633. // Animations
  634. Animation.AppendSerializedAnimations(this, serializationObject);
  635. // Particle system
  636. serializationObject.activeParticleCount = this.activeParticleCount;
  637. serializationObject.randomTextureSize = this._randomTextureSize;
  638. serializationObject.minSize = this.minSize;
  639. serializationObject.maxSize = this.maxSize;
  640. serializationObject.minEmitPower = this.minEmitPower;
  641. serializationObject.maxEmitPower = this.maxEmitPower;
  642. serializationObject.minLifeTime = this.minLifeTime;
  643. serializationObject.maxLifeTime = this.maxLifeTime;
  644. serializationObject.minAngularSpeed = this.minAngularSpeed;
  645. serializationObject.maxAngularSpeed = this.maxAngularSpeed;
  646. serializationObject.emitRate = this.emitRate;
  647. serializationObject.gravity = this.gravity.asArray();
  648. serializationObject.color1 = this.color1.asArray();
  649. serializationObject.color2 = this.color2.asArray();
  650. serializationObject.colorDead = this.colorDead.asArray();
  651. serializationObject.updateSpeed = this.updateSpeed;
  652. serializationObject.targetStopDuration = this.targetStopDuration;
  653. serializationObject.blendMode = this.blendMode;
  654. // Emitter
  655. if (this.particleEmitterType) {
  656. serializationObject.particleEmitterType = this.particleEmitterType.serialize();
  657. }
  658. return serializationObject;
  659. }
  660. /**
  661. * Parses a JSON object to create a GPU particle system.
  662. * @param parsedParticleSystem The JSON object to parse
  663. * @param scene The scene to create the particle system in
  664. * @param rootUrl The root url to use to load external dependencies like texture
  665. * @returns the parsed GPU particle system
  666. */
  667. public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): GPUParticleSystem {
  668. var name = parsedParticleSystem.name;
  669. var particleSystem = new GPUParticleSystem(name, {capacity: parsedParticleSystem.capacity, randomTextureSize: parsedParticleSystem.randomTextureSize}, scene);
  670. if (parsedParticleSystem.id) {
  671. particleSystem.id = parsedParticleSystem.id;
  672. }
  673. // Texture
  674. if (parsedParticleSystem.textureName) {
  675. particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
  676. particleSystem.particleTexture.name = parsedParticleSystem.textureName;
  677. }
  678. // Emitter
  679. if (parsedParticleSystem.emitterId) {
  680. particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
  681. } else {
  682. particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
  683. }
  684. // Animations
  685. if (parsedParticleSystem.animations) {
  686. for (var animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
  687. var parsedAnimation = parsedParticleSystem.animations[animationIndex];
  688. particleSystem.animations.push(Animation.Parse(parsedAnimation));
  689. }
  690. }
  691. // Particle system
  692. particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;
  693. particleSystem.minSize = parsedParticleSystem.minSize;
  694. particleSystem.maxSize = parsedParticleSystem.maxSize;
  695. particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
  696. particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
  697. particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
  698. particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
  699. particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;
  700. particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
  701. particleSystem.emitRate = parsedParticleSystem.emitRate;
  702. particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
  703. particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
  704. particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
  705. particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
  706. particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
  707. particleSystem.targetStopDuration = parsedParticleSystem.targetStopDuration;
  708. particleSystem.blendMode = parsedParticleSystem.blendMode;
  709. // Emitter
  710. if (parsedParticleSystem.particleEmitterType) {
  711. let emitterType: IParticleEmitterType;
  712. switch (parsedParticleSystem.particleEmitterType.type) {
  713. case "SphereEmitter":
  714. emitterType = new SphereParticleEmitter();
  715. break;
  716. case "SphereDirectedParticleEmitter":
  717. emitterType = new SphereDirectedParticleEmitter();
  718. break;
  719. case "ConeEmitter":
  720. emitterType = new ConeParticleEmitter();
  721. break;
  722. case "BoxEmitter":
  723. default:
  724. emitterType = new BoxParticleEmitter();
  725. break;
  726. }
  727. emitterType.parse(parsedParticleSystem.particleEmitterType);
  728. particleSystem.particleEmitterType = emitterType;
  729. }
  730. return particleSystem;
  731. }
  732. }
  733. }