babylon.solidParticleSystem.js 44 KB

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