babylon.gpuParticleSystem.ts 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443
  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 _accumulatedCount = 0;
  32. private _renderEffect: Effect;
  33. private _updateEffect: Effect;
  34. private _buffer0: Buffer;
  35. private _buffer1: Buffer;
  36. private _spriteBuffer: Buffer;
  37. private _updateVAO: Array<WebGLVertexArrayObject>;
  38. private _renderVAO: Array<WebGLVertexArrayObject>;
  39. private _targetIndex = 0;
  40. private _sourceBuffer: Buffer;
  41. private _targetBuffer: Buffer;
  42. private _scene: Scene;
  43. private _engine: Engine;
  44. private _currentRenderId = -1;
  45. private _started = false;
  46. private _stopped = false;
  47. private _timeDelta = 0;
  48. private _randomTexture: RawTexture;
  49. private _randomTexture2: RawTexture;
  50. private _attributesStrideSize = 21;
  51. private _updateEffectOptions: EffectCreationOptions;
  52. private _randomTextureSize: number;
  53. private _actualFrame = 0;
  54. private readonly _rawTextureWidth = 256;
  55. /**
  56. * List of animations used by the particle system.
  57. */
  58. public animations: Animation[] = [];
  59. /**
  60. * Gets a boolean indicating if the GPU particles can be rendered on current browser
  61. */
  62. public static get IsSupported(): boolean {
  63. if (!Engine.LastCreatedEngine) {
  64. return false;
  65. }
  66. return Engine.LastCreatedEngine.webGLVersion > 1;
  67. }
  68. /**
  69. * An event triggered when the system is disposed.
  70. */
  71. public onDisposeObservable = new Observable<GPUParticleSystem>();
  72. /**
  73. * The overall motion speed (0.01 is default update speed, faster updates = faster animation)
  74. */
  75. public updateSpeed = 0.01;
  76. /**
  77. * The amount of time the particle system is running (depends of the overall update speed).
  78. */
  79. public targetStopDuration = 0;
  80. /**
  81. * The texture used to render each particle. (this can be a spritesheet)
  82. */
  83. public particleTexture: Nullable<Texture>;
  84. /**
  85. * Blend mode use to render the particle, it can be either ParticleSystem.BLENDMODE_ONEONE or ParticleSystem.BLENDMODE_STANDARD.
  86. */
  87. public blendMode = ParticleSystem.BLENDMODE_ONEONE;
  88. /**
  89. * Minimum life time of emitting particles.
  90. */
  91. public minLifeTime = 1;
  92. /**
  93. * Maximum life time of emitting particles.
  94. */
  95. public maxLifeTime = 1;
  96. /**
  97. * Minimum Size of emitting particles.
  98. */
  99. public minSize = 1;
  100. /**
  101. * Maximum Size of emitting particles.
  102. */
  103. public maxSize = 1;
  104. /**
  105. * Minimum scale of emitting particles on X axis.
  106. */
  107. public minScaleX = 1;
  108. /**
  109. * Maximum scale of emitting particles on X axis.
  110. */
  111. public maxScaleX = 1;
  112. /**
  113. * Minimum scale of emitting particles on Y axis.
  114. */
  115. public minScaleY = 1;
  116. /**
  117. * Maximum scale of emitting particles on Y axis.
  118. */
  119. public maxScaleY = 1;
  120. /**
  121. * Random color of each particle after it has been emitted, between color1 and color2 vectors.
  122. */
  123. public color1 = new Color4(1.0, 1.0, 1.0, 1.0);
  124. /**
  125. * Random color of each particle after it has been emitted, between color1 and color2 vectors.
  126. */
  127. public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
  128. /**
  129. * Color the particle will have at the end of its lifetime.
  130. */
  131. public colorDead = new Color4(0, 0, 0, 0);
  132. /**
  133. * The maximum number of particles to emit per frame until we reach the activeParticleCount value
  134. */
  135. public emitRate = 100;
  136. /**
  137. * You can use gravity if you want to give an orientation to your particles.
  138. */
  139. public gravity = Vector3.Zero();
  140. /**
  141. * Minimum power of emitting particles.
  142. */
  143. public minEmitPower = 1;
  144. /**
  145. * Maximum power of emitting particles.
  146. */
  147. public maxEmitPower = 1;
  148. /**
  149. * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
  150. */
  151. public minAngularSpeed = 0;
  152. /**
  153. * Maximum angular speed of emitting particles (Z-axis rotation for each particle).
  154. */
  155. public maxAngularSpeed = 0;
  156. /**
  157. * The particle emitter type defines the emitter used by the particle system.
  158. * It can be for example box, sphere, or cone...
  159. */
  160. public particleEmitterType: Nullable<IParticleEmitterType>;
  161. /**
  162. * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
  163. * This only works when particleEmitterTyps is a BoxParticleEmitter
  164. */
  165. public get direction1(): Vector3 {
  166. if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
  167. return (<BoxParticleEmitter>this.particleEmitterType).direction1;
  168. }
  169. return Vector3.Zero();
  170. }
  171. public set direction1(value: Vector3) {
  172. if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
  173. (<BoxParticleEmitter>this.particleEmitterType).direction1 = value;
  174. }
  175. }
  176. /**
  177. * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
  178. * This only works when particleEmitterTyps is a BoxParticleEmitter
  179. */
  180. public get direction2(): Vector3 {
  181. if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
  182. return (<BoxParticleEmitter>this.particleEmitterType).direction2;
  183. }
  184. return Vector3.Zero();
  185. }
  186. public set direction2(value: Vector3) {
  187. if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
  188. (<BoxParticleEmitter>this.particleEmitterType).direction2 = value;
  189. }
  190. }
  191. /**
  192. * 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.
  193. * This only works when particleEmitterTyps is a BoxParticleEmitter
  194. */
  195. public get minEmitBox(): Vector3 {
  196. if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
  197. return (<BoxParticleEmitter>this.particleEmitterType).minEmitBox;
  198. }
  199. return Vector3.Zero();
  200. }
  201. public set minEmitBox(value: Vector3) {
  202. if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
  203. (<BoxParticleEmitter>this.particleEmitterType).minEmitBox = value;
  204. }
  205. }
  206. /**
  207. * 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.
  208. * This only works when particleEmitterTyps is a BoxParticleEmitter
  209. */
  210. public get maxEmitBox(): Vector3 {
  211. if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
  212. return (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox;
  213. }
  214. return Vector3.Zero();
  215. }
  216. public set maxEmitBox(value: Vector3) {
  217. if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
  218. (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox = value;
  219. }
  220. }
  221. /**
  222. * Gets the maximum number of particles active at the same time.
  223. * @returns The max number of active particles.
  224. */
  225. public getCapacity(): number {
  226. return this._capacity;
  227. }
  228. /**
  229. * Forces the particle to write their depth information to the depth buffer. This can help preventing other draw calls
  230. * to override the particles.
  231. */
  232. public forceDepthWrite = false;
  233. /**
  234. * Gets or set the number of active particles
  235. */
  236. public get activeParticleCount(): number {
  237. return this._activeCount;
  238. }
  239. public set activeParticleCount(value: number) {
  240. this._activeCount = Math.min(value, this._capacity);
  241. }
  242. private _preWarmDone = false;
  243. /** Gets or sets a value indicating how many cycles (or frames) must be executed before first rendering (this value has to be set before starting the system). Default is 0 */
  244. public preWarmCycles = 0;
  245. /** Gets or sets a value indicating the time step multiplier to use in pre-warm mode (default is 1) */
  246. public preWarmStepOffset = 1;
  247. /**
  248. * Gets or sets the minimal initial rotation in radians.
  249. */
  250. public minInitialRotation = 0;
  251. /**
  252. * Gets or sets the maximal initial rotation in radians.
  253. */
  254. public maxInitialRotation = 0;
  255. /**
  256. * If using a spritesheet (isAnimationSheetEnabled) defines the speed of the sprite loop (default is 1 meaning the animation will play once during the entire particle lifetime)
  257. */
  258. public spriteCellChangeSpeed = 1;
  259. /**
  260. * If using a spritesheet (isAnimationSheetEnabled) defines the first sprite cell to display
  261. */
  262. public startSpriteCellID = 0;
  263. /**
  264. * If using a spritesheet (isAnimationSheetEnabled) defines the last sprite cell to display
  265. */
  266. public endSpriteCellID = 0;
  267. /**
  268. * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell width to use
  269. */
  270. public spriteCellWidth = 0;
  271. /**
  272. * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell height to use
  273. */
  274. public spriteCellHeight = 0;
  275. /** Gets or sets a Vector2 used to move the pivot (by default (0,0)) */
  276. public translationPivot = new Vector2(0, 0);
  277. /**
  278. * Gets or sets the billboard mode to use when isBillboardBased = true.
  279. * Only BABYLON.AbstractMesh.BILLBOARDMODE_ALL and AbstractMesh.BILLBOARDMODE_Y are supported so far
  280. */
  281. public billboardMode = AbstractMesh.BILLBOARDMODE_ALL;
  282. private _isAnimationSheetEnabled: boolean;
  283. /**
  284. * Gets whether an animation sprite sheet is enabled or not on the particle system
  285. */
  286. public get isAnimationSheetEnabled(): boolean {
  287. return this._isAnimationSheetEnabled;
  288. }
  289. /**
  290. * Is this system ready to be used/rendered
  291. * @return true if the system is ready
  292. */
  293. public isReady(): boolean {
  294. if (!this._updateEffect) {
  295. this._recreateUpdateEffect();
  296. this._recreateRenderEffect();
  297. return false;
  298. }
  299. if (!this.emitter || !this._updateEffect.isReady() || !this._renderEffect.isReady() || !this.particleTexture || !this.particleTexture.isReady()) {
  300. return false;
  301. }
  302. return true;
  303. }
  304. /**
  305. * Gets Wether the system has been started.
  306. * @returns True if it has been started, otherwise false.
  307. */
  308. public isStarted(): boolean {
  309. return this._started;
  310. }
  311. /**
  312. * Starts the particle system and begins to emit
  313. * @param delay defines the delay in milliseconds before starting the system (0 by default)
  314. */
  315. public start(delay = 0): void {
  316. if (delay) {
  317. setTimeout(()=> {
  318. this.start(0);
  319. }, delay);
  320. return;
  321. }
  322. this._started = true;
  323. this._stopped = false;
  324. this._preWarmDone = false;
  325. }
  326. /**
  327. * Stops the particle system.
  328. */
  329. public stop(): void {
  330. this._stopped = true;
  331. }
  332. /**
  333. * Remove all active particles
  334. */
  335. public reset(): void {
  336. this._releaseBuffers();
  337. this._releaseVAOs();
  338. this._currentActiveCount = 0;
  339. this._targetIndex = 0;
  340. }
  341. /**
  342. * Returns the string "GPUParticleSystem"
  343. * @returns a string containing the class name
  344. */
  345. public getClassName(): string {
  346. return "GPUParticleSystem";
  347. }
  348. private _isBillboardBased = true;
  349. /**
  350. * Gets or sets a boolean indicating if the particles must be rendered as billboard or aligned with the direction
  351. */
  352. public get isBillboardBased(): boolean {
  353. return this._isBillboardBased;
  354. }
  355. public set isBillboardBased(value: boolean) {
  356. if (this._isBillboardBased === value) {
  357. return;
  358. }
  359. this._isBillboardBased = value;
  360. this._releaseBuffers();
  361. }
  362. private _colorGradients: Nullable<Array<ColorGradient>> = null;
  363. private _colorGradientsTexture: RawTexture;
  364. /**
  365. * Gets the current list of color gradients.
  366. * You must use addColorGradient and removeColorGradient to udpate this list
  367. * @returns the list of color gradients
  368. */
  369. public getColorGradients(): Nullable<Array<ColorGradient>> {
  370. return this._colorGradients;
  371. }
  372. /**
  373. * Gets the current list of size gradients.
  374. * You must use addSizeGradient and removeSizeGradient to udpate this list
  375. * @returns the list of size gradients
  376. */
  377. public getSizeGradients(): Nullable<Array<FactorGradient>> {
  378. return this._sizeGradients;
  379. }
  380. /**
  381. * Gets the current list of angular speed gradients.
  382. * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
  383. * @returns the list of angular speed gradients
  384. */
  385. public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
  386. return this._angularSpeedGradients;
  387. }
  388. /**
  389. * Gets the current list of velocity gradients.
  390. * You must use addVelocityGradient and removeVelocityGradient to udpate this list
  391. * @returns the list of angular speed gradients
  392. */
  393. public getVelocityGradients(): Nullable<Array<FactorGradient>> {
  394. return this._velocityGradients;
  395. }
  396. private _removeGradient(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): GPUParticleSystem {
  397. if (!gradients) {
  398. return this;
  399. }
  400. let index = 0;
  401. for (var valueGradient of gradients) {
  402. if (valueGradient.gradient === gradient) {
  403. gradients.splice(index, 1);
  404. break;
  405. }
  406. index++;
  407. }
  408. if (texture) {
  409. texture.dispose();
  410. }
  411. this._releaseBuffers();
  412. return this;
  413. }
  414. /**
  415. * Adds a new color gradient
  416. * @param gradient defines the gradient to use (between 0 and 1)
  417. * @param color defines the color to affect to the specified gradient
  418. * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
  419. * @returns the current particle system
  420. */
  421. public addColorGradient(gradient: number, color1: Color4, color2?: Color4): GPUParticleSystem {
  422. if (!this._colorGradients) {
  423. this._colorGradients = [];
  424. }
  425. let colorGradient = new ColorGradient();
  426. colorGradient.gradient = gradient;
  427. colorGradient.color1 = color1;
  428. this._colorGradients.push(colorGradient);
  429. this._colorGradients.sort((a, b) => {
  430. if (a.gradient < b.gradient) {
  431. return -1;
  432. } else if (a.gradient > b.gradient) {
  433. return 1;
  434. }
  435. return 0;
  436. });
  437. if (this._colorGradientsTexture) {
  438. this._colorGradientsTexture.dispose();
  439. (<any>this._colorGradientsTexture) = null;
  440. }
  441. this._releaseBuffers();
  442. return this;
  443. }
  444. /**
  445. * Remove a specific color gradient
  446. * @param gradient defines the gradient to remove
  447. * @returns the current particle system
  448. */
  449. public removeColorGradient(gradient: number): GPUParticleSystem {
  450. this._removeGradient(gradient, this._colorGradients, this._colorGradientsTexture);
  451. (<any>this._colorGradientsTexture) = null;
  452. return this;
  453. }
  454. private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
  455. private _angularSpeedGradientsTexture: RawTexture;
  456. private _sizeGradients: Nullable<Array<FactorGradient>> = null;
  457. private _sizeGradientsTexture: RawTexture;
  458. private _velocityGradients: Nullable<Array<FactorGradient>> = null;
  459. private _velocityGradientsTexture: RawTexture;
  460. private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number) {
  461. let valueGradient = new FactorGradient();
  462. valueGradient.gradient = gradient;
  463. valueGradient.factor1 = factor;
  464. factorGradients.push(valueGradient);
  465. factorGradients.sort((a, b) => {
  466. if (a.gradient < b.gradient) {
  467. return -1;
  468. } else if (a.gradient > b.gradient) {
  469. return 1;
  470. }
  471. return 0;
  472. });
  473. this._releaseBuffers();
  474. }
  475. /**
  476. * Adds a new size gradient
  477. * @param gradient defines the gradient to use (between 0 and 1)
  478. * @param factor defines the size factor to affect to the specified gradient
  479. * @returns the current particle system
  480. */
  481. public addSizeGradient(gradient: number, factor: number): GPUParticleSystem {
  482. if (!this._sizeGradients) {
  483. this._sizeGradients = [];
  484. }
  485. this._addFactorGradient(this._sizeGradients, gradient, factor);
  486. if (this._sizeGradientsTexture) {
  487. this._sizeGradientsTexture.dispose();
  488. (<any>this._sizeGradientsTexture) = null;
  489. }
  490. this._releaseBuffers();
  491. return this;
  492. }
  493. /**
  494. * Remove a specific size gradient
  495. * @param gradient defines the gradient to remove
  496. * @returns the current particle system
  497. */
  498. public removeSizeGradient(gradient: number): GPUParticleSystem {
  499. this._removeGradient(gradient, this._sizeGradients, this._sizeGradientsTexture);
  500. (<any>this._sizeGradientsTexture) = null;
  501. return this;
  502. }
  503. /**
  504. * Adds a new angular speed gradient
  505. * @param gradient defines the gradient to use (between 0 and 1)
  506. * @param factor defines the size factor to affect to the specified gradient
  507. * @returns the current particle system
  508. */
  509. public addAngularSpeedGradient(gradient: number, factor: number): GPUParticleSystem {
  510. if (!this._angularSpeedGradients) {
  511. this._angularSpeedGradients = [];
  512. }
  513. this._addFactorGradient(this._angularSpeedGradients, gradient, factor);
  514. if (this._angularSpeedGradientsTexture) {
  515. this._angularSpeedGradientsTexture.dispose();
  516. (<any>this._angularSpeedGradientsTexture) = null;
  517. }
  518. this._releaseBuffers();
  519. return this;
  520. }
  521. /**
  522. * Remove a specific angular speed gradient
  523. * @param gradient defines the gradient to remove
  524. * @returns the current particle system
  525. */
  526. public removeAngularSpeedGradient(gradient: number): GPUParticleSystem {
  527. this._removeGradient(gradient, this._angularSpeedGradients, this._angularSpeedGradientsTexture);
  528. (<any>this._angularSpeedGradientsTexture) = null;
  529. return this;
  530. }
  531. /**
  532. * Adds a new velocity gradient
  533. * @param gradient defines the gradient to use (between 0 and 1)
  534. * @param factor defines the size factor to affect to the specified gradient
  535. * @returns the current particle system
  536. */
  537. public addVelocityGradient(gradient: number, factor: number): GPUParticleSystem {
  538. if (!this._velocityGradients) {
  539. this._velocityGradients = [];
  540. }
  541. this._addFactorGradient(this._velocityGradients, gradient, factor);
  542. if (this._velocityGradientsTexture) {
  543. this._velocityGradientsTexture.dispose();
  544. (<any>this._velocityGradientsTexture) = null;
  545. }
  546. this._releaseBuffers();
  547. return this;
  548. }
  549. /**
  550. * Remove a specific velocity gradient
  551. * @param gradient defines the gradient to remove
  552. * @returns the current particle system
  553. */
  554. public removeVelocityGradient(gradient: number): GPUParticleSystem {
  555. this._removeGradient(gradient, this._velocityGradients, this._velocityGradientsTexture);
  556. (<any>this._velocityGradientsTexture) = null;
  557. return this;
  558. }
  559. /**
  560. * Instantiates a GPU particle system.
  561. * 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.
  562. * @param name The name of the particle system
  563. * @param options The options used to create the system
  564. * @param scene The scene the particle system belongs to
  565. * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
  566. */
  567. constructor(name: string, options: Partial<{
  568. capacity: number,
  569. randomTextureSize: number
  570. }>, scene: Scene, isAnimationSheetEnabled: boolean = false) {
  571. this.id = name;
  572. this.name = name;
  573. this._scene = scene || Engine.LastCreatedScene;
  574. this._engine = this._scene.getEngine();
  575. if (!options.randomTextureSize) {
  576. delete options.randomTextureSize;
  577. }
  578. let fullOptions = {
  579. capacity: 50000,
  580. randomTextureSize: this._engine.getCaps().maxTextureSize,
  581. ...options
  582. };
  583. var optionsAsNumber = <number>options;
  584. if (isFinite(optionsAsNumber)) {
  585. fullOptions.capacity = optionsAsNumber;
  586. }
  587. this._capacity = fullOptions.capacity;
  588. this._activeCount = fullOptions.capacity;
  589. this._currentActiveCount = 0;
  590. this._isAnimationSheetEnabled = isAnimationSheetEnabled;
  591. this._scene.particleSystems.push(this);
  592. this._updateEffectOptions = {
  593. attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex"],
  594. uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
  595. "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor",
  596. "angleRange", "radiusRange", "cellInfos"],
  597. uniformBuffersNames: [],
  598. samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler"],
  599. defines: "",
  600. fallbacks: null,
  601. onCompiled: null,
  602. onError: null,
  603. indexParameters: null,
  604. maxSimultaneousLights: 0,
  605. transformFeedbackVaryings: []
  606. };
  607. this.particleEmitterType = new BoxParticleEmitter();
  608. // Random data
  609. var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
  610. var d = [];
  611. for (var i = 0; i < maxTextureSize; ++i) {
  612. d.push(Math.random());
  613. d.push(Math.random());
  614. d.push(Math.random());
  615. d.push(Math.random());
  616. }
  617. this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT);
  618. this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
  619. this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
  620. d = [];
  621. for (var i = 0; i < maxTextureSize; ++i) {
  622. d.push(Math.random());
  623. d.push(Math.random());
  624. d.push(Math.random());
  625. d.push(Math.random());
  626. }
  627. this._randomTexture2 = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT);
  628. this._randomTexture2.wrapU = Texture.WRAP_ADDRESSMODE;
  629. this._randomTexture2.wrapV = Texture.WRAP_ADDRESSMODE;
  630. this._randomTextureSize = maxTextureSize;
  631. }
  632. private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {
  633. let updateVertexBuffers: {[key: string]: VertexBuffer} = {};
  634. updateVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3);
  635. updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
  636. updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
  637. updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 4);
  638. updateVertexBuffers["size"] = source.createVertexBuffer("size", 9, 3);
  639. let offset = 12;
  640. if (!this._colorGradientsTexture) {
  641. updateVertexBuffers["color"] = source.createVertexBuffer("color", offset, 4);
  642. offset += 4;
  643. }
  644. updateVertexBuffers["direction"] = source.createVertexBuffer("direction", offset, 3);
  645. offset += 3
  646. if (!this._isBillboardBased) {
  647. updateVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3);
  648. offset += 3;
  649. }
  650. if (this._angularSpeedGradientsTexture) {
  651. updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1);
  652. offset += 1;
  653. } else {
  654. updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
  655. offset += 2;
  656. }
  657. if (this._isAnimationSheetEnabled) {
  658. updateVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1);
  659. offset += 1;
  660. }
  661. let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
  662. this._engine.bindArrayBuffer(null);
  663. return vao;
  664. }
  665. private _createRenderVAO(source: Buffer, spriteSource: Buffer): WebGLVertexArrayObject {
  666. let renderVertexBuffers: {[key: string]: VertexBuffer} = {};
  667. renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, this._attributesStrideSize, true);
  668. renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
  669. renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
  670. renderVertexBuffers["size"] = source.createVertexBuffer("size", 9, 3, this._attributesStrideSize, true);
  671. let offset = 12;
  672. if (!this._colorGradientsTexture) {
  673. renderVertexBuffers["color"] = source.createVertexBuffer("color", offset, 4, this._attributesStrideSize, true);
  674. offset += 4;
  675. }
  676. offset += 3; // Direction
  677. if (!this._isBillboardBased) {
  678. renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
  679. offset += 3;
  680. }
  681. renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1, this._attributesStrideSize, true);
  682. if (this._angularSpeedGradientsTexture) {
  683. offset++;
  684. } else {
  685. offset += 2;
  686. }
  687. if (this._isAnimationSheetEnabled) {
  688. renderVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1, this._attributesStrideSize, true);
  689. offset += 1;
  690. }
  691. renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
  692. renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
  693. let vao = this._engine.recordVertexArrayObject(renderVertexBuffers, null, this._renderEffect);
  694. this._engine.bindArrayBuffer(null);
  695. return vao;
  696. }
  697. private _initialize(force = false): void {
  698. if (this._buffer0 && !force) {
  699. return;
  700. }
  701. let engine = this._scene.getEngine();
  702. var data = new Array<float>();
  703. if (!this.isBillboardBased) {
  704. this._attributesStrideSize += 3;
  705. }
  706. if (this._colorGradientsTexture) {
  707. this._attributesStrideSize -= 4;
  708. }
  709. if (this._angularSpeedGradientsTexture) {
  710. this._attributesStrideSize -= 1;
  711. }
  712. if (this._isAnimationSheetEnabled) {
  713. this._attributesStrideSize += 1;
  714. }
  715. for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
  716. // position
  717. data.push(0.0);
  718. data.push(0.0);
  719. data.push(0.0);
  720. // Age and life
  721. data.push(0.0); // create the particle as a dead one to create a new one at start
  722. data.push(0.0);
  723. // Seed
  724. data.push(Math.random());
  725. data.push(Math.random());
  726. data.push(Math.random());
  727. data.push(Math.random());
  728. // Size
  729. data.push(0.0);
  730. data.push(0.0);
  731. data.push(0.0);
  732. if (!this._colorGradientsTexture) {
  733. // color
  734. data.push(0.0);
  735. data.push(0.0);
  736. data.push(0.0);
  737. data.push(0.0);
  738. }
  739. // direction
  740. data.push(0.0);
  741. data.push(0.0);
  742. data.push(0.0);
  743. if (!this.isBillboardBased) {
  744. // initialDirection
  745. data.push(0.0);
  746. data.push(0.0);
  747. data.push(0.0);
  748. }
  749. // angle
  750. data.push(0.0);
  751. if (!this._angularSpeedGradientsTexture) {
  752. data.push(0.0);
  753. }
  754. if (this._isAnimationSheetEnabled) {
  755. data.push(0.0);
  756. }
  757. }
  758. // Sprite data
  759. var spriteData = new Float32Array([0.5, 0.5, 1, 1,
  760. -0.5, 0.5, 0, 1,
  761. -0.5, -0.5, 0, 0,
  762. 0.5, -0.5, 1, 0]);
  763. // Buffers
  764. this._buffer0 = new Buffer(engine, data, false, this._attributesStrideSize);
  765. this._buffer1 = new Buffer(engine, data, false, this._attributesStrideSize);
  766. this._spriteBuffer = new Buffer(engine, spriteData, false, 4);
  767. // Update VAO
  768. this._updateVAO = [];
  769. this._updateVAO.push(this._createUpdateVAO(this._buffer0));
  770. this._updateVAO.push(this._createUpdateVAO(this._buffer1));
  771. // Render VAO
  772. this._renderVAO = [];
  773. this._renderVAO.push(this._createRenderVAO(this._buffer1, this._spriteBuffer));
  774. this._renderVAO.push(this._createRenderVAO(this._buffer0, this._spriteBuffer));
  775. // Links
  776. this._sourceBuffer = this._buffer0;
  777. this._targetBuffer = this._buffer1;
  778. }
  779. /** @hidden */
  780. public _recreateUpdateEffect() {
  781. let defines = this.particleEmitterType ? this.particleEmitterType.getEffectDefines() : "";
  782. if (this._isBillboardBased) {
  783. defines += "\n#define BILLBOARD";
  784. }
  785. if (this._colorGradientsTexture) {
  786. defines += "\n#define COLORGRADIENTS";
  787. }
  788. if (this._sizeGradientsTexture) {
  789. defines += "\n#define SIZEGRADIENTS";
  790. }
  791. if (this._angularSpeedGradientsTexture) {
  792. defines += "\n#define ANGULARSPEEDGRADIENTS";
  793. }
  794. if (this._velocityGradientsTexture) {
  795. defines += "\n#define VELOCITYGRADIENTS";
  796. }
  797. if (this.isAnimationSheetEnabled) {
  798. defines += "\n#define ANIMATESHEET";
  799. }
  800. if (this._updateEffect && this._updateEffectOptions.defines === defines) {
  801. return;
  802. }
  803. this._updateEffectOptions.transformFeedbackVaryings = ["outPosition", "outAge", "outLife", "outSeed", "outSize"];
  804. if (!this._colorGradientsTexture) {
  805. this._updateEffectOptions.transformFeedbackVaryings.push("outColor");
  806. }
  807. this._updateEffectOptions.transformFeedbackVaryings.push("outDirection");
  808. if (!this._isBillboardBased) {
  809. this._updateEffectOptions.transformFeedbackVaryings.push("outInitialDirection");
  810. }
  811. this._updateEffectOptions.transformFeedbackVaryings.push("outAngle");
  812. if (this.isAnimationSheetEnabled) {
  813. this._updateEffectOptions.transformFeedbackVaryings.push("outCellIndex");
  814. }
  815. this._updateEffectOptions.defines = defines;
  816. this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());
  817. }
  818. /** @hidden */
  819. public _recreateRenderEffect() {
  820. let defines = "";
  821. if (this._scene.clipPlane) {
  822. defines = "\n#define CLIPPLANE";
  823. }
  824. if (this._isBillboardBased) {
  825. defines += "\n#define BILLBOARD";
  826. switch (this.billboardMode) {
  827. case AbstractMesh.BILLBOARDMODE_Y:
  828. defines += "\n#define BILLBOARDY";
  829. break;
  830. case AbstractMesh.BILLBOARDMODE_ALL:
  831. default:
  832. break;
  833. }
  834. }
  835. if (this._colorGradientsTexture) {
  836. defines += "\n#define COLORGRADIENTS";
  837. }
  838. if (this.isAnimationSheetEnabled) {
  839. defines += "\n#define ANIMATESHEET";
  840. }
  841. if (this._renderEffect && this._renderEffect.defines === defines) {
  842. return;
  843. }
  844. this._renderEffect = new Effect("gpuRenderParticles",
  845. ["position", "age", "life", "size", "color", "offset", "uv", "initialDirection", "angle", "cellIndex"],
  846. ["view", "projection", "colorDead", "invView", "vClipPlane", "sheetInfos", "translationPivot", "eyePosition"],
  847. ["textureSampler", "colorGradientSampler"], this._scene.getEngine(), defines);
  848. }
  849. /**
  850. * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
  851. * @param preWarm defines if we are in the pre-warmimg phase
  852. */
  853. public animate(preWarm = false): void {
  854. this._timeDelta = this.updateSpeed * (preWarm ? this.preWarmStepOffset : this._scene.getAnimationRatio());
  855. this._actualFrame += this._timeDelta;
  856. if (!this._stopped) {
  857. if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration) {
  858. this.stop();
  859. }
  860. }
  861. }
  862. private _createFactorGradientTexture(factorGradients: Nullable<IValueGradient[]>, textureName: string) {
  863. let texture:RawTexture = (<any>this)[textureName];
  864. if (!factorGradients || !factorGradients.length || texture) {
  865. return;
  866. }
  867. let data = new Float32Array(this._rawTextureWidth);
  868. for (var x = 0; x < this._rawTextureWidth; x++) {
  869. var ratio = x / this._rawTextureWidth;
  870. Tools.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
  871. data[x] = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
  872. });
  873. }
  874. (<any>this)[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
  875. }
  876. private _createSizeGradientTexture() {
  877. this._createFactorGradientTexture(this._sizeGradients, "_sizeGradientsTexture");
  878. }
  879. private _createAngularSpeedGradientTexture() {
  880. this._createFactorGradientTexture(this._angularSpeedGradients, "_angularSpeedGradientsTexture");
  881. }
  882. private _createVelocityGradientTexture() {
  883. this._createFactorGradientTexture(this._velocityGradients, "_velocityGradientsTexture");
  884. }
  885. private _createColorGradientTexture() {
  886. if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
  887. return;
  888. }
  889. let data = new Uint8Array(this._rawTextureWidth * 4);
  890. let tmpColor = Tmp.Color4[0];
  891. for (var x = 0; x < this._rawTextureWidth; x++) {
  892. var ratio = x / this._rawTextureWidth;
  893. Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
  894. Color4.LerpToRef((<ColorGradient>currentGradient).color1, (<ColorGradient>nextGradient).color1, scale, tmpColor);
  895. data[x * 4] = tmpColor.r * 255;
  896. data[x * 4 + 1] = tmpColor.g * 255;
  897. data[x * 4 + 2] = tmpColor.b * 255;
  898. data[x * 4 + 3] = tmpColor.a * 255;
  899. });
  900. }
  901. this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
  902. }
  903. /**
  904. * Renders the particle system in its current state
  905. * @param preWarm defines if the system should only update the particles but not render them
  906. * @returns the current number of particles
  907. */
  908. public render(preWarm = false): number {
  909. if (!this._started) {
  910. return 0;
  911. }
  912. this._createColorGradientTexture();
  913. this._createSizeGradientTexture();
  914. this._createAngularSpeedGradientTexture();
  915. this._createVelocityGradientTexture();
  916. this._recreateUpdateEffect();
  917. this._recreateRenderEffect();
  918. if (!this.isReady()) {
  919. return 0;
  920. }
  921. if (!preWarm) {
  922. if (!this._preWarmDone && this.preWarmCycles) {
  923. for (var index = 0; index < this.preWarmCycles; index++) {
  924. this.animate(true);
  925. this.render(true);
  926. }
  927. this._preWarmDone = true;
  928. }
  929. if (this._currentRenderId === this._scene.getRenderId()) {
  930. return 0;
  931. }
  932. this._currentRenderId = this._scene.getRenderId();
  933. }
  934. // Get everything ready to render
  935. this._initialize();
  936. this._accumulatedCount += this.emitRate * this._timeDelta;
  937. if (this._accumulatedCount > 1) {
  938. var intPart = this._accumulatedCount | 0;
  939. this._accumulatedCount -= intPart;
  940. this._currentActiveCount = Math.min(this._activeCount, this._currentActiveCount + intPart);
  941. }
  942. if (!this._currentActiveCount) {
  943. return 0;
  944. }
  945. // Enable update effect
  946. this._engine.enableEffect(this._updateEffect);
  947. this._engine.setState(false);
  948. this._updateEffect.setFloat("currentCount", this._currentActiveCount);
  949. this._updateEffect.setFloat("timeDelta", this._timeDelta);
  950. this._updateEffect.setFloat("stopFactor", this._stopped ? 0 : 1);
  951. this._updateEffect.setTexture("randomSampler", this._randomTexture);
  952. this._updateEffect.setTexture("randomSampler2", this._randomTexture2);
  953. this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
  954. this._updateEffect.setFloat2("emitPower", this.minEmitPower, this.maxEmitPower);
  955. if (!this._colorGradientsTexture) {
  956. this._updateEffect.setDirectColor4("color1", this.color1);
  957. this._updateEffect.setDirectColor4("color2", this.color2);
  958. }
  959. this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
  960. this._updateEffect.setFloat4("scaleRange", this.minScaleX, this.maxScaleX, this.minScaleY, this.maxScaleY);
  961. this._updateEffect.setFloat4("angleRange", this.minAngularSpeed, this.maxAngularSpeed, this.minInitialRotation, this.maxInitialRotation);
  962. this._updateEffect.setVector3("gravity", this.gravity);
  963. if (this._sizeGradientsTexture) {
  964. this._updateEffect.setTexture("sizeGradientSampler", this._sizeGradientsTexture);
  965. }
  966. if (this._angularSpeedGradientsTexture) {
  967. this._updateEffect.setTexture("angularSpeedGradientSampler", this._angularSpeedGradientsTexture);
  968. }
  969. if (this._velocityGradientsTexture) {
  970. this._updateEffect.setTexture("velocityGradientSampler", this._velocityGradientsTexture);
  971. }
  972. if (this.particleEmitterType) {
  973. this.particleEmitterType.applyToShader(this._updateEffect);
  974. }
  975. if (this._isAnimationSheetEnabled) {
  976. this._updateEffect.setFloat3("cellInfos", this.startSpriteCellID, this.endSpriteCellID, this.spriteCellChangeSpeed);
  977. }
  978. let emitterWM: Matrix;
  979. if ((<AbstractMesh>this.emitter).position) {
  980. var emitterMesh = (<AbstractMesh>this.emitter);
  981. emitterWM = emitterMesh.getWorldMatrix();
  982. } else {
  983. var emitterPosition = (<Vector3>this.emitter);
  984. emitterWM = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
  985. }
  986. this._updateEffect.setMatrix("emitterWM", emitterWM);
  987. // Bind source VAO
  988. this._engine.bindVertexArrayObject(this._updateVAO[this._targetIndex], null);
  989. // Update
  990. this._engine.bindTransformFeedbackBuffer(this._targetBuffer.getBuffer());
  991. this._engine.setRasterizerState(false);
  992. this._engine.beginTransformFeedback(true);
  993. this._engine.drawArraysType(Material.PointListDrawMode, 0, this._currentActiveCount);
  994. this._engine.endTransformFeedback();
  995. this._engine.setRasterizerState(true);
  996. this._engine.bindTransformFeedbackBuffer(null);
  997. if (!preWarm) {
  998. // Enable render effect
  999. this._engine.enableEffect(this._renderEffect);
  1000. let viewMatrix = this._scene.getViewMatrix();
  1001. this._renderEffect.setMatrix("view", viewMatrix);
  1002. this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
  1003. this._renderEffect.setTexture("textureSampler", this.particleTexture);
  1004. this._renderEffect.setVector2("translationPivot", this.translationPivot);
  1005. if (this._colorGradientsTexture) {
  1006. this._renderEffect.setTexture("colorGradientSampler", this._colorGradientsTexture);
  1007. } else {
  1008. this._renderEffect.setDirectColor4("colorDead", this.colorDead);
  1009. }
  1010. if (this._isAnimationSheetEnabled && this.particleTexture) {
  1011. let baseSize = this.particleTexture.getBaseSize();
  1012. this._renderEffect.setFloat3("sheetInfos", this.spriteCellWidth / baseSize.width, this.spriteCellHeight / baseSize.height, baseSize.width / this.spriteCellWidth);
  1013. }
  1014. if (this._isBillboardBased) {
  1015. var camera = this._scene.activeCamera!;
  1016. this._renderEffect.setVector3("eyePosition", camera.globalPosition);
  1017. }
  1018. if (this._scene.clipPlane) {
  1019. var clipPlane = this._scene.clipPlane;
  1020. var invView = viewMatrix.clone();
  1021. invView.invert();
  1022. this._renderEffect.setMatrix("invView", invView);
  1023. this._renderEffect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
  1024. }
  1025. // Draw order
  1026. switch(this.blendMode)
  1027. {
  1028. case ParticleSystem.BLENDMODE_ADD:
  1029. this._engine.setAlphaMode(Engine.ALPHA_ADD);
  1030. break;
  1031. case ParticleSystem.BLENDMODE_ONEONE:
  1032. this._engine.setAlphaMode(Engine.ALPHA_ONEONE);
  1033. break;
  1034. case ParticleSystem.BLENDMODE_STANDARD:
  1035. this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
  1036. break;
  1037. }
  1038. if (this.forceDepthWrite) {
  1039. this._engine.setDepthWrite(true);
  1040. }
  1041. // Bind source VAO
  1042. this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
  1043. // Render
  1044. this._engine.drawArraysType(Material.TriangleFanDrawMode, 0, 4, this._currentActiveCount);
  1045. this._engine.setAlphaMode(Engine.ALPHA_DISABLE);
  1046. }
  1047. // Switch VAOs
  1048. this._targetIndex++;
  1049. if (this._targetIndex === 2) {
  1050. this._targetIndex = 0;
  1051. }
  1052. // Switch buffers
  1053. let tmpBuffer = this._sourceBuffer;
  1054. this._sourceBuffer = this._targetBuffer;
  1055. this._targetBuffer = tmpBuffer;
  1056. return this._currentActiveCount;
  1057. }
  1058. /**
  1059. * Rebuilds the particle system
  1060. */
  1061. public rebuild(): void {
  1062. this._initialize(true);
  1063. }
  1064. private _releaseBuffers() {
  1065. if (this._buffer0) {
  1066. this._buffer0.dispose();
  1067. (<any>this._buffer0) = null;
  1068. }
  1069. if (this._buffer1) {
  1070. this._buffer1.dispose();
  1071. (<any>this._buffer1) = null;
  1072. }
  1073. if (this._spriteBuffer) {
  1074. this._spriteBuffer.dispose();
  1075. (<any>this._spriteBuffer) = null;
  1076. }
  1077. }
  1078. private _releaseVAOs() {
  1079. if (!this._updateVAO) {
  1080. return;
  1081. }
  1082. for (var index = 0; index < this._updateVAO.length; index++) {
  1083. this._engine.releaseVertexArrayObject(this._updateVAO[index]);
  1084. }
  1085. this._updateVAO = [];
  1086. for (var index = 0; index < this._renderVAO.length; index++) {
  1087. this._engine.releaseVertexArrayObject(this._renderVAO[index]);
  1088. }
  1089. this._renderVAO = [];
  1090. }
  1091. /**
  1092. * Disposes the particle system and free the associated resources
  1093. * @param disposeTexture defines if the particule texture must be disposed as well (true by default)
  1094. */
  1095. public dispose(disposeTexture = true): void {
  1096. var index = this._scene.particleSystems.indexOf(this);
  1097. if (index > -1) {
  1098. this._scene.particleSystems.splice(index, 1);
  1099. }
  1100. this._releaseBuffers();
  1101. this._releaseVAOs();
  1102. if (this._colorGradientsTexture) {
  1103. this._colorGradientsTexture.dispose();
  1104. (<any>this._colorGradientsTexture) = null;
  1105. }
  1106. if (this._sizeGradientsTexture) {
  1107. this._sizeGradientsTexture.dispose();
  1108. (<any>this._sizeGradientsTexture) = null;
  1109. }
  1110. if (this._angularSpeedGradientsTexture) {
  1111. this._angularSpeedGradientsTexture.dispose();
  1112. (<any>this._angularSpeedGradientsTexture) = null;
  1113. }
  1114. if (this._velocityGradientsTexture) {
  1115. this._velocityGradientsTexture.dispose();
  1116. (<any>this._velocityGradientsTexture) = null;
  1117. }
  1118. if (this._randomTexture) {
  1119. this._randomTexture.dispose();
  1120. (<any>this._randomTexture) = null;
  1121. }
  1122. if (this._randomTexture2) {
  1123. this._randomTexture2.dispose();
  1124. (<any>this._randomTexture2) = null;
  1125. }
  1126. if (disposeTexture && this.particleTexture) {
  1127. this.particleTexture.dispose();
  1128. this.particleTexture = null;
  1129. }
  1130. // Callback
  1131. this.onDisposeObservable.notifyObservers(this);
  1132. this.onDisposeObservable.clear();
  1133. }
  1134. /**
  1135. * Clones the particle system.
  1136. * @param name The name of the cloned object
  1137. * @param newEmitter The new emitter to use
  1138. * @returns the cloned particle system
  1139. */
  1140. public clone(name: string, newEmitter: any): Nullable<GPUParticleSystem> {
  1141. var result = new GPUParticleSystem(name, {capacity: this._capacity, randomTextureSize: this._randomTextureSize}, this._scene);
  1142. Tools.DeepCopy(this, result);
  1143. if (newEmitter === undefined) {
  1144. newEmitter = this.emitter;
  1145. }
  1146. result.emitter = newEmitter;
  1147. if (this.particleTexture) {
  1148. result.particleTexture = new Texture(this.particleTexture.url, this._scene);
  1149. }
  1150. return result;
  1151. }
  1152. /**
  1153. * Serializes the particle system to a JSON object.
  1154. * @returns the JSON object
  1155. */
  1156. public serialize(): any {
  1157. var serializationObject: any = {};
  1158. ParticleSystem._Serialize(serializationObject, this);
  1159. serializationObject.activeParticleCount = this.activeParticleCount;
  1160. return serializationObject;
  1161. }
  1162. /**
  1163. * Parses a JSON object to create a GPU particle system.
  1164. * @param parsedParticleSystem The JSON object to parse
  1165. * @param scene The scene to create the particle system in
  1166. * @param rootUrl The root url to use to load external dependencies like texture
  1167. * @returns the parsed GPU particle system
  1168. */
  1169. public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): GPUParticleSystem {
  1170. var name = parsedParticleSystem.name;
  1171. var particleSystem = new GPUParticleSystem(name, {capacity: parsedParticleSystem.capacity, randomTextureSize: parsedParticleSystem.randomTextureSize}, scene);
  1172. if (parsedParticleSystem.activeParticleCount) {
  1173. particleSystem.activeParticleCount = parsedParticleSystem.activeParticleCount;
  1174. }
  1175. ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
  1176. return particleSystem;
  1177. }
  1178. }
  1179. }