solidParticleSystem.ts 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  1. import { Nullable, IndicesArray, FloatArray } from "types";
  2. import { Color4, Vector3, Matrix, Tmp, Quaternion, Axis } from "Math/math";
  3. import { VertexBuffer } from "Mesh/vertexBuffer";
  4. import { VertexData } from "Mesh/mesh.vertexData";
  5. import { Mesh } from "Mesh/mesh";
  6. import { MeshBuilder } from "Mesh/meshBuilder";
  7. import { Engine } from "Engine/engine";
  8. import { Scene, IDisposable } from "scene";
  9. import { DepthSortedParticle, SolidParticle, ModelShape } from "./solidParticle";
  10. import { TargetCamera } from "Cameras/targetCamera";
  11. import { BoundingInfo } from "Culling/boundingInfo";
  12. const depthSortFunction = (p1: DepthSortedParticle, p2: DepthSortedParticle) => p2.sqDistance - p1.sqDistance;
  13. /**
  14. * The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
  15. *As it is just a mesh, the SPS has all the same properties than any other BJS mesh : not more, not less. It can be scaled, rotated, translated, enlighted, textured, moved, etc.
  16. * The SPS is also a particle system. It provides some methods to manage the particles.
  17. * However it is behavior agnostic. This means it has no emitter, no particle physics, no particle recycler. You have to implement your own behavior.
  18. *
  19. * Full documentation here : http://doc.babylonjs.com/overviews/Solid_Particle_System
  20. */
  21. export class SolidParticleSystem implements IDisposable {
  22. /**
  23. * The SPS array of Solid Particle objects. Just access each particle as with any classic array.
  24. * Example : var p = SPS.particles[i];
  25. */
  26. public particles: SolidParticle[] = new Array<SolidParticle>();
  27. /**
  28. * The SPS total number of particles. Read only. Use SPS.counter instead if you need to set your own value.
  29. */
  30. public nbParticles: number = 0;
  31. /**
  32. * If the particles must ever face the camera (default false). Useful for planar particles.
  33. */
  34. public billboard: boolean = false;
  35. /**
  36. * Recompute normals when adding a shape
  37. */
  38. public recomputeNormals: boolean = true;
  39. /**
  40. * This a counter ofr your own usage. It's not set by any SPS functions.
  41. */
  42. public counter: number = 0;
  43. /**
  44. * The SPS name. This name is also given to the underlying mesh.
  45. */
  46. public name: string;
  47. /**
  48. * The SPS mesh. It's a standard BJS Mesh, so all the methods from the Mesh class are avalaible.
  49. */
  50. public mesh: Mesh;
  51. /**
  52. * This empty object is intended to store some SPS specific or temporary values in order to lower the Garbage Collector activity.
  53. * Please read : http://doc.babylonjs.com/overviews/Solid_Particle_System#garbage-collector-concerns
  54. */
  55. public vars: any = {};
  56. /**
  57. * This array is populated when the SPS is set as 'pickable'.
  58. * Each key of this array is a `faceId` value that you can get from a pickResult object.
  59. * Each element of this array is an object `{idx: int, faceId: int}`.
  60. * `idx` is the picked particle index in the `SPS.particles` array
  61. * `faceId` is the picked face index counted within this particle.
  62. * Please read : http://doc.babylonjs.com/overviews/Solid_Particle_System#pickable-particles
  63. */
  64. public pickedParticles: { idx: number; faceId: number }[];
  65. /**
  66. * This array is populated when `enableDepthSort` is set to true.
  67. * Each element of this array is an instance of the class DepthSortedParticle.
  68. */
  69. public depthSortedParticles: DepthSortedParticle[];
  70. /**
  71. * If the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster). (Internal use only)
  72. * @hidden
  73. */
  74. public _bSphereOnly: boolean = false;
  75. /**
  76. * A number to multiply the boundind sphere radius by in order to reduce it for instance. (Internal use only)
  77. * @hidden
  78. */
  79. public _bSphereRadiusFactor: number = 1.0;
  80. private _scene: Scene;
  81. private _positions: number[] = new Array<number>();
  82. private _indices: number[] = new Array<number>();
  83. private _normals: number[] = new Array<number>();
  84. private _colors: number[] = new Array<number>();
  85. private _uvs: number[] = new Array<number>();
  86. private _indices32: IndicesArray; // used as depth sorted array if depth sort enabled, else used as typed indices
  87. private _positions32: Float32Array; // updated positions for the VBO
  88. private _normals32: Float32Array; // updated normals for the VBO
  89. private _fixedNormal32: Float32Array; // initial normal references
  90. private _colors32: Float32Array;
  91. private _uvs32: Float32Array;
  92. private _index: number = 0; // indices index
  93. private _updatable: boolean = true;
  94. private _pickable: boolean = false;
  95. private _isVisibilityBoxLocked = false;
  96. private _alwaysVisible: boolean = false;
  97. private _depthSort: boolean = false;
  98. private _shapeCounter: number = 0;
  99. private _copy: SolidParticle = new SolidParticle(0, 0, 0, null, 0, 0, this);
  100. private _color: Color4 = new Color4(0, 0, 0, 0);
  101. private _computeParticleColor: boolean = true;
  102. private _computeParticleTexture: boolean = true;
  103. private _computeParticleRotation: boolean = true;
  104. private _computeParticleVertex: boolean = false;
  105. private _computeBoundingBox: boolean = false;
  106. private _depthSortParticles: boolean = true;
  107. private _camera: TargetCamera;
  108. private _mustUnrotateFixedNormals = false;
  109. private _particlesIntersect: boolean = false;
  110. private _needs32Bits: boolean = false;
  111. /**
  112. * Creates a SPS (Solid Particle System) object.
  113. * @param name (String) is the SPS name, this will be the underlying mesh name.
  114. * @param scene (Scene) is the scene in which the SPS is added.
  115. * @param updatable (optional boolean, default true) : if the SPS must be updatable or immutable.
  116. * @param isPickable (optional boolean, default false) : if the solid particles must be pickable.
  117. * @param enableDepthSort (optional boolean, default false) : if the solid particles must be sorted in the geometry according to their distance to the camera.
  118. * @param particleIntersection (optional boolean, default false) : if the solid particle intersections must be computed.
  119. * @param boundingSphereOnly (optional boolean, default false) : if the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster).
  120. * @param bSphereRadiusFactor (optional float, default 1.0) : a number to multiply the boundind sphere radius by in order to reduce it for instance.
  121. * @example bSphereRadiusFactor = 1.0 / Math.sqrt(3.0) => the bounding sphere exactly matches a spherical mesh.
  122. */
  123. constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; enableDepthSort?: boolean; particleIntersection?: boolean; boundingSphereOnly?: boolean; bSphereRadiusFactor?: number }) {
  124. this.name = name;
  125. this._scene = scene || Engine.LastCreatedScene;
  126. this._camera = <TargetCamera>scene.activeCamera;
  127. this._pickable = options ? <boolean>options.isPickable : false;
  128. this._depthSort = options ? <boolean>options.enableDepthSort : false;
  129. this._particlesIntersect = options ? <boolean>options.particleIntersection : false;
  130. this._bSphereOnly = options ? <boolean>options.boundingSphereOnly : false;
  131. this._bSphereRadiusFactor = (options && options.bSphereRadiusFactor) ? options.bSphereRadiusFactor : 1.0;
  132. if (options && options.updatable !== undefined) {
  133. this._updatable = options.updatable;
  134. } else {
  135. this._updatable = true;
  136. }
  137. if (this._pickable) {
  138. this.pickedParticles = [];
  139. }
  140. if (this._depthSort) {
  141. this.depthSortedParticles = [];
  142. }
  143. }
  144. /**
  145. * Builds the SPS underlying mesh. Returns a standard Mesh.
  146. * If no model shape was added to the SPS, the returned mesh is just a single triangular plane.
  147. * @returns the created mesh
  148. */
  149. public buildMesh(): Mesh {
  150. if (this.nbParticles === 0) {
  151. var triangle = MeshBuilder.CreateDisc("", { radius: 1, tessellation: 3 }, this._scene);
  152. this.addShape(triangle, 1);
  153. triangle.dispose();
  154. }
  155. this._indices32 = (this._needs32Bits) ? new Uint32Array(this._indices) : new Uint16Array(this._indices);
  156. this._positions32 = new Float32Array(this._positions);
  157. this._uvs32 = new Float32Array(this._uvs);
  158. this._colors32 = new Float32Array(this._colors);
  159. if (this.recomputeNormals) {
  160. VertexData.ComputeNormals(this._positions32, this._indices32, this._normals);
  161. }
  162. this._normals32 = new Float32Array(this._normals);
  163. this._fixedNormal32 = new Float32Array(this._normals);
  164. if (this._mustUnrotateFixedNormals) { // the particles could be created already rotated in the mesh with a positionFunction
  165. this._unrotateFixedNormals();
  166. }
  167. var vertexData = new VertexData();
  168. vertexData.indices = (this._depthSort) ? this._indices : this._indices32;
  169. vertexData.set(this._positions32, VertexBuffer.PositionKind);
  170. vertexData.set(this._normals32, VertexBuffer.NormalKind);
  171. if (this._uvs32.length > 0) {
  172. vertexData.set(this._uvs32, VertexBuffer.UVKind);
  173. }
  174. if (this._colors32.length > 0) {
  175. vertexData.set(this._colors32, VertexBuffer.ColorKind);
  176. }
  177. var mesh = new Mesh(this.name, this._scene);
  178. vertexData.applyToMesh(mesh, this._updatable);
  179. this.mesh = mesh;
  180. this.mesh.isPickable = this._pickable;
  181. // free memory
  182. if (!this._depthSort) {
  183. (<any>this._indices) = null;
  184. }
  185. (<any>this._positions) = null;
  186. (<any>this._normals) = null;
  187. (<any>this._uvs) = null;
  188. (<any>this._colors) = null;
  189. if (!this._updatable) {
  190. this.particles.length = 0;
  191. }
  192. return mesh;
  193. }
  194. /**
  195. * Digests the mesh and generates as many solid particles in the system as wanted. Returns the SPS.
  196. * These particles will have the same geometry than the mesh parts and will be positioned at the same localisation than the mesh original places.
  197. * Thus the particles generated from `digest()` have their property `position` set yet.
  198. * @param mesh ( Mesh ) is the mesh to be digested
  199. * @param options {facetNb} (optional integer, default 1) is the number of mesh facets per particle, this parameter is overriden by the parameter `number` if any
  200. * {delta} (optional integer, default 0) is the random extra number of facets per particle , each particle will have between `facetNb` and `facetNb + delta` facets
  201. * {number} (optional positive integer) is the wanted number of particles : each particle is built with `mesh_total_facets / number` facets
  202. * @returns the current SPS
  203. */
  204. public digest(mesh: Mesh, options?: { facetNb?: number; number?: number; delta?: number }): SolidParticleSystem {
  205. var size: number = (options && options.facetNb) || 1;
  206. var number: number = (options && options.number) || 0;
  207. var delta: number = (options && options.delta) || 0;
  208. var meshPos = <FloatArray>mesh.getVerticesData(VertexBuffer.PositionKind);
  209. var meshInd = <IndicesArray>mesh.getIndices();
  210. var meshUV = <FloatArray>mesh.getVerticesData(VertexBuffer.UVKind);
  211. var meshCol = <FloatArray>mesh.getVerticesData(VertexBuffer.ColorKind);
  212. var meshNor = <FloatArray>mesh.getVerticesData(VertexBuffer.NormalKind);
  213. var f: number = 0; // facet counter
  214. var totalFacets: number = meshInd.length / 3; // a facet is a triangle, so 3 indices
  215. // compute size from number
  216. if (number) {
  217. number = (number > totalFacets) ? totalFacets : number;
  218. size = Math.round(totalFacets / number);
  219. delta = 0;
  220. } else {
  221. size = (size > totalFacets) ? totalFacets : size;
  222. }
  223. var facetPos: number[] = []; // submesh positions
  224. var facetInd: number[] = []; // submesh indices
  225. var facetUV: number[] = []; // submesh UV
  226. var facetCol: number[] = []; // submesh colors
  227. var barycenter: Vector3 = Vector3.Zero();
  228. var sizeO: number = size;
  229. while (f < totalFacets) {
  230. size = sizeO + Math.floor((1 + delta) * Math.random());
  231. if (f > totalFacets - size) {
  232. size = totalFacets - f;
  233. }
  234. // reset temp arrays
  235. facetPos.length = 0;
  236. facetInd.length = 0;
  237. facetUV.length = 0;
  238. facetCol.length = 0;
  239. // iterate over "size" facets
  240. var fi: number = 0;
  241. for (var j = f * 3; j < (f + size) * 3; j++) {
  242. facetInd.push(fi);
  243. var i: number = meshInd[j];
  244. facetPos.push(meshPos[i * 3], meshPos[i * 3 + 1], meshPos[i * 3 + 2]);
  245. if (meshUV) {
  246. facetUV.push(meshUV[i * 2], meshUV[i * 2 + 1]);
  247. }
  248. if (meshCol) {
  249. facetCol.push(meshCol[i * 4], meshCol[i * 4 + 1], meshCol[i * 4 + 2], meshCol[i * 4 + 3]);
  250. }
  251. fi++;
  252. }
  253. // create a model shape for each single particle
  254. var idx: number = this.nbParticles;
  255. var shape: Vector3[] = this._posToShape(facetPos);
  256. var shapeUV: number[] = this._uvsToShapeUV(facetUV);
  257. // compute the barycenter of the shape
  258. var v: number;
  259. for (v = 0; v < shape.length; v++) {
  260. barycenter.addInPlace(shape[v]);
  261. }
  262. barycenter.scaleInPlace(1 / shape.length);
  263. // shift the shape from its barycenter to the origin
  264. for (v = 0; v < shape.length; v++) {
  265. shape[v].subtractInPlace(barycenter);
  266. }
  267. var bInfo;
  268. if (this._particlesIntersect) {
  269. bInfo = new BoundingInfo(barycenter, barycenter);
  270. }
  271. var modelShape = new ModelShape(this._shapeCounter, shape, size * 3, shapeUV, null, null);
  272. // add the particle in the SPS
  273. var currentPos = this._positions.length;
  274. var currentInd = this._indices.length;
  275. this._meshBuilder(this._index, shape, this._positions, facetInd, this._indices, facetUV, this._uvs, facetCol, this._colors, meshNor, this._normals, idx, 0, null);
  276. this._addParticle(idx, currentPos, currentInd, modelShape, this._shapeCounter, 0, bInfo);
  277. // initialize the particle position
  278. this.particles[this.nbParticles].position.addInPlace(barycenter);
  279. this._index += shape.length;
  280. idx++;
  281. this.nbParticles++;
  282. this._shapeCounter++;
  283. f += size;
  284. }
  285. return this;
  286. }
  287. // unrotate the fixed normals in case the mesh was built with pre-rotated particles, ex : use of positionFunction in addShape()
  288. private _unrotateFixedNormals() {
  289. var index = 0;
  290. var idx = 0;
  291. const tmpNormal = Tmp.Vector3[0];
  292. const quaternion = Tmp.Quaternion[0];
  293. const invertedRotMatrix = Tmp.Matrix[0];
  294. for (var p = 0; p < this.particles.length; p++) {
  295. const particle = this.particles[p];
  296. const shape = particle._model._shape;
  297. // computing the inverse of the rotation matrix from the quaternion
  298. // is equivalent to computing the matrix of the inverse quaternion, i.e of the conjugate quaternion
  299. if (particle.rotationQuaternion) {
  300. particle.rotationQuaternion.conjugateToRef(quaternion);
  301. }
  302. else {
  303. const rotation = particle.rotation;
  304. Quaternion.RotationYawPitchRollToRef(rotation.y, rotation.x, rotation.z, quaternion);
  305. quaternion.conjugateInPlace();
  306. }
  307. quaternion.toRotationMatrix(invertedRotMatrix);
  308. for (var pt = 0; pt < shape.length; pt++) {
  309. idx = index + pt * 3;
  310. Vector3.TransformNormalFromFloatsToRef(this._normals32[idx], this._normals32[idx + 1], this._normals32[idx + 2], invertedRotMatrix, tmpNormal);
  311. tmpNormal.toArray(this._fixedNormal32, idx);
  312. }
  313. index = idx + 3;
  314. }
  315. }
  316. //reset copy
  317. private _resetCopy() {
  318. const copy = this._copy;
  319. copy.position.setAll(0);
  320. copy.rotation.setAll(0);
  321. copy.rotationQuaternion = null;
  322. copy.scaling.setAll(1);
  323. copy.uvs.copyFromFloats(0.0, 0.0, 1.0, 1.0);
  324. copy.color = null;
  325. copy.translateFromPivot = false;
  326. }
  327. // _meshBuilder : inserts the shape model in the global SPS mesh
  328. private _meshBuilder(p: number, shape: Vector3[], positions: number[], meshInd: IndicesArray, indices: number[], meshUV: number[] | Float32Array, uvs: number[], meshCol: number[] | Float32Array, colors: number[], meshNor: number[] | Float32Array, normals: number[], idx: number, idxInShape: number, options: any): SolidParticle {
  329. var i;
  330. var u = 0;
  331. var c = 0;
  332. var n = 0;
  333. this._resetCopy();
  334. const copy = this._copy;
  335. if (options && options.positionFunction) { // call to custom positionFunction
  336. options.positionFunction(copy, idx, idxInShape);
  337. this._mustUnrotateFixedNormals = true;
  338. }
  339. const rotMatrix = Tmp.Matrix[0];
  340. const tmpVertex = Tmp.Vector3[0];
  341. const tmpRotated = Tmp.Vector3[1];
  342. const pivotBackTranslation = Tmp.Vector3[2];
  343. const scaledPivot = Tmp.Vector3[3];
  344. copy.getRotationMatrix(rotMatrix);
  345. copy.pivot.multiplyToRef(copy.scaling, scaledPivot);
  346. if (copy.translateFromPivot) {
  347. pivotBackTranslation.setAll(0.0);
  348. }
  349. else {
  350. pivotBackTranslation.copyFrom(scaledPivot);
  351. }
  352. for (i = 0; i < shape.length; i++) {
  353. tmpVertex.copyFrom(shape[i]);
  354. if (options && options.vertexFunction) {
  355. options.vertexFunction(copy, tmpVertex, i);
  356. }
  357. tmpVertex.multiplyInPlace(copy.scaling).subtractInPlace(scaledPivot);
  358. Vector3.TransformCoordinatesToRef(tmpVertex, rotMatrix, tmpRotated);
  359. tmpRotated.addInPlace(pivotBackTranslation).addInPlace(copy.position);
  360. positions.push(tmpRotated.x, tmpRotated.y, tmpRotated.z);
  361. if (meshUV) {
  362. const copyUvs = copy.uvs;
  363. uvs.push((copyUvs.z - copyUvs.x) * meshUV[u] + copyUvs.x, (copyUvs.w - copyUvs.y) * meshUV[u + 1] + copyUvs.y);
  364. u += 2;
  365. }
  366. if (copy.color) {
  367. this._color = copy.color;
  368. } else {
  369. const color = this._color;
  370. if (meshCol && meshCol[c] !== undefined) {
  371. color.r = meshCol[c];
  372. color.g = meshCol[c + 1];
  373. color.b = meshCol[c + 2];
  374. color.a = meshCol[c + 3];
  375. } else {
  376. color.r = 1.0;
  377. color.g = 1.0;
  378. color.b = 1.0;
  379. color.a = 1.0;
  380. }
  381. }
  382. colors.push(this._color.r, this._color.g, this._color.b, this._color.a);
  383. c += 4;
  384. if (!this.recomputeNormals && meshNor) {
  385. tmpVertex.x = meshNor[n];
  386. tmpVertex.y = meshNor[n + 1];
  387. tmpVertex.z = meshNor[n + 2];
  388. Vector3.TransformNormalToRef(tmpVertex, rotMatrix, tmpVertex);
  389. normals.push(tmpVertex.x, tmpVertex.y, tmpVertex.z);
  390. n += 3;
  391. }
  392. }
  393. for (i = 0; i < meshInd.length; i++) {
  394. var current_ind = p + meshInd[i];
  395. indices.push(current_ind);
  396. if (current_ind > 65535) {
  397. this._needs32Bits = true;
  398. }
  399. }
  400. if (this._pickable) {
  401. var nbfaces = meshInd.length / 3;
  402. for (i = 0; i < nbfaces; i++) {
  403. this.pickedParticles.push({ idx: idx, faceId: i });
  404. }
  405. }
  406. if (this._depthSort) {
  407. this.depthSortedParticles.push(new DepthSortedParticle());
  408. }
  409. return copy;
  410. }
  411. // returns a shape array from positions array
  412. private _posToShape(positions: number[] | Float32Array): Vector3[] {
  413. var shape = [];
  414. for (var i = 0; i < positions.length; i += 3) {
  415. shape.push(Vector3.FromArray(positions, i));
  416. }
  417. return shape;
  418. }
  419. // returns a shapeUV array from a Vector4 uvs
  420. private _uvsToShapeUV(uvs: number[] | Float32Array): number[] {
  421. var shapeUV = [];
  422. if (uvs) {
  423. for (var i = 0; i < uvs.length; i++) {
  424. shapeUV.push(uvs[i]);
  425. }
  426. }
  427. return shapeUV;
  428. }
  429. // adds a new particle object in the particles array
  430. private _addParticle(idx: number, idxpos: number, idxind: number, model: ModelShape, shapeId: number, idxInShape: number, bInfo: Nullable<BoundingInfo> = null): SolidParticle {
  431. var sp = new SolidParticle(idx, idxpos, idxind, model, shapeId, idxInShape, this, bInfo);
  432. this.particles.push(sp);
  433. return sp;
  434. }
  435. /**
  436. * Adds some particles to the SPS from the model shape. Returns the shape id.
  437. * Please read the doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#create-an-immutable-sps
  438. * @param mesh is any Mesh object that will be used as a model for the solid particles.
  439. * @param nb (positive integer) the number of particles to be created from this model
  440. * @param options {positionFunction} is an optional javascript function to called for each particle on SPS creation.
  441. * {vertexFunction} is an optional javascript function to called for each vertex of each particle on SPS creation
  442. * @returns the number of shapes in the system
  443. */
  444. public addShape(mesh: Mesh, nb: number, options?: { positionFunction?: any; vertexFunction?: any }): number {
  445. var meshPos = <FloatArray>mesh.getVerticesData(VertexBuffer.PositionKind);
  446. var meshInd = <IndicesArray>mesh.getIndices();
  447. var meshUV = <FloatArray>mesh.getVerticesData(VertexBuffer.UVKind);
  448. var meshCol = <FloatArray>mesh.getVerticesData(VertexBuffer.ColorKind);
  449. var meshNor = <FloatArray>mesh.getVerticesData(VertexBuffer.NormalKind);
  450. var bbInfo;
  451. if (this._particlesIntersect) {
  452. bbInfo = mesh.getBoundingInfo();
  453. }
  454. var shape = this._posToShape(meshPos);
  455. var shapeUV = this._uvsToShapeUV(meshUV);
  456. var posfunc = options ? options.positionFunction : null;
  457. var vtxfunc = options ? options.vertexFunction : null;
  458. var modelShape = new ModelShape(this._shapeCounter, shape, meshInd.length, shapeUV, posfunc, vtxfunc);
  459. // particles
  460. var sp;
  461. var currentCopy;
  462. var idx = this.nbParticles;
  463. for (var i = 0; i < nb; i++) {
  464. var currentPos = this._positions.length;
  465. var currentInd = this._indices.length;
  466. currentCopy = this._meshBuilder(this._index, shape, this._positions, meshInd, this._indices, meshUV, this._uvs, meshCol, this._colors, meshNor, this._normals, idx, i, options);
  467. if (this._updatable) {
  468. sp = this._addParticle(idx, currentPos, currentInd, modelShape, this._shapeCounter, i, bbInfo);
  469. sp.position.copyFrom(currentCopy.position);
  470. sp.rotation.copyFrom(currentCopy.rotation);
  471. if (currentCopy.rotationQuaternion && sp.rotationQuaternion) {
  472. sp.rotationQuaternion.copyFrom(currentCopy.rotationQuaternion);
  473. }
  474. if (currentCopy.color && sp.color) {
  475. sp.color.copyFrom(currentCopy.color);
  476. }
  477. sp.scaling.copyFrom(currentCopy.scaling);
  478. sp.uvs.copyFrom(currentCopy.uvs);
  479. }
  480. this._index += shape.length;
  481. idx++;
  482. }
  483. this.nbParticles += nb;
  484. this._shapeCounter++;
  485. return this._shapeCounter - 1;
  486. }
  487. // rebuilds a particle back to its just built status : if needed, recomputes the custom positions and vertices
  488. private _rebuildParticle(particle: SolidParticle): void {
  489. this._resetCopy();
  490. const copy = this._copy;
  491. if (particle._model._positionFunction) { // recall to stored custom positionFunction
  492. particle._model._positionFunction(copy, particle.idx, particle.idxInShape);
  493. }
  494. const rotMatrix = Tmp.Matrix[0];
  495. const tmpVertex = Tmp.Vector3[0];
  496. const tmpRotated = Tmp.Vector3[1];
  497. const pivotBackTranslation = Tmp.Vector3[2];
  498. const scaledPivot = Tmp.Vector3[3];
  499. copy.getRotationMatrix(rotMatrix);
  500. particle.pivot.multiplyToRef(particle.scaling, scaledPivot);
  501. if (copy.translateFromPivot) {
  502. pivotBackTranslation.copyFromFloats(0.0, 0.0, 0.0);
  503. }
  504. else {
  505. pivotBackTranslation.copyFrom(scaledPivot);
  506. }
  507. const shape = particle._model._shape;
  508. for (var pt = 0; pt < shape.length; pt++) {
  509. tmpVertex.copyFrom(shape[pt]);
  510. if (particle._model._vertexFunction) {
  511. particle._model._vertexFunction(copy, tmpVertex, pt); // recall to stored vertexFunction
  512. }
  513. tmpVertex.multiplyInPlace(copy.scaling).subtractInPlace(scaledPivot);
  514. Vector3.TransformCoordinatesToRef(tmpVertex, rotMatrix, tmpRotated);
  515. tmpRotated.addInPlace(pivotBackTranslation).addInPlace(copy.position).toArray(this._positions32, particle._pos + pt * 3);
  516. }
  517. particle.position.setAll(0.0);
  518. particle.rotation.setAll(0.0);
  519. particle.rotationQuaternion = null;
  520. particle.scaling.setAll(1.0);
  521. particle.uvs.setAll(0.0);
  522. particle.pivot.setAll(0.0);
  523. particle.translateFromPivot = false;
  524. particle.parentId = null;
  525. }
  526. /**
  527. * Rebuilds the whole mesh and updates the VBO : custom positions and vertices are recomputed if needed.
  528. * @returns the SPS.
  529. */
  530. public rebuildMesh(): SolidParticleSystem {
  531. for (var p = 0; p < this.particles.length; p++) {
  532. this._rebuildParticle(this.particles[p]);
  533. }
  534. this.mesh.updateVerticesData(VertexBuffer.PositionKind, this._positions32, false, false);
  535. return this;
  536. }
  537. /**
  538. * Sets all the particles : this method actually really updates the mesh according to the particle positions, rotations, colors, textures, etc.
  539. * This method calls `updateParticle()` for each particle of the SPS.
  540. * For an animated SPS, it is usually called within the render loop.
  541. * @param start The particle index in the particle array where to start to compute the particle property values _(default 0)_
  542. * @param end The particle index in the particle array where to stop to compute the particle property values _(default nbParticle - 1)_
  543. * @param update If the mesh must be finally updated on this call after all the particle computations _(default true)_
  544. * @returns the SPS.
  545. */
  546. public setParticles(start: number = 0, end: number = this.nbParticles - 1, update: boolean = true): SolidParticleSystem {
  547. if (!this._updatable) {
  548. return this;
  549. }
  550. // custom beforeUpdate
  551. this.beforeUpdateParticles();
  552. const rotMatrix = Tmp.Matrix[0];
  553. const invertedMatrix = Tmp.Matrix[1];
  554. const mesh = this.mesh;
  555. const colors32 = this._colors32;
  556. const positions32 = this._positions32;
  557. const normals32 = this._normals32;
  558. const uvs32 = this._uvs32;
  559. const indices32 = this._indices32;
  560. const indices = this._indices;
  561. const fixedNormal32 = this._fixedNormal32;
  562. const tempVectors = Tmp.Vector3;
  563. const camAxisX = tempVectors[5].copyFromFloats(1.0, 0.0, 0.0);
  564. const camAxisY = tempVectors[6].copyFromFloats(0.0, 1.0, 0.0);
  565. const camAxisZ = tempVectors[7].copyFromFloats(0.0, 0.0, 1.0);
  566. const minimum = tempVectors[8].setAll(Number.MAX_VALUE);
  567. const maximum = tempVectors[9].setAll(-Number.MAX_VALUE);
  568. const camInvertedPosition = tempVectors[10].setAll(0);
  569. // cases when the World Matrix is to be computed first
  570. if (this.billboard || this._depthSort) {
  571. this.mesh.computeWorldMatrix(true);
  572. this.mesh._worldMatrix.invertToRef(invertedMatrix);
  573. }
  574. // if the particles will always face the camera
  575. if (this.billboard) {
  576. // compute the camera position and un-rotate it by the current mesh rotation
  577. const tmpVertex = tempVectors[0];
  578. this._camera.getDirectionToRef(Axis.Z, tmpVertex);
  579. Vector3.TransformNormalToRef(tmpVertex, invertedMatrix, camAxisZ);
  580. camAxisZ.normalize();
  581. // same for camera up vector extracted from the cam view matrix
  582. var view = this._camera.getViewMatrix(true);
  583. Vector3.TransformNormalFromFloatsToRef(view.m[1], view.m[5], view.m[9], invertedMatrix, camAxisY);
  584. Vector3.CrossToRef(camAxisY, camAxisZ, camAxisX);
  585. camAxisY.normalize();
  586. camAxisX.normalize();
  587. }
  588. // if depthSort, compute the camera global position in the mesh local system
  589. if (this._depthSort) {
  590. Vector3.TransformCoordinatesToRef(this._camera.globalPosition, invertedMatrix, camInvertedPosition); // then un-rotate the camera
  591. }
  592. Matrix.IdentityToRef(rotMatrix);
  593. var idx = 0; // current position index in the global array positions32
  594. var index = 0; // position start index in the global array positions32 of the current particle
  595. var colidx = 0; // current color index in the global array colors32
  596. var colorIndex = 0; // color start index in the global array colors32 of the current particle
  597. var uvidx = 0; // current uv index in the global array uvs32
  598. var uvIndex = 0; // uv start index in the global array uvs32 of the current particle
  599. var pt = 0; // current index in the particle model shape
  600. if (this.mesh.isFacetDataEnabled) {
  601. this._computeBoundingBox = true;
  602. }
  603. end = (end >= this.nbParticles) ? this.nbParticles - 1 : end;
  604. if (this._computeBoundingBox) {
  605. if (start != 0 || end != this.nbParticles - 1) { // only some particles are updated, then use the current existing BBox basis. Note : it can only increase.
  606. const boundingInfo = this.mesh._boundingInfo;
  607. if (boundingInfo) {
  608. minimum.copyFrom(boundingInfo.minimum);
  609. maximum.copyFrom(boundingInfo.maximum);
  610. }
  611. }
  612. }
  613. // particle loop
  614. index = this.particles[start]._pos;
  615. const vpos = (index / 3) | 0;
  616. colorIndex = vpos * 4;
  617. uvIndex = vpos * 2;
  618. for (var p = start; p <= end; p++) {
  619. const particle = this.particles[p];
  620. // call to custom user function to update the particle properties
  621. this.updateParticle(particle);
  622. const shape = particle._model._shape;
  623. const shapeUV = particle._model._shapeUV;
  624. const particleRotationMatrix = particle._rotationMatrix;
  625. const particlePosition = particle.position;
  626. const particleRotation = particle.rotation;
  627. const particleScaling = particle.scaling;
  628. const particleGlobalPosition = particle._globalPosition;
  629. // camera-particle distance for depth sorting
  630. if (this._depthSort && this._depthSortParticles) {
  631. var dsp = this.depthSortedParticles[p];
  632. dsp.ind = particle._ind;
  633. dsp.indicesLength = particle._model._indicesLength;
  634. dsp.sqDistance = Vector3.DistanceSquared(particle.position, camInvertedPosition);
  635. }
  636. // skip the computations for inactive or already invisible particles
  637. if (!particle.alive || (particle._stillInvisible && !particle.isVisible)) {
  638. // increment indexes for the next particle
  639. pt = shape.length;
  640. index += pt * 3;
  641. colorIndex += pt * 4;
  642. uvIndex += pt * 2;
  643. continue;
  644. }
  645. if (particle.isVisible) {
  646. particle._stillInvisible = false; // un-mark permanent invisibility
  647. const scaledPivot = tempVectors[12];
  648. particle.pivot.multiplyToRef(particleScaling, scaledPivot);
  649. // particle rotation matrix
  650. if (this.billboard) {
  651. particleRotation.x = 0.0;
  652. particleRotation.y = 0.0;
  653. }
  654. if (this._computeParticleRotation || this.billboard) {
  655. particle.getRotationMatrix(rotMatrix);
  656. }
  657. const particleHasParent = (particle.parentId !== null);
  658. if (particleHasParent) {
  659. const parent = this.particles[particle.parentId!];
  660. const parentRotationMatrix = parent._rotationMatrix;
  661. const parentGlobalPosition = parent._globalPosition;
  662. const rotatedY = particlePosition.x * parentRotationMatrix[1] + particlePosition.y * parentRotationMatrix[4] + particlePosition.z * parentRotationMatrix[7];
  663. const rotatedX = particlePosition.x * parentRotationMatrix[0] + particlePosition.y * parentRotationMatrix[3] + particlePosition.z * parentRotationMatrix[6];
  664. const rotatedZ = particlePosition.x * parentRotationMatrix[2] + particlePosition.y * parentRotationMatrix[5] + particlePosition.z * parentRotationMatrix[8];
  665. particleGlobalPosition.x = parentGlobalPosition.x + rotatedX;
  666. particleGlobalPosition.y = parentGlobalPosition.y + rotatedY;
  667. particleGlobalPosition.z = parentGlobalPosition.z + rotatedZ;
  668. if (this._computeParticleRotation || this.billboard) {
  669. const rotMatrixValues = rotMatrix.m;
  670. particleRotationMatrix[0] = rotMatrixValues[0] * parentRotationMatrix[0] + rotMatrixValues[1] * parentRotationMatrix[3] + rotMatrixValues[2] * parentRotationMatrix[6];
  671. particleRotationMatrix[1] = rotMatrixValues[0] * parentRotationMatrix[1] + rotMatrixValues[1] * parentRotationMatrix[4] + rotMatrixValues[2] * parentRotationMatrix[7];
  672. particleRotationMatrix[2] = rotMatrixValues[0] * parentRotationMatrix[2] + rotMatrixValues[1] * parentRotationMatrix[5] + rotMatrixValues[2] * parentRotationMatrix[8];
  673. particleRotationMatrix[3] = rotMatrixValues[4] * parentRotationMatrix[0] + rotMatrixValues[5] * parentRotationMatrix[3] + rotMatrixValues[6] * parentRotationMatrix[6];
  674. particleRotationMatrix[4] = rotMatrixValues[4] * parentRotationMatrix[1] + rotMatrixValues[5] * parentRotationMatrix[4] + rotMatrixValues[6] * parentRotationMatrix[7];
  675. particleRotationMatrix[5] = rotMatrixValues[4] * parentRotationMatrix[2] + rotMatrixValues[5] * parentRotationMatrix[5] + rotMatrixValues[6] * parentRotationMatrix[8];
  676. particleRotationMatrix[6] = rotMatrixValues[8] * parentRotationMatrix[0] + rotMatrixValues[9] * parentRotationMatrix[3] + rotMatrixValues[10] * parentRotationMatrix[6];
  677. particleRotationMatrix[7] = rotMatrixValues[8] * parentRotationMatrix[1] + rotMatrixValues[9] * parentRotationMatrix[4] + rotMatrixValues[10] * parentRotationMatrix[7];
  678. particleRotationMatrix[8] = rotMatrixValues[8] * parentRotationMatrix[2] + rotMatrixValues[9] * parentRotationMatrix[5] + rotMatrixValues[10] * parentRotationMatrix[8];
  679. }
  680. }
  681. else {
  682. particleGlobalPosition.x = particlePosition.x;
  683. particleGlobalPosition.y = particlePosition.y;
  684. particleGlobalPosition.z = particlePosition.z;
  685. if (this._computeParticleRotation || this.billboard) {
  686. const rotMatrixValues = rotMatrix.m;
  687. particleRotationMatrix[0] = rotMatrixValues[0];
  688. particleRotationMatrix[1] = rotMatrixValues[1];
  689. particleRotationMatrix[2] = rotMatrixValues[2];
  690. particleRotationMatrix[3] = rotMatrixValues[4];
  691. particleRotationMatrix[4] = rotMatrixValues[5];
  692. particleRotationMatrix[5] = rotMatrixValues[6];
  693. particleRotationMatrix[6] = rotMatrixValues[8];
  694. particleRotationMatrix[7] = rotMatrixValues[9];
  695. particleRotationMatrix[8] = rotMatrixValues[10];
  696. }
  697. }
  698. const pivotBackTranslation = tempVectors[11];
  699. if (particle.translateFromPivot) {
  700. pivotBackTranslation.setAll(0.0);
  701. }
  702. else {
  703. pivotBackTranslation.copyFrom(scaledPivot);
  704. }
  705. // particle vertex loop
  706. for (pt = 0; pt < shape.length; pt++) {
  707. idx = index + pt * 3;
  708. colidx = colorIndex + pt * 4;
  709. uvidx = uvIndex + pt * 2;
  710. const tmpVertex = tempVectors[0];
  711. tmpVertex.copyFrom(shape[pt]);
  712. if (this._computeParticleVertex) {
  713. this.updateParticleVertex(tmpVertex);
  714. }
  715. // positions
  716. const vertexX = tmpVertex.x * particleScaling.x - scaledPivot.x;
  717. const vertexY = tmpVertex.y * particleScaling.y - scaledPivot.y;
  718. const vertexZ = tmpVertex.z * particleScaling.z - scaledPivot.z;
  719. let rotatedX = vertexX * particleRotationMatrix[0] + vertexY * particleRotationMatrix[3] + vertexZ * particleRotationMatrix[6];
  720. let rotatedY = vertexX * particleRotationMatrix[1] + vertexY * particleRotationMatrix[4] + vertexZ * particleRotationMatrix[7];
  721. let rotatedZ = vertexX * particleRotationMatrix[2] + vertexY * particleRotationMatrix[5] + vertexZ * particleRotationMatrix[8];
  722. rotatedX += pivotBackTranslation.x;
  723. rotatedY += pivotBackTranslation.y;
  724. rotatedZ += pivotBackTranslation.z;
  725. const px = positions32[idx] = particleGlobalPosition.x + camAxisX.x * rotatedX + camAxisY.x * rotatedY + camAxisZ.x * rotatedZ;
  726. const py = positions32[idx + 1] = particleGlobalPosition.y + camAxisX.y * rotatedX + camAxisY.y * rotatedY + camAxisZ.y * rotatedZ;
  727. const pz = positions32[idx + 2] = particleGlobalPosition.z + camAxisX.z * rotatedX + camAxisY.z * rotatedY + camAxisZ.z * rotatedZ;
  728. if (this._computeBoundingBox) {
  729. minimum.minimizeInPlaceFromFloats(px, py, pz);
  730. maximum.maximizeInPlaceFromFloats(px, py, pz);
  731. }
  732. // normals : if the particles can't be morphed then just rotate the normals, what is much more faster than ComputeNormals()
  733. if (!this._computeParticleVertex) {
  734. const normalx = fixedNormal32[idx];
  735. const normaly = fixedNormal32[idx + 1];
  736. const normalz = fixedNormal32[idx + 2];
  737. const rotatedx = normalx * particleRotationMatrix[0] + normaly * particleRotationMatrix[3] + normalz * particleRotationMatrix[6];
  738. const rotatedy = normalx * particleRotationMatrix[1] + normaly * particleRotationMatrix[4] + normalz * particleRotationMatrix[7];
  739. const rotatedz = normalx * particleRotationMatrix[2] + normaly * particleRotationMatrix[5] + normalz * particleRotationMatrix[8];
  740. normals32[idx] = camAxisX.x * rotatedx + camAxisY.x * rotatedy + camAxisZ.x * rotatedz;
  741. normals32[idx + 1] = camAxisX.y * rotatedx + camAxisY.y * rotatedy + camAxisZ.y * rotatedz;
  742. normals32[idx + 2] = camAxisX.z * rotatedx + camAxisY.z * rotatedy + camAxisZ.z * rotatedz;
  743. }
  744. if (this._computeParticleColor && particle.color) {
  745. const color = particle.color;
  746. const colors32 = this._colors32;
  747. colors32[colidx] = color.r;
  748. colors32[colidx + 1] = color.g;
  749. colors32[colidx + 2] = color.b;
  750. colors32[colidx + 3] = color.a;
  751. }
  752. if (this._computeParticleTexture) {
  753. const uvs = particle.uvs;
  754. uvs32[uvidx] = shapeUV[pt * 2] * (uvs.z - uvs.x) + uvs.x;
  755. uvs32[uvidx + 1] = shapeUV[pt * 2 + 1] * (uvs.w - uvs.y) + uvs.y;
  756. }
  757. }
  758. }
  759. // particle just set invisible : scaled to zero and positioned at the origin
  760. else {
  761. particle._stillInvisible = true; // mark the particle as invisible
  762. for (pt = 0; pt < shape.length; pt++) {
  763. idx = index + pt * 3;
  764. colidx = colorIndex + pt * 4;
  765. uvidx = uvIndex + pt * 2;
  766. positions32[idx] = positions32[idx + 1] = positions32[idx + 2] = 0;
  767. normals32[idx] = normals32[idx + 1] = normals32[idx + 2] = 0;
  768. if (this._computeParticleColor && particle.color) {
  769. const color = particle.color;
  770. colors32[colidx] = color.r;
  771. colors32[colidx + 1] = color.g;
  772. colors32[colidx + 2] = color.b;
  773. colors32[colidx + 3] = color.a;
  774. }
  775. if (this._computeParticleTexture) {
  776. const uvs = particle.uvs;
  777. uvs32[uvidx] = shapeUV[pt * 2] * (uvs.z - uvs.x) + uvs.x;
  778. uvs32[uvidx + 1] = shapeUV[pt * 2 + 1] * (uvs.w - uvs.y) + uvs.y;
  779. }
  780. }
  781. }
  782. // if the particle intersections must be computed : update the bbInfo
  783. if (this._particlesIntersect) {
  784. const bInfo = particle._boundingInfo;
  785. const bBox = bInfo.boundingBox;
  786. const bSphere = bInfo.boundingSphere;
  787. const modelBoundingInfo = particle._modelBoundingInfo;
  788. if (!this._bSphereOnly) {
  789. // place, scale and rotate the particle bbox within the SPS local system, then update it
  790. const modelBoundingInfoVectors = modelBoundingInfo.boundingBox.vectors;
  791. const tempMin = tempVectors[1];
  792. const tempMax = tempVectors[2];
  793. tempMin.setAll(Number.MAX_VALUE);
  794. tempMax.setAll(-Number.MAX_VALUE);
  795. for (var b = 0; b < 8; b++) {
  796. const scaledX = modelBoundingInfoVectors[b].x * particleScaling.x;
  797. const scaledY = modelBoundingInfoVectors[b].y * particleScaling.y;
  798. const scaledZ = modelBoundingInfoVectors[b].z * particleScaling.z;
  799. const rotatedX = scaledX * particleRotationMatrix[0] + scaledY * particleRotationMatrix[3] + scaledZ * particleRotationMatrix[6];
  800. const rotatedY = scaledX * particleRotationMatrix[1] + scaledY * particleRotationMatrix[4] + scaledZ * particleRotationMatrix[7];
  801. const rotatedZ = scaledX * particleRotationMatrix[2] + scaledY * particleRotationMatrix[5] + scaledZ * particleRotationMatrix[8];
  802. const x = particlePosition.x + camAxisX.x * rotatedX + camAxisY.x * rotatedY + camAxisZ.x * rotatedZ;
  803. const y = particlePosition.y + camAxisX.y * rotatedX + camAxisY.y * rotatedY + camAxisZ.y * rotatedZ;
  804. const z = particlePosition.z + camAxisX.z * rotatedX + camAxisY.z * rotatedY + camAxisZ.z * rotatedZ;
  805. tempMin.minimizeInPlaceFromFloats(x, y, z);
  806. tempMax.maximizeInPlaceFromFloats(x, y, z);
  807. }
  808. bBox.reConstruct(tempMin, tempMax, mesh._worldMatrix);
  809. }
  810. // place and scale the particle bouding sphere in the SPS local system, then update it
  811. const minBbox = modelBoundingInfo.minimum.multiplyToRef(particleScaling, tempVectors[1]);
  812. const maxBbox = modelBoundingInfo.maximum.multiplyToRef(particleScaling, tempVectors[2]);
  813. const bSphereCenter = maxBbox.addToRef(minBbox, tempVectors[3]).scaleInPlace(0.5);
  814. const halfDiag = maxBbox.subtractToRef(minBbox, tempVectors[4]).scaleInPlace(0.5 * this._bSphereRadiusFactor);
  815. const bSphereMinBbox = bSphereCenter.subtractToRef(halfDiag, tempVectors[1]);
  816. const bSphereMaxBbox = bSphereCenter.addToRef(halfDiag, tempVectors[2]);
  817. bSphere.reConstruct(bSphereMinBbox, bSphereMaxBbox, mesh._worldMatrix);
  818. }
  819. // increment indexes for the next particle
  820. index = idx + 3;
  821. colorIndex = colidx + 4;
  822. uvIndex = uvidx + 2;
  823. }
  824. // if the VBO must be updated
  825. if (update) {
  826. if (this._computeParticleColor) {
  827. mesh.updateVerticesData(VertexBuffer.ColorKind, colors32, false, false);
  828. }
  829. if (this._computeParticleTexture) {
  830. mesh.updateVerticesData(VertexBuffer.UVKind, uvs32, false, false);
  831. }
  832. mesh.updateVerticesData(VertexBuffer.PositionKind, positions32, false, false);
  833. if (!mesh.areNormalsFrozen || mesh.isFacetDataEnabled) {
  834. if (this._computeParticleVertex || mesh.isFacetDataEnabled) {
  835. // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
  836. var params = mesh.isFacetDataEnabled ? mesh.getFacetDataParameters() : null;
  837. VertexData.ComputeNormals(positions32, indices32, normals32, params);
  838. for (var i = 0; i < normals32.length; i++) {
  839. fixedNormal32[i] = normals32[i];
  840. }
  841. }
  842. if (!mesh.areNormalsFrozen) {
  843. mesh.updateVerticesData(VertexBuffer.NormalKind, normals32, false, false);
  844. }
  845. }
  846. if (this._depthSort && this._depthSortParticles) {
  847. const depthSortedParticles = this.depthSortedParticles;
  848. depthSortedParticles.sort(depthSortFunction);
  849. const dspl = depthSortedParticles.length;
  850. let sid = 0;
  851. for (let sorted = 0; sorted < dspl; sorted++) {
  852. const lind = depthSortedParticles[sorted].indicesLength;
  853. const sind = depthSortedParticles[sorted].ind;
  854. for (var i = 0; i < lind; i++) {
  855. indices32[sid] = indices[sind + i];
  856. sid++;
  857. }
  858. }
  859. mesh.updateIndices(indices32);
  860. }
  861. }
  862. if (this._computeBoundingBox) {
  863. if (mesh._boundingInfo) {
  864. mesh._boundingInfo.reConstruct(minimum, maximum, mesh._worldMatrix);
  865. }
  866. else {
  867. mesh._boundingInfo = new BoundingInfo(minimum, maximum, mesh._worldMatrix);
  868. }
  869. }
  870. this.afterUpdateParticles();
  871. return this;
  872. }
  873. /**
  874. * Disposes the SPS.
  875. */
  876. public dispose(): void {
  877. this.mesh.dispose();
  878. this.vars = null;
  879. // drop references to internal big arrays for the GC
  880. (<any>this._positions) = null;
  881. (<any>this._indices) = null;
  882. (<any>this._normals) = null;
  883. (<any>this._uvs) = null;
  884. (<any>this._colors) = null;
  885. (<any>this._indices32) = null;
  886. (<any>this._positions32) = null;
  887. (<any>this._normals32) = null;
  888. (<any>this._fixedNormal32) = null;
  889. (<any>this._uvs32) = null;
  890. (<any>this._colors32) = null;
  891. (<any>this.pickedParticles) = null;
  892. }
  893. /**
  894. * Visibilty helper : Recomputes the visible size according to the mesh bounding box
  895. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  896. * @returns the SPS.
  897. */
  898. public refreshVisibleSize(): SolidParticleSystem {
  899. if (!this._isVisibilityBoxLocked) {
  900. this.mesh.refreshBoundingInfo();
  901. }
  902. return this;
  903. }
  904. /**
  905. * Visibility helper : Sets the size of a visibility box, this sets the underlying mesh bounding box.
  906. * @param size the size (float) of the visibility box
  907. * note : this doesn't lock the SPS mesh bounding box.
  908. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  909. */
  910. public setVisibilityBox(size: number): void {
  911. var vis = size / 2;
  912. this.mesh._boundingInfo = new BoundingInfo(new Vector3(-vis, -vis, -vis), new Vector3(vis, vis, vis));
  913. }
  914. /**
  915. * Gets whether the SPS as always visible or not
  916. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  917. */
  918. public get isAlwaysVisible(): boolean {
  919. return this._alwaysVisible;
  920. }
  921. /**
  922. * Sets the SPS as always visible or not
  923. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  924. */
  925. public set isAlwaysVisible(val: boolean) {
  926. this._alwaysVisible = val;
  927. this.mesh.alwaysSelectAsActiveMesh = val;
  928. }
  929. /**
  930. * Sets the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
  931. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  932. */
  933. public set isVisibilityBoxLocked(val: boolean) {
  934. this._isVisibilityBoxLocked = val;
  935. let boundingInfo = this.mesh.getBoundingInfo();
  936. boundingInfo.isLocked = val;
  937. }
  938. /**
  939. * Gets if the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
  940. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
  941. */
  942. public get isVisibilityBoxLocked(): boolean {
  943. return this._isVisibilityBoxLocked;
  944. }
  945. /**
  946. * Tells to `setParticles()` to compute the particle rotations or not.
  947. * Default value : true. The SPS is faster when it's set to false.
  948. * Note : the particle rotations aren't stored values, so setting `computeParticleRotation` to false will prevents the particle to rotate.
  949. */
  950. public set computeParticleRotation(val: boolean) {
  951. this._computeParticleRotation = val;
  952. }
  953. /**
  954. * Tells to `setParticles()` to compute the particle colors or not.
  955. * Default value : true. The SPS is faster when it's set to false.
  956. * Note : the particle colors are stored values, so setting `computeParticleColor` to false will keep yet the last colors set.
  957. */
  958. public set computeParticleColor(val: boolean) {
  959. this._computeParticleColor = val;
  960. }
  961. public set computeParticleTexture(val: boolean) {
  962. this._computeParticleTexture = val;
  963. }
  964. /**
  965. * Tells to `setParticles()` to call the vertex function for each vertex of each particle, or not.
  966. * Default value : false. The SPS is faster when it's set to false.
  967. * Note : the particle custom vertex positions aren't stored values.
  968. */
  969. public set computeParticleVertex(val: boolean) {
  970. this._computeParticleVertex = val;
  971. }
  972. /**
  973. * Tells to `setParticles()` to compute or not the mesh bounding box when computing the particle positions.
  974. */
  975. public set computeBoundingBox(val: boolean) {
  976. this._computeBoundingBox = val;
  977. }
  978. /**
  979. * Tells to `setParticles()` to sort or not the distance between each particle and the camera.
  980. * Skipped when `enableDepthSort` is set to `false` (default) at construction time.
  981. * Default : `true`
  982. */
  983. public set depthSortParticles(val: boolean) {
  984. this._depthSortParticles = val;
  985. }
  986. /**
  987. * Gets if `setParticles()` computes the particle rotations or not.
  988. * Default value : true. The SPS is faster when it's set to false.
  989. * Note : the particle rotations aren't stored values, so setting `computeParticleRotation` to false will prevents the particle to rotate.
  990. */
  991. public get computeParticleRotation(): boolean {
  992. return this._computeParticleRotation;
  993. }
  994. /**
  995. * Gets if `setParticles()` computes the particle colors or not.
  996. * Default value : true. The SPS is faster when it's set to false.
  997. * Note : the particle colors are stored values, so setting `computeParticleColor` to false will keep yet the last colors set.
  998. */
  999. public get computeParticleColor(): boolean {
  1000. return this._computeParticleColor;
  1001. }
  1002. /**
  1003. * Gets if `setParticles()` computes the particle textures or not.
  1004. * Default value : true. The SPS is faster when it's set to false.
  1005. * Note : the particle textures are stored values, so setting `computeParticleTexture` to false will keep yet the last colors set.
  1006. */
  1007. public get computeParticleTexture(): boolean {
  1008. return this._computeParticleTexture;
  1009. }
  1010. /**
  1011. * Gets if `setParticles()` calls the vertex function for each vertex of each particle, or not.
  1012. * Default value : false. The SPS is faster when it's set to false.
  1013. * Note : the particle custom vertex positions aren't stored values.
  1014. */
  1015. public get computeParticleVertex(): boolean {
  1016. return this._computeParticleVertex;
  1017. }
  1018. /**
  1019. * Gets if `setParticles()` computes or not the mesh bounding box when computing the particle positions.
  1020. */
  1021. public get computeBoundingBox(): boolean {
  1022. return this._computeBoundingBox;
  1023. }
  1024. /**
  1025. * Gets if `setParticles()` sorts or not the distance between each particle and the camera.
  1026. * Skipped when `enableDepthSort` is set to `false` (default) at construction time.
  1027. * Default : `true`
  1028. */
  1029. public get depthSortParticles(): boolean {
  1030. return this._depthSortParticles;
  1031. }
  1032. // =======================================================================
  1033. // Particle behavior logic
  1034. // these following methods may be overwritten by the user to fit his needs
  1035. /**
  1036. * This function does nothing. It may be overwritten to set all the particle first values.
  1037. * The SPS doesn't call this function, you may have to call it by your own.
  1038. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
  1039. */
  1040. public initParticles(): void {
  1041. }
  1042. /**
  1043. * This function does nothing. It may be overwritten to recycle a particle.
  1044. * The SPS doesn't call this function, you may have to call it by your own.
  1045. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
  1046. * @param particle The particle to recycle
  1047. * @returns the recycled particle
  1048. */
  1049. public recycleParticle(particle: SolidParticle): SolidParticle {
  1050. return particle;
  1051. }
  1052. /**
  1053. * Updates a particle : this function should be overwritten by the user.
  1054. * It is called on each particle by `setParticles()`. This is the place to code each particle behavior.
  1055. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
  1056. * @example : just set a particle position or velocity and recycle conditions
  1057. * @param particle The particle to update
  1058. * @returns the updated particle
  1059. */
  1060. public updateParticle(particle: SolidParticle): SolidParticle {
  1061. return particle;
  1062. }
  1063. /**
  1064. * Updates a vertex of a particle : it can be overwritten by the user.
  1065. * This will be called on each vertex particle by `setParticles()` if `computeParticleVertex` is set to true only.
  1066. * @param particle the current particle
  1067. * @param vertex the current index of the current particle
  1068. * @param pt the index of the current vertex in the particle shape
  1069. * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#update-each-particle-shape
  1070. * @example : just set a vertex particle position
  1071. * @returns the updated vertex
  1072. */
  1073. public updateParticleVertex(vertex: Vector3): Vector3 {
  1074. return vertex;
  1075. }
  1076. /**
  1077. * This will be called before any other treatment by `setParticles()` and will be passed three parameters.
  1078. * This does nothing and may be overwritten by the user.
  1079. * @param start the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1080. * @param stop the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1081. * @param update the boolean update value actually passed to setParticles()
  1082. */
  1083. public beforeUpdateParticles(): void {
  1084. }
  1085. /**
  1086. * This will be called by `setParticles()` after all the other treatments and just before the actual mesh update.
  1087. * This will be passed three parameters.
  1088. * This does nothing and may be overwritten by the user.
  1089. * @param start the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1090. * @param stop the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1091. * @param update the boolean update value actually passed to setParticles()
  1092. */
  1093. public afterUpdateParticles(): void {
  1094. }
  1095. }