babylon.particleSystem.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. var BABYLON = BABYLON || {};
  2. (function () {
  3. var appendParticleVertex = function (particle, vertices, offsetX, offsetY) {
  4. vertices.push(particle.position.x);
  5. vertices.push(particle.position.y);
  6. vertices.push(particle.position.z);
  7. vertices.push(particle.color.r);
  8. vertices.push(particle.color.g);
  9. vertices.push(particle.color.b);
  10. vertices.push(particle.color.a);
  11. vertices.push(particle.angle);
  12. vertices.push(particle.size);
  13. vertices.push(offsetX);
  14. vertices.push(offsetY);
  15. };
  16. var randomNumber = function (min, max) {
  17. if (min == max) {
  18. return (min);
  19. }
  20. var random = Math.random();
  21. return ((random * (max - min)) + min);
  22. };
  23. BABYLON.ParticleSystem = function (name, capacity, scene) {
  24. this.name = name;
  25. this.id = name;
  26. this._capacity = capacity;
  27. this._scene = scene;
  28. scene.particleSystems.push(this);
  29. // Vectors and colors
  30. this.gravity = BABYLON.Vector3.Zero();
  31. this.direction1 = new BABYLON.Vector3(0, 1.0, 0);
  32. this.direction2 = new BABYLON.Vector3(0, 1.0, 0);
  33. this.minEmitBox = new BABYLON.Vector3(-0.5, -0.5, -0.5);
  34. this.maxEmitBox = new BABYLON.Vector3(0.5, 0.5, 0.5);
  35. this.color1 = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
  36. this.color2 = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
  37. this.colorDead = new BABYLON.Color4(0, 0, 0, 1.0);
  38. this.deadAlpha = 0;
  39. this.textureMask = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
  40. // Particles
  41. this.particles = [];
  42. this._newPartsExcess = 0;
  43. // VBO
  44. this._vertexDeclaration = [3, 4, 4];
  45. this._vertexStrideSize = 11 * 4; // 10 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY)
  46. this._vertexBuffer = scene.getEngine().createDynamicVertexBuffer(capacity * this._vertexStrideSize * 4);
  47. var indices = [];
  48. var index = 0;
  49. for (var count = 0; count < capacity; count++) {
  50. indices.push(index);
  51. indices.push(index + 1);
  52. indices.push(index + 2);
  53. indices.push(index);
  54. indices.push(index + 2);
  55. indices.push(index + 3);
  56. index += 4;
  57. }
  58. this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
  59. };
  60. // Members
  61. BABYLON.ParticleSystem.prototype.emitter = null;
  62. BABYLON.ParticleSystem.prototype.emitRate = 10;
  63. BABYLON.ParticleSystem.prototype.manualEmitCount = -1;
  64. BABYLON.ParticleSystem.prototype.updateSpeed = 0.01;
  65. BABYLON.ParticleSystem.prototype.targetStopDuration = 0;
  66. BABYLON.ParticleSystem.prototype.disposeOnStop = false;
  67. BABYLON.ParticleSystem.prototype.minEmitPower = 1;
  68. BABYLON.ParticleSystem.prototype.maxEmitPower = 1;
  69. BABYLON.ParticleSystem.prototype.minLifeTime = 1;
  70. BABYLON.ParticleSystem.prototype.maxLifeTime = 1;
  71. BABYLON.ParticleSystem.prototype.minSize = 1;
  72. BABYLON.ParticleSystem.prototype.maxSize = 1;
  73. BABYLON.ParticleSystem.prototype.minAngularSpeed = 0;
  74. BABYLON.ParticleSystem.prototype.maxAngularSpeed = 0;
  75. BABYLON.ParticleSystem.prototype.particleTexture = null;
  76. BABYLON.ParticleSystem.prototype.onDispose = null;
  77. BABYLON.ParticleSystem.prototype.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
  78. // Methods
  79. BABYLON.ParticleSystem.prototype.isAlive = function () {
  80. return this._alive;
  81. };
  82. BABYLON.ParticleSystem.prototype.start = function () {
  83. this._started = true;
  84. this._stopped = false;
  85. this._actualFrame = 0;
  86. };
  87. BABYLON.ParticleSystem.prototype.stop = function () {
  88. this._stopped = true;
  89. };
  90. BABYLON.ParticleSystem.prototype._update = function (newParticles) {
  91. // Update current
  92. this._alive = this.particles.length > 0;
  93. for (var index = 0; index < this.particles.length; index++) {
  94. var particle = this.particles[index];
  95. particle.age += this._scaledUpdateSpeed;
  96. if (particle.age >= particle.lifeTime) {
  97. this.particles.splice(index, 1);
  98. index--;
  99. continue;
  100. }
  101. else {
  102. particle.color = particle.color.add(particle.colorStep.scale(this._scaledUpdateSpeed));
  103. if (particle.color.a < 0)
  104. particle.color.a = 0;
  105. particle.position = particle.position.add(particle.direction.scale(this._scaledUpdateSpeed));
  106. particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
  107. particle.direction = particle.direction.add(this.gravity.scale(this._scaledUpdateSpeed));
  108. }
  109. }
  110. // Add new ones
  111. var worldMatrix;
  112. if (this.emitter.position) {
  113. worldMatrix = this.emitter.getWorldMatrix();
  114. } else {
  115. worldMatrix = BABYLON.Matrix.Translation(this.emitter.x, this.emitter.y, this.emitter.z);
  116. }
  117. for (var index = 0; index < newParticles; index++) {
  118. if (this.particles.length == this._capacity) {
  119. break;
  120. }
  121. var particle = new BABYLON.Particle();
  122. this.particles.push(particle);
  123. var emitPower = randomNumber(this.minEmitPower, this.maxEmitPower);
  124. var randX = randomNumber(this.direction1.x, this.direction2.x);
  125. var randY = randomNumber(this.direction1.y, this.direction2.y);
  126. var randZ = randomNumber(this.direction1.z, this.direction2.z);
  127. particle.direction = BABYLON.Vector3.TransformNormal(new BABYLON.Vector3(randX, randY, randZ).scale(emitPower), worldMatrix);
  128. particle.lifeTime = randomNumber(this.minLifeTime, this.maxLifeTime);
  129. particle.size = randomNumber(this.minSize, this.maxSize);
  130. particle.angularSpeed = randomNumber(this.minAngularSpeed, this.maxAngularSpeed);
  131. randX = randomNumber(this.minEmitBox.x, this.maxEmitBox.x);
  132. randY = randomNumber(this.minEmitBox.y, this.maxEmitBox.y);
  133. randZ = randomNumber(this.minEmitBox.z, this.maxEmitBox.z);
  134. var dispatch = new BABYLON.Vector3(randX, randY, randZ);
  135. particle.position = BABYLON.Vector3.TransformCoordinates(dispatch, worldMatrix);
  136. var step = randomNumber(0, 1.0);
  137. var startColor = BABYLON.Color4.Lerp(this.color1, this.color2, step);
  138. var deadColor = this.colorDead;
  139. startColor.a = 1.0;
  140. deadColor.a = this.deadAlpha;
  141. particle.color = startColor;
  142. var diff = deadColor.subtract(startColor);
  143. particle.colorStep = diff.scale(1.0 / particle.lifeTime);
  144. }
  145. };
  146. BABYLON.ParticleSystem.prototype._getEffect = function () {
  147. var defines = [];
  148. if (BABYLON.clipPlane) {
  149. defines.push("#define CLIPPLANE");
  150. }
  151. // Effect
  152. var join = defines.join("\n");
  153. if (this._cachedDefines != join) {
  154. this._cachedDefines = join;
  155. this._effect = this._scene.getEngine().createEffect("particles",
  156. ["position", "color", "options"],
  157. ["invView", "view", "projection", "vClipPlane", "textureMask"],
  158. ["diffuseSampler"], join);
  159. }
  160. return this._effect;
  161. };
  162. BABYLON.ParticleSystem.prototype.animate = function () {
  163. if (!this._started)
  164. return;
  165. var effect = this._getEffect();
  166. // Check
  167. if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady())
  168. return;
  169. this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
  170. // determine the number of particles we need to create
  171. var emitCout;
  172. if (this.manualEmitCount > -1) {
  173. emitCout = this.manualEmitCount;
  174. this.manualEmitCount = 0;
  175. } else {
  176. emitCout = this.emitRate;
  177. }
  178. var newParticles = ((emitCout * this._scaledUpdateSpeed) >> 0);
  179. this._newPartsExcess += emitCout * this._scaledUpdateSpeed - newParticles;
  180. if (this._newPartsExcess > 1.0) {
  181. newParticles += this._newPartsExcess >> 0;
  182. this._newPartsExcess -= this._newPartsExcess >> 0;
  183. }
  184. this._alive = false;
  185. if (!this._stopped) {
  186. this._actualFrame += this._scaledUpdateSpeed;
  187. if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration)
  188. this.stop();
  189. } else {
  190. newParticles = 0;
  191. }
  192. this._update(newParticles);
  193. // Stopped?
  194. if (this._stopped) {
  195. if (!this._alive) {
  196. this._started = false;
  197. if (this.disposeOnStop) {
  198. this._scene._toBeDisposed.push(this);
  199. }
  200. }
  201. }
  202. // Update VBO
  203. var vertices = [];
  204. for (var index = 0; index < this.particles.length; index++) {
  205. var particle = this.particles[index];
  206. appendParticleVertex(particle, vertices, 0, 0);
  207. appendParticleVertex(particle, vertices, 1, 0);
  208. appendParticleVertex(particle, vertices, 1, 1);
  209. appendParticleVertex(particle, vertices, 0, 1);
  210. }
  211. var engine = this._scene.getEngine();
  212. engine.updateDynamicVertexBuffer(this._vertexBuffer, vertices);
  213. };
  214. BABYLON.ParticleSystem.prototype.render = function () {
  215. var effect = this._getEffect();
  216. // Check
  217. if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady() || !this.particles.length)
  218. return 0;
  219. var engine = this._scene.getEngine();
  220. // Render
  221. engine.enableEffect(effect);
  222. var viewMatrix = this._scene.getViewMatrix();
  223. effect.setTexture("diffuseSampler", this.particleTexture);
  224. effect.setMatrix("view", viewMatrix);
  225. effect.setMatrix("projection", this._scene.getProjectionMatrix());
  226. effect.setFloat4("textureMask", this.textureMask.r, this.textureMask.g, this.textureMask.b, this.textureMask.a);
  227. if (BABYLON.clipPlane) {
  228. var invView = viewMatrix.clone();
  229. invView.invert();
  230. effect.setMatrix("invView", invView);
  231. effect.setFloat4("vClipPlane", BABYLON.clipPlane.normal.x, BABYLON.clipPlane.normal.y, BABYLON.clipPlane.normal.z, BABYLON.clipPlane.d);
  232. }
  233. // VBOs
  234. engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
  235. // Draw order
  236. if (this.blendMode === BABYLON.ParticleSystem.BLENDMODE_ONEONE) {
  237. engine.setAlphaMode(BABYLON.Engine.ALPHA_ADD);
  238. } else {
  239. engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
  240. }
  241. engine.draw(true, 0, this.particles.length * 6);
  242. engine.setAlphaMode(BABYLON.Engine.ALPHA_DISABLE);
  243. return this.particles.length;
  244. };
  245. BABYLON.ParticleSystem.prototype.dispose = function () {
  246. if (this._vertexBuffer) {
  247. //this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
  248. this._vertexBuffer = null;
  249. }
  250. if (this._indexBuffer) {
  251. this._scene.getEngine()._releaseBuffer(this._indexBuffer);
  252. this._indexBuffer = null;
  253. }
  254. if (this.particleTexture) {
  255. this.particleTexture.dispose();
  256. this.particleTexture = null;
  257. }
  258. // Remove from scene
  259. var index = this._scene.particleSystems.indexOf(this);
  260. this._scene.particleSystems.splice(index, 1);
  261. // Callback
  262. if (this.onDispose) {
  263. this.onDispose();
  264. }
  265. };
  266. // Clone
  267. BABYLON.ParticleSystem.prototype.clone = function(name, newEmitter) {
  268. var result = new BABYLON.ParticleSystem(name, this._capacity, this._scene);
  269. BABYLON.Tools.DeepCopy(this, result, ["particles"], ["_vertexDeclaration", "_vertexStrideSize"]);
  270. if (newEmitter === undefined) {
  271. newEmitter = this.emitter;
  272. }
  273. result.emitter = newEmitter;
  274. if (this.particleTexture) {
  275. result.particleTexture = new BABYLON.Texture(this.particleTexture.name, this._scene);
  276. }
  277. result.start();
  278. return result;
  279. };
  280. // Statics
  281. BABYLON.ParticleSystem.BLENDMODE_ONEONE = 0;
  282. BABYLON.ParticleSystem.BLENDMODE_STANDARD = 1;
  283. })();