babylon.solidParticleSystem.ts 71 KB

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