babylon.gpuParticleSystem.ts 60 KB

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