babylon.solidParticleSystem.ts 78 KB

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