babylon.solidParticleSystem.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. var BABYLON;
  2. (function (BABYLON) {
  3. /**
  4. * Full documentation here : http://doc.babylonjs.com/overviews/Solid_Particle_System
  5. */
  6. var SolidParticleSystem = (function () {
  7. /**
  8. * Creates a SPS (Solid Particle System) object.
  9. * `name` (String) is the SPS name, this will be the underlying mesh name.
  10. * `scene` (Scene) is the scene in which the SPS is added.
  11. * `updatable` (default true) : if the SPS must be updatable or immutable.
  12. * `isPickable` (default false) : if the solid particles must be pickable.
  13. */
  14. function SolidParticleSystem(name, scene, options) {
  15. // public members
  16. /**
  17. * The SPS array of Solid Particle objects. Just access each particle as with any classic array.
  18. * Example : var p = SPS.particles[i];
  19. */
  20. this.particles = new Array();
  21. /**
  22. * The SPS total number of particles. Read only. Use SPS.counter instead if you need to set your own value.
  23. */
  24. this.nbParticles = 0;
  25. /**
  26. * If the particles must ever face the camera (default false). Useful for planar particles.
  27. */
  28. this.billboard = false;
  29. /**
  30. * This a counter ofr your own usage. It's not set by any SPS functions.
  31. */
  32. this.counter = 0;
  33. /**
  34. * This empty object is intended to store some SPS specific or temporary values in order to lower the Garbage Collector activity.
  35. * Please read : http://doc.babylonjs.com/overviews/Solid_Particle_System#garbage-collector-concerns
  36. */
  37. this.vars = {};
  38. this._positions = new Array();
  39. this._indices = new Array();
  40. this._normals = new Array();
  41. this._colors = new Array();
  42. this._uvs = new Array();
  43. this._index = 0; // indices index
  44. this._updatable = true;
  45. this._pickable = false;
  46. this._isVisibilityBoxLocked = false;
  47. this._alwaysVisible = false;
  48. this._shapeCounter = 0;
  49. this._copy = new BABYLON.SolidParticle(null, null, null, null, null);
  50. this._color = new BABYLON.Color4(0, 0, 0, 0);
  51. this._computeParticleColor = true;
  52. this._computeParticleTexture = true;
  53. this._computeParticleRotation = true;
  54. this._computeParticleVertex = false;
  55. this._computeBoundingBox = false;
  56. this._cam_axisZ = BABYLON.Vector3.Zero();
  57. this._cam_axisY = BABYLON.Vector3.Zero();
  58. this._cam_axisX = BABYLON.Vector3.Zero();
  59. this._axisX = BABYLON.Axis.X;
  60. this._axisY = BABYLON.Axis.Y;
  61. this._axisZ = BABYLON.Axis.Z;
  62. this._fakeCamPos = BABYLON.Vector3.Zero();
  63. this._rotMatrix = new BABYLON.Matrix();
  64. this._invertMatrix = new BABYLON.Matrix();
  65. this._rotated = BABYLON.Vector3.Zero();
  66. this._quaternion = new BABYLON.Quaternion();
  67. this._vertex = BABYLON.Vector3.Zero();
  68. this._normal = BABYLON.Vector3.Zero();
  69. this._yaw = 0.0;
  70. this._pitch = 0.0;
  71. this._roll = 0.0;
  72. this._halfroll = 0.0;
  73. this._halfpitch = 0.0;
  74. this._halfyaw = 0.0;
  75. this._sinRoll = 0.0;
  76. this._cosRoll = 0.0;
  77. this._sinPitch = 0.0;
  78. this._cosPitch = 0.0;
  79. this._sinYaw = 0.0;
  80. this._cosYaw = 0.0;
  81. this._w = 0.0;
  82. this._minimum = BABYLON.Tmp.Vector3[0];
  83. this._maximum = BABYLON.Tmp.Vector3[1];
  84. this.name = name;
  85. this._scene = scene;
  86. this._camera = scene.activeCamera;
  87. this._pickable = options ? options.isPickable : false;
  88. if (options && options.updatable) {
  89. this._updatable = options.updatable;
  90. }
  91. else {
  92. this._updatable = true;
  93. }
  94. if (this._pickable) {
  95. this.pickedParticles = [];
  96. }
  97. }
  98. /**
  99. * Builds the SPS underlying mesh. Returns a standard Mesh.
  100. * If no model shape was added to the SPS, the returned mesh is just a single triangular plane.
  101. */
  102. SolidParticleSystem.prototype.buildMesh = function () {
  103. if (this.nbParticles === 0) {
  104. var triangle = BABYLON.MeshBuilder.CreateDisc("", { radius: 1, tessellation: 3 }, this._scene);
  105. this.addShape(triangle, 1);
  106. triangle.dispose();
  107. }
  108. this._positions32 = new Float32Array(this._positions);
  109. this._uvs32 = new Float32Array(this._uvs);
  110. this._colors32 = new Float32Array(this._colors);
  111. BABYLON.VertexData.ComputeNormals(this._positions32, this._indices, this._normals);
  112. this._normals32 = new Float32Array(this._normals);
  113. this._fixedNormal32 = new Float32Array(this._normals);
  114. var vertexData = new BABYLON.VertexData();
  115. vertexData.set(this._positions32, BABYLON.VertexBuffer.PositionKind);
  116. vertexData.indices = this._indices;
  117. vertexData.set(this._normals32, BABYLON.VertexBuffer.NormalKind);
  118. if (this._uvs32) {
  119. vertexData.set(this._uvs32, BABYLON.VertexBuffer.UVKind);
  120. ;
  121. }
  122. if (this._colors32) {
  123. vertexData.set(this._colors32, BABYLON.VertexBuffer.ColorKind);
  124. }
  125. var mesh = new BABYLON.Mesh(this.name, this._scene);
  126. vertexData.applyToMesh(mesh, this._updatable);
  127. this.mesh = mesh;
  128. this.mesh.isPickable = this._pickable;
  129. // free memory
  130. this._positions = null;
  131. this._normals = null;
  132. this._uvs = null;
  133. this._colors = null;
  134. if (!this._updatable) {
  135. this.particles.length = 0;
  136. }
  137. return mesh;
  138. };
  139. /**
  140. * Digests the mesh and generates as many solid particles in the system as wanted. Returns the SPS.
  141. * These particles will have the same geometry than the mesh parts and will be positioned at the same localisation than the mesh original places.
  142. * Thus the particles generated from `digest()` have their property `position` set yet.
  143. * `mesh` ( Mesh ) is the mesh to be digested
  144. * `facetNb` (optional integer, default 1) is the number of mesh facets per particle, this parameter is overriden by the parameter `number` if any
  145. * `delta` (optional integer, default 0) is the random extra number of facets per particle , each particle will have between `facetNb` and `facetNb + delta` facets
  146. * `number` (optional positive integer) is the wanted number of particles : each particle is built with `mesh_total_facets / number` facets
  147. */
  148. SolidParticleSystem.prototype.digest = function (mesh, options) {
  149. var size = (options && options.facetNb) || 1;
  150. var number = (options && options.number);
  151. var delta = (options && options.delta) || 0;
  152. var meshPos = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
  153. var meshInd = mesh.getIndices();
  154. var meshUV = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
  155. var meshCol = mesh.getVerticesData(BABYLON.VertexBuffer.ColorKind);
  156. var f = 0; // facet counter
  157. var totalFacets = meshInd.length / 3; // a facet is a triangle, so 3 indices
  158. // compute size from number
  159. if (number) {
  160. number = (number > totalFacets) ? totalFacets : number;
  161. size = Math.round(totalFacets / number);
  162. delta = 0;
  163. }
  164. else {
  165. size = (size > totalFacets) ? totalFacets : size;
  166. }
  167. var facetPos = []; // submesh positions
  168. var facetInd = []; // submesh indices
  169. var facetUV = []; // submesh UV
  170. var facetCol = []; // submesh colors
  171. var barycenter = BABYLON.Tmp.Vector3[0];
  172. var rand;
  173. var sizeO = size;
  174. while (f < totalFacets) {
  175. size = sizeO + Math.floor((1 + delta) * Math.random());
  176. if (f > totalFacets - size) {
  177. size = totalFacets - f;
  178. }
  179. // reset temp arrays
  180. facetPos.length = 0;
  181. facetInd.length = 0;
  182. facetUV.length = 0;
  183. facetCol.length = 0;
  184. // iterate over "size" facets
  185. var fi = 0;
  186. for (var j = f * 3; j < (f + size) * 3; j++) {
  187. facetInd.push(fi);
  188. var i = meshInd[j];
  189. facetPos.push(meshPos[i * 3], meshPos[i * 3 + 1], meshPos[i * 3 + 2]);
  190. if (meshUV) {
  191. facetUV.push(meshUV[i * 2], meshUV[i * 2 + 1]);
  192. }
  193. if (meshCol) {
  194. facetCol.push(meshCol[i * 4], meshCol[i * 4 + 1], meshCol[i * 4 + 2], meshCol[i * 4 + 3]);
  195. }
  196. fi++;
  197. }
  198. // create a model shape for each single particle
  199. var idx = this.nbParticles;
  200. var shape = this._posToShape(facetPos);
  201. var shapeUV = this._uvsToShapeUV(facetUV);
  202. // compute the barycenter of the shape
  203. var v;
  204. for (v = 0; v < shape.length; v++) {
  205. barycenter.addInPlace(shape[v]);
  206. }
  207. barycenter.scaleInPlace(1 / shape.length);
  208. // shift the shape from its barycenter to the origin
  209. for (v = 0; v < shape.length; v++) {
  210. shape[v].subtractInPlace(barycenter);
  211. }
  212. var modelShape = new BABYLON.ModelShape(this._shapeCounter, shape, shapeUV, null, null);
  213. // add the particle in the SPS
  214. this._meshBuilder(this._index, shape, this._positions, facetInd, this._indices, facetUV, this._uvs, facetCol, this._colors, idx, 0, null);
  215. this._addParticle(idx, this._positions.length, modelShape, this._shapeCounter, 0);
  216. // initialize the particle position
  217. this.particles[this.nbParticles].position.addInPlace(barycenter);
  218. this._index += shape.length;
  219. idx++;
  220. this.nbParticles++;
  221. this._shapeCounter++;
  222. f += size;
  223. }
  224. return this;
  225. };
  226. //reset copy
  227. SolidParticleSystem.prototype._resetCopy = function () {
  228. this._copy.position.x = 0;
  229. this._copy.position.y = 0;
  230. this._copy.position.z = 0;
  231. this._copy.rotation.x = 0;
  232. this._copy.rotation.y = 0;
  233. this._copy.rotation.z = 0;
  234. this._copy.rotationQuaternion = null;
  235. this._copy.scaling.x = 1;
  236. this._copy.scaling.y = 1;
  237. this._copy.scaling.z = 1;
  238. this._copy.uvs.x = 0;
  239. this._copy.uvs.y = 0;
  240. this._copy.uvs.z = 1;
  241. this._copy.uvs.w = 1;
  242. this._copy.color = null;
  243. };
  244. // _meshBuilder : inserts the shape model in the global SPS mesh
  245. SolidParticleSystem.prototype._meshBuilder = function (p, shape, positions, meshInd, indices, meshUV, uvs, meshCol, colors, idx, idxInShape, options) {
  246. var i;
  247. var u = 0;
  248. var c = 0;
  249. this._resetCopy();
  250. if (options && options.positionFunction) {
  251. options.positionFunction(this._copy, idx, idxInShape);
  252. }
  253. if (this._copy.rotationQuaternion) {
  254. this._quaternion.copyFrom(this._copy.rotationQuaternion);
  255. }
  256. else {
  257. this._yaw = this._copy.rotation.y;
  258. this._pitch = this._copy.rotation.x;
  259. this._roll = this._copy.rotation.z;
  260. this._quaternionRotationYPR();
  261. }
  262. this._quaternionToRotationMatrix();
  263. for (i = 0; i < shape.length; i++) {
  264. this._vertex.x = shape[i].x;
  265. this._vertex.y = shape[i].y;
  266. this._vertex.z = shape[i].z;
  267. if (options && options.vertexFunction) {
  268. options.vertexFunction(this._copy, this._vertex, i);
  269. }
  270. this._vertex.x *= this._copy.scaling.x;
  271. this._vertex.y *= this._copy.scaling.y;
  272. this._vertex.z *= this._copy.scaling.z;
  273. BABYLON.Vector3.TransformCoordinatesToRef(this._vertex, this._rotMatrix, this._rotated);
  274. positions.push(this._copy.position.x + this._rotated.x, this._copy.position.y + this._rotated.y, this._copy.position.z + this._rotated.z);
  275. if (meshUV) {
  276. uvs.push((this._copy.uvs.z - this._copy.uvs.x) * meshUV[u] + this._copy.uvs.x, (this._copy.uvs.w - this._copy.uvs.y) * meshUV[u + 1] + this._copy.uvs.y);
  277. u += 2;
  278. }
  279. if (this._copy.color) {
  280. this._color = this._copy.color;
  281. }
  282. else if (meshCol && meshCol[c]) {
  283. this._color.r = meshCol[c];
  284. this._color.g = meshCol[c + 1];
  285. this._color.b = meshCol[c + 2];
  286. this._color.a = meshCol[c + 3];
  287. }
  288. else {
  289. this._color.r = 1;
  290. this._color.g = 1;
  291. this._color.b = 1;
  292. this._color.a = 1;
  293. }
  294. colors.push(this._color.r, this._color.g, this._color.b, this._color.a);
  295. c += 4;
  296. }
  297. for (i = 0; i < meshInd.length; i++) {
  298. indices.push(p + meshInd[i]);
  299. }
  300. if (this._pickable) {
  301. var nbfaces = meshInd.length / 3;
  302. for (i = 0; i < nbfaces; i++) {
  303. this.pickedParticles.push({ idx: idx, faceId: i });
  304. }
  305. }
  306. };
  307. // returns a shape array from positions array
  308. SolidParticleSystem.prototype._posToShape = function (positions) {
  309. var shape = [];
  310. for (var i = 0; i < positions.length; i += 3) {
  311. shape.push(new BABYLON.Vector3(positions[i], positions[i + 1], positions[i + 2]));
  312. }
  313. return shape;
  314. };
  315. // returns a shapeUV array from a Vector4 uvs
  316. SolidParticleSystem.prototype._uvsToShapeUV = function (uvs) {
  317. var shapeUV = [];
  318. if (uvs) {
  319. for (var i = 0; i < uvs.length; i++)
  320. shapeUV.push(uvs[i]);
  321. }
  322. return shapeUV;
  323. };
  324. // adds a new particle object in the particles array
  325. SolidParticleSystem.prototype._addParticle = function (idx, idxpos, model, shapeId, idxInShape) {
  326. this.particles.push(new BABYLON.SolidParticle(idx, idxpos, model, shapeId, idxInShape));
  327. };
  328. /**
  329. * Adds some particles to the SPS from the model shape. Returns the shape id.
  330. * Please read the doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#create-an-immutable-sps
  331. * `mesh` is any Mesh object that will be used as a model for the solid particles.
  332. * `nb` (positive integer) the number of particles to be created from this model
  333. * `positionFunction` is an optional javascript function to called for each particle on SPS creation.
  334. * `vertexFunction` is an optional javascript function to called for each vertex of each particle on SPS creation
  335. */
  336. SolidParticleSystem.prototype.addShape = function (mesh, nb, options) {
  337. var meshPos = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
  338. var meshInd = mesh.getIndices();
  339. var meshUV = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
  340. var meshCol = mesh.getVerticesData(BABYLON.VertexBuffer.ColorKind);
  341. var shape = this._posToShape(meshPos);
  342. var shapeUV = this._uvsToShapeUV(meshUV);
  343. var posfunc = options ? options.positionFunction : null;
  344. var vtxfunc = options ? options.vertexFunction : null;
  345. var modelShape = new BABYLON.ModelShape(this._shapeCounter, shape, shapeUV, posfunc, vtxfunc);
  346. // particles
  347. var idx = this.nbParticles;
  348. for (var i = 0; i < nb; i++) {
  349. this._meshBuilder(this._index, shape, this._positions, meshInd, this._indices, meshUV, this._uvs, meshCol, this._colors, idx, i, options);
  350. if (this._updatable) {
  351. this._addParticle(idx, this._positions.length, modelShape, this._shapeCounter, i);
  352. }
  353. this._index += shape.length;
  354. idx++;
  355. }
  356. this.nbParticles += nb;
  357. this._shapeCounter++;
  358. return this._shapeCounter - 1;
  359. };
  360. // rebuilds a particle back to its just built status : if needed, recomputes the custom positions and vertices
  361. SolidParticleSystem.prototype._rebuildParticle = function (particle) {
  362. this._resetCopy();
  363. if (particle._model._positionFunction) {
  364. particle._model._positionFunction(this._copy, particle.idx, particle.idxInShape);
  365. }
  366. if (this._copy.rotationQuaternion) {
  367. this._quaternion.copyFrom(this._copy.rotationQuaternion);
  368. }
  369. else {
  370. this._yaw = this._copy.rotation.y;
  371. this._pitch = this._copy.rotation.x;
  372. this._roll = this._copy.rotation.z;
  373. this._quaternionRotationYPR();
  374. }
  375. this._quaternionToRotationMatrix();
  376. this._shape = particle._model._shape;
  377. for (var pt = 0; pt < this._shape.length; pt++) {
  378. this._vertex.x = this._shape[pt].x;
  379. this._vertex.y = this._shape[pt].y;
  380. this._vertex.z = this._shape[pt].z;
  381. if (particle._model._vertexFunction) {
  382. particle._model._vertexFunction(this._copy, this._vertex, pt); // recall to stored vertexFunction
  383. }
  384. this._vertex.x *= this._copy.scaling.x;
  385. this._vertex.y *= this._copy.scaling.y;
  386. this._vertex.z *= this._copy.scaling.z;
  387. BABYLON.Vector3.TransformCoordinatesToRef(this._vertex, this._rotMatrix, this._rotated);
  388. this._positions32[particle._pos + pt * 3] = this._copy.position.x + this._rotated.x;
  389. this._positions32[particle._pos + pt * 3 + 1] = this._copy.position.y + this._rotated.y;
  390. this._positions32[particle._pos + pt * 3 + 2] = this._copy.position.z + this._rotated.z;
  391. }
  392. particle.position.x = 0;
  393. particle.position.y = 0;
  394. particle.position.z = 0;
  395. particle.rotation.x = 0;
  396. particle.rotation.y = 0;
  397. particle.rotation.z = 0;
  398. particle.rotationQuaternion = null;
  399. particle.scaling.x = 1;
  400. particle.scaling.y = 1;
  401. particle.scaling.z = 1;
  402. };
  403. /**
  404. * Rebuilds the whole mesh and updates the VBO : custom positions and vertices are recomputed if needed.
  405. */
  406. SolidParticleSystem.prototype.rebuildMesh = function () {
  407. for (var p = 0; p < this.particles.length; p++) {
  408. this._rebuildParticle(this.particles[p]);
  409. }
  410. this.mesh.updateVerticesData(BABYLON.VertexBuffer.PositionKind, this._positions32, false, false);
  411. };
  412. /**
  413. * Sets all the particles : this method actually really updates the mesh according to the particle positions, rotations, colors, textures, etc.
  414. * This method calls `updateParticle()` for each particle of the SPS.
  415. * For an animated SPS, it is usually called within the render loop.
  416. * @param start The particle index in the particle array where to start to compute the particle property values _(default 0)_
  417. * @param end The particle index in the particle array where to stop to compute the particle property values _(default nbParticle - 1)_
  418. * @param update If the mesh must be finally updated on this call after all the particle computations _(default true)_
  419. */
  420. SolidParticleSystem.prototype.setParticles = function (start, end, update) {
  421. if (start === void 0) { start = 0; }
  422. if (end === void 0) { end = this.nbParticles - 1; }
  423. if (update === void 0) { update = true; }
  424. if (!this._updatable) {
  425. return;
  426. }
  427. // custom beforeUpdate
  428. this.beforeUpdateParticles(start, end, update);
  429. this._cam_axisX.x = 1;
  430. this._cam_axisX.y = 0;
  431. this._cam_axisX.z = 0;
  432. this._cam_axisY.x = 0;
  433. this._cam_axisY.y = 1;
  434. this._cam_axisY.z = 0;
  435. this._cam_axisZ.x = 0;
  436. this._cam_axisZ.y = 0;
  437. this._cam_axisZ.z = 1;
  438. // if the particles will always face the camera
  439. if (this.billboard) {
  440. // compute a fake camera position : un-rotate the camera position by the current mesh rotation
  441. this._yaw = this.mesh.rotation.y;
  442. this._pitch = this.mesh.rotation.x;
  443. this._roll = this.mesh.rotation.z;
  444. this._quaternionRotationYPR();
  445. this._quaternionToRotationMatrix();
  446. this._rotMatrix.invertToRef(this._invertMatrix);
  447. BABYLON.Vector3.TransformCoordinatesToRef(this._camera.globalPosition, this._invertMatrix, this._fakeCamPos);
  448. // set two orthogonal vectors (_cam_axisX and and _cam_axisY) to the cam-mesh axis (_cam_axisZ)
  449. (this._fakeCamPos).subtractToRef(this.mesh.position, this._cam_axisZ);
  450. BABYLON.Vector3.CrossToRef(this._cam_axisZ, this._axisX, this._cam_axisY);
  451. BABYLON.Vector3.CrossToRef(this._cam_axisZ, this._cam_axisY, this._cam_axisX);
  452. this._cam_axisY.normalize();
  453. this._cam_axisX.normalize();
  454. this._cam_axisZ.normalize();
  455. }
  456. BABYLON.Matrix.IdentityToRef(this._rotMatrix);
  457. var idx = 0;
  458. var index = 0;
  459. var colidx = 0;
  460. var colorIndex = 0;
  461. var uvidx = 0;
  462. var uvIndex = 0;
  463. var pt = 0;
  464. if (this._computeBoundingBox) {
  465. BABYLON.Vector3.FromFloatsToRef(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, this._minimum);
  466. BABYLON.Vector3.FromFloatsToRef(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, this._maximum);
  467. }
  468. // particle loop
  469. end = (end > this.nbParticles - 1) ? this.nbParticles - 1 : end;
  470. for (var p = start; p <= end; p++) {
  471. this._particle = this.particles[p];
  472. this._shape = this._particle._model._shape;
  473. this._shapeUV = this._particle._model._shapeUV;
  474. // call to custom user function to update the particle properties
  475. this.updateParticle(this._particle);
  476. if (this._particle.isVisible) {
  477. // particle rotation matrix
  478. if (this.billboard) {
  479. this._particle.rotation.x = 0.0;
  480. this._particle.rotation.y = 0.0;
  481. }
  482. if (this._computeParticleRotation) {
  483. if (this._particle.rotationQuaternion) {
  484. this._quaternion.copyFrom(this._particle.rotationQuaternion);
  485. }
  486. else {
  487. this._yaw = this._particle.rotation.y;
  488. this._pitch = this._particle.rotation.x;
  489. this._roll = this._particle.rotation.z;
  490. this._quaternionRotationYPR();
  491. }
  492. this._quaternionToRotationMatrix();
  493. }
  494. // particle vertex loop
  495. for (pt = 0; pt < this._shape.length; pt++) {
  496. idx = index + pt * 3;
  497. colidx = colorIndex + pt * 4;
  498. uvidx = uvIndex + pt * 2;
  499. this._vertex.x = this._shape[pt].x;
  500. this._vertex.y = this._shape[pt].y;
  501. this._vertex.z = this._shape[pt].z;
  502. if (this._computeParticleVertex) {
  503. this.updateParticleVertex(this._particle, this._vertex, pt);
  504. }
  505. // positions
  506. this._vertex.x *= this._particle.scaling.x;
  507. this._vertex.y *= this._particle.scaling.y;
  508. this._vertex.z *= this._particle.scaling.z;
  509. this._w = (this._vertex.x * this._rotMatrix.m[3]) + (this._vertex.y * this._rotMatrix.m[7]) + (this._vertex.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
  510. this._rotated.x = ((this._vertex.x * this._rotMatrix.m[0]) + (this._vertex.y * this._rotMatrix.m[4]) + (this._vertex.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
  511. this._rotated.y = ((this._vertex.x * this._rotMatrix.m[1]) + (this._vertex.y * this._rotMatrix.m[5]) + (this._vertex.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
  512. this._rotated.z = ((this._vertex.x * this._rotMatrix.m[2]) + (this._vertex.y * this._rotMatrix.m[6]) + (this._vertex.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
  513. this._positions32[idx] = this._particle.position.x + this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
  514. this._positions32[idx + 1] = this._particle.position.y + this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
  515. this._positions32[idx + 2] = this._particle.position.z + this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
  516. if (this._computeBoundingBox) {
  517. if (this._positions32[idx] < this._minimum.x) {
  518. this._minimum.x = this._positions32[idx];
  519. }
  520. if (this._positions32[idx] > this._maximum.x) {
  521. this._maximum.x = this._positions32[idx];
  522. }
  523. if (this._positions32[idx + 1] < this._minimum.y) {
  524. this._minimum.y = this._positions32[idx + 1];
  525. }
  526. if (this._positions32[idx + 1] > this._maximum.y) {
  527. this._maximum.y = this._positions32[idx + 1];
  528. }
  529. if (this._positions32[idx + 2] < this._minimum.z) {
  530. this._minimum.z = this._positions32[idx + 2];
  531. }
  532. if (this._positions32[idx + 2] > this._maximum.z) {
  533. this._maximum.z = this._positions32[idx + 2];
  534. }
  535. }
  536. // normals : if the particles can't be morphed then just rotate the normals, what if much more faster than ComputeNormals()
  537. if (!this._computeParticleVertex && !this.billboard) {
  538. this._normal.x = this._fixedNormal32[idx];
  539. this._normal.y = this._fixedNormal32[idx + 1];
  540. this._normal.z = this._fixedNormal32[idx + 2];
  541. this._w = (this._normal.x * this._rotMatrix.m[3]) + (this._normal.y * this._rotMatrix.m[7]) + (this._normal.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
  542. this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
  543. this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
  544. this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
  545. this._normals32[idx] = this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
  546. this._normals32[idx + 1] = this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
  547. this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
  548. }
  549. if (this._computeParticleColor) {
  550. this._colors32[colidx] = this._particle.color.r;
  551. this._colors32[colidx + 1] = this._particle.color.g;
  552. this._colors32[colidx + 2] = this._particle.color.b;
  553. this._colors32[colidx + 3] = this._particle.color.a;
  554. }
  555. if (this._computeParticleTexture) {
  556. this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
  557. this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
  558. }
  559. }
  560. }
  561. else {
  562. for (pt = 0; pt < this._shape.length; pt++) {
  563. idx = index + pt * 3;
  564. colidx = colorIndex + pt * 4;
  565. uvidx = uvIndex + pt * 2;
  566. this._positions32[idx] = this._camera.position.x;
  567. this._positions32[idx + 1] = this._camera.position.y;
  568. this._positions32[idx + 2] = this._camera.position.z;
  569. this._normals32[idx] = 0.0;
  570. this._normals32[idx + 1] = 0.0;
  571. this._normals32[idx + 2] = 0.0;
  572. if (this._computeParticleColor) {
  573. this._colors32[colidx] = this._particle.color.r;
  574. this._colors32[colidx + 1] = this._particle.color.g;
  575. this._colors32[colidx + 2] = this._particle.color.b;
  576. this._colors32[colidx + 3] = this._particle.color.a;
  577. }
  578. if (this._computeParticleTexture) {
  579. this._uvs32[uvidx] = this._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
  580. this._uvs32[uvidx + 1] = this._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
  581. }
  582. }
  583. }
  584. // increment indexes for the next particle
  585. index = idx + 3;
  586. colorIndex = colidx + 4;
  587. uvIndex = uvidx + 2;
  588. }
  589. // if the VBO must be updated
  590. if (update) {
  591. if (this._computeParticleColor) {
  592. this.mesh.updateVerticesData(BABYLON.VertexBuffer.ColorKind, this._colors32, false, false);
  593. }
  594. if (this._computeParticleTexture) {
  595. this.mesh.updateVerticesData(BABYLON.VertexBuffer.UVKind, this._uvs32, false, false);
  596. }
  597. this.mesh.updateVerticesData(BABYLON.VertexBuffer.PositionKind, this._positions32, false, false);
  598. if (!this.mesh.areNormalsFrozen) {
  599. if (this._computeParticleVertex || this.billboard) {
  600. // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
  601. BABYLON.VertexData.ComputeNormals(this._positions32, this._indices, this._normals32);
  602. for (var i = 0; i < this._normals32.length; i++) {
  603. this._fixedNormal32[i] = this._normals32[i];
  604. }
  605. }
  606. this.mesh.updateVerticesData(BABYLON.VertexBuffer.NormalKind, this._normals32, false, false);
  607. }
  608. }
  609. if (this._computeBoundingBox) {
  610. this.mesh._boundingInfo = new BABYLON.BoundingInfo(this._minimum, this._maximum);
  611. this.mesh._boundingInfo.update(this.mesh._worldMatrix);
  612. }
  613. this.afterUpdateParticles(start, end, update);
  614. };
  615. SolidParticleSystem.prototype._quaternionRotationYPR = function () {
  616. this._halfroll = this._roll * 0.5;
  617. this._halfpitch = this._pitch * 0.5;
  618. this._halfyaw = this._yaw * 0.5;
  619. this._sinRoll = Math.sin(this._halfroll);
  620. this._cosRoll = Math.cos(this._halfroll);
  621. this._sinPitch = Math.sin(this._halfpitch);
  622. this._cosPitch = Math.cos(this._halfpitch);
  623. this._sinYaw = Math.sin(this._halfyaw);
  624. this._cosYaw = Math.cos(this._halfyaw);
  625. this._quaternion.x = (this._cosYaw * this._sinPitch * this._cosRoll) + (this._sinYaw * this._cosPitch * this._sinRoll);
  626. this._quaternion.y = (this._sinYaw * this._cosPitch * this._cosRoll) - (this._cosYaw * this._sinPitch * this._sinRoll);
  627. this._quaternion.z = (this._cosYaw * this._cosPitch * this._sinRoll) - (this._sinYaw * this._sinPitch * this._cosRoll);
  628. this._quaternion.w = (this._cosYaw * this._cosPitch * this._cosRoll) + (this._sinYaw * this._sinPitch * this._sinRoll);
  629. };
  630. SolidParticleSystem.prototype._quaternionToRotationMatrix = function () {
  631. this._rotMatrix.m[0] = 1.0 - (2.0 * (this._quaternion.y * this._quaternion.y + this._quaternion.z * this._quaternion.z));
  632. this._rotMatrix.m[1] = 2.0 * (this._quaternion.x * this._quaternion.y + this._quaternion.z * this._quaternion.w);
  633. this._rotMatrix.m[2] = 2.0 * (this._quaternion.z * this._quaternion.x - this._quaternion.y * this._quaternion.w);
  634. this._rotMatrix.m[3] = 0;
  635. this._rotMatrix.m[4] = 2.0 * (this._quaternion.x * this._quaternion.y - this._quaternion.z * this._quaternion.w);
  636. this._rotMatrix.m[5] = 1.0 - (2.0 * (this._quaternion.z * this._quaternion.z + this._quaternion.x * this._quaternion.x));
  637. this._rotMatrix.m[6] = 2.0 * (this._quaternion.y * this._quaternion.z + this._quaternion.x * this._quaternion.w);
  638. this._rotMatrix.m[7] = 0;
  639. this._rotMatrix.m[8] = 2.0 * (this._quaternion.z * this._quaternion.x + this._quaternion.y * this._quaternion.w);
  640. this._rotMatrix.m[9] = 2.0 * (this._quaternion.y * this._quaternion.z - this._quaternion.x * this._quaternion.w);
  641. this._rotMatrix.m[10] = 1.0 - (2.0 * (this._quaternion.y * this._quaternion.y + this._quaternion.x * this._quaternion.x));
  642. this._rotMatrix.m[11] = 0;
  643. this._rotMatrix.m[12] = 0;
  644. this._rotMatrix.m[13] = 0;
  645. this._rotMatrix.m[14] = 0;
  646. this._rotMatrix.m[15] = 1.0;
  647. };
  648. /**
  649. * Disposes the SPS
  650. */
  651. SolidParticleSystem.prototype.dispose = function () {
  652. this.mesh.dispose();
  653. this.vars = null;
  654. // drop references to internal big arrays for the GC
  655. this._positions = null;
  656. this._indices = null;
  657. this._normals = null;
  658. this._uvs = null;
  659. this._colors = null;
  660. this._positions32 = null;
  661. this._normals32 = null;
  662. this._fixedNormal32 = null;
  663. this._uvs32 = null;
  664. this._colors32 = null;
  665. this.pickedParticles = null;
  666. };
  667. /**
  668. * Visibilty helper : Recomputes the visible size according to the mesh bounding box
  669. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  670. */
  671. SolidParticleSystem.prototype.refreshVisibleSize = function () {
  672. if (!this._isVisibilityBoxLocked) {
  673. this.mesh.refreshBoundingInfo();
  674. }
  675. };
  676. /**
  677. * Visibility helper : Sets the size of a visibility box, this sets the underlying mesh bounding box.
  678. * @param size the size (float) of the visibility box
  679. * note : this doesn't lock the SPS mesh bounding box.
  680. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  681. */
  682. SolidParticleSystem.prototype.setVisibilityBox = function (size) {
  683. var vis = size / 2;
  684. this.mesh._boundingInfo = new BABYLON.BoundingInfo(new BABYLON.Vector3(-vis, -vis, -vis), new BABYLON.Vector3(vis, vis, vis));
  685. };
  686. Object.defineProperty(SolidParticleSystem.prototype, "isAlwaysVisible", {
  687. // getter and setter
  688. get: function () {
  689. return this._alwaysVisible;
  690. },
  691. /**
  692. * Sets the SPS as always visible or not
  693. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  694. */
  695. set: function (val) {
  696. this._alwaysVisible = val;
  697. this.mesh.alwaysSelectAsActiveMesh = val;
  698. },
  699. enumerable: true,
  700. configurable: true
  701. });
  702. Object.defineProperty(SolidParticleSystem.prototype, "isVisibilityBoxLocked", {
  703. get: function () {
  704. return this._isVisibilityBoxLocked;
  705. },
  706. /**
  707. * Sets the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
  708. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  709. */
  710. set: function (val) {
  711. this._isVisibilityBoxLocked = val;
  712. this.mesh.getBoundingInfo().isLocked = val;
  713. },
  714. enumerable: true,
  715. configurable: true
  716. });
  717. Object.defineProperty(SolidParticleSystem.prototype, "computeParticleRotation", {
  718. // getters
  719. get: function () {
  720. return this._computeParticleRotation;
  721. },
  722. // Optimizer setters
  723. /**
  724. * Tells to `setParticles()` to compute the particle rotations or not.
  725. * Default value : true. The SPS is faster when it's set to false.
  726. * Note : the particle rotations aren't stored values, so setting `computeParticleRotation` to false will prevents the particle to rotate.
  727. */
  728. set: function (val) {
  729. this._computeParticleRotation = val;
  730. },
  731. enumerable: true,
  732. configurable: true
  733. });
  734. Object.defineProperty(SolidParticleSystem.prototype, "computeParticleColor", {
  735. get: function () {
  736. return this._computeParticleColor;
  737. },
  738. /**
  739. * Tells to `setParticles()` to compute the particle colors or not.
  740. * Default value : true. The SPS is faster when it's set to false.
  741. * Note : the particle colors are stored values, so setting `computeParticleColor` to false will keep yet the last colors set.
  742. */
  743. set: function (val) {
  744. this._computeParticleColor = val;
  745. },
  746. enumerable: true,
  747. configurable: true
  748. });
  749. Object.defineProperty(SolidParticleSystem.prototype, "computeParticleTexture", {
  750. get: function () {
  751. return this._computeParticleTexture;
  752. },
  753. /**
  754. * Tells to `setParticles()` to compute the particle textures or not.
  755. * Default value : true. The SPS is faster when it's set to false.
  756. * Note : the particle textures are stored values, so setting `computeParticleTexture` to false will keep yet the last colors set.
  757. */
  758. set: function (val) {
  759. this._computeParticleTexture = val;
  760. },
  761. enumerable: true,
  762. configurable: true
  763. });
  764. Object.defineProperty(SolidParticleSystem.prototype, "computeParticleVertex", {
  765. get: function () {
  766. return this._computeParticleVertex;
  767. },
  768. /**
  769. * Tells to `setParticles()` to call the vertex function for each vertex of each particle, or not.
  770. * Default value : false. The SPS is faster when it's set to false.
  771. * Note : the particle custom vertex positions aren't stored values.
  772. */
  773. set: function (val) {
  774. this._computeParticleVertex = val;
  775. },
  776. enumerable: true,
  777. configurable: true
  778. });
  779. Object.defineProperty(SolidParticleSystem.prototype, "computeBoundingBox", {
  780. get: function () {
  781. return this._computeBoundingBox;
  782. },
  783. /**
  784. * Tells to `setParticles()` to compute or not the mesh bounding box when computing the particle positions.
  785. */
  786. set: function (val) {
  787. this._computeBoundingBox = val;
  788. },
  789. enumerable: true,
  790. configurable: true
  791. });
  792. // =======================================================================
  793. // Particle behavior logic
  794. // these following methods may be overwritten by the user to fit his needs
  795. /**
  796. * This function does nothing. It may be overwritten to set all the particle first values.
  797. * The SPS doesn't call this function, you may have to call it by your own.
  798. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
  799. */
  800. SolidParticleSystem.prototype.initParticles = function () {
  801. };
  802. /**
  803. * This function does nothing. It may be overwritten to recycle a particle.
  804. * The SPS doesn't call this function, you may have to call it by your own.
  805. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
  806. */
  807. SolidParticleSystem.prototype.recycleParticle = function (particle) {
  808. return particle;
  809. };
  810. /**
  811. * Updates a particle : this function should be overwritten by the user.
  812. * It is called on each particle by `setParticles()`. This is the place to code each particle behavior.
  813. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
  814. * ex : just set a particle position or velocity and recycle conditions
  815. */
  816. SolidParticleSystem.prototype.updateParticle = function (particle) {
  817. return particle;
  818. };
  819. /**
  820. * Updates a vertex of a particle : it can be overwritten by the user.
  821. * This will be called on each vertex particle by `setParticles()` if `computeParticleVertex` is set to true only.
  822. * @param particle the current particle
  823. * @param vertex the current index of the current particle
  824. * @param pt the index of the current vertex in the particle shape
  825. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#update-each-particle-shape
  826. * ex : just set a vertex particle position
  827. */
  828. SolidParticleSystem.prototype.updateParticleVertex = function (particle, vertex, pt) {
  829. return vertex;
  830. };
  831. /**
  832. * This will be called before any other treatment by `setParticles()` and will be passed three parameters.
  833. * This does nothing and may be overwritten by the user.
  834. * @param start the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  835. * @param stop the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  836. * @param update the boolean update value actually passed to setParticles()
  837. */
  838. SolidParticleSystem.prototype.beforeUpdateParticles = function (start, stop, update) {
  839. };
  840. /**
  841. * This will be called by `setParticles()` after all the other treatments and just before the actual mesh update.
  842. * This will be passed three parameters.
  843. * This does nothing and may be overwritten by the user.
  844. * @param start the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  845. * @param stop the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  846. * @param update the boolean update value actually passed to setParticles()
  847. */
  848. SolidParticleSystem.prototype.afterUpdateParticles = function (start, stop, update) {
  849. };
  850. return SolidParticleSystem;
  851. })();
  852. BABYLON.SolidParticleSystem = SolidParticleSystem;
  853. })(BABYLON || (BABYLON = {}));