babylon.solidParticleSystem.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. module BABYLON {
  2. export class SolidParticleSystem implements IDisposable {
  3. // public members
  4. public particles = new Array<SolidParticle>();
  5. public nbParticles = 0;
  6. public billboard = false;
  7. public counter = 0;
  8. public name: string;
  9. public mesh: Mesh;
  10. // private members
  11. private _scene: Scene;
  12. private _positions = new Array<number>();
  13. private _indices = new Array<number>();
  14. private _normals = new Array<number>();
  15. private _colors = new Array<number>();
  16. private _uvs = new Array<number>();
  17. private _index = 0; // indices index
  18. private _shapeCounter = 0;
  19. private _setParticleColor = true;
  20. private _setParticleTexture = true;
  21. private _setParticleRotation = true;
  22. private _setParticleVertex = false;
  23. private _cam_axisZ = Vector3.Zero();
  24. private _cam_axisY = Vector3.Zero();
  25. private _cam_axisX = Vector3.Zero();
  26. private _axisX = Axis.X;
  27. private _axisY = Axis.Y;
  28. private _axisZ = Axis.Z;
  29. private _camera: Camera;
  30. private _particle: SolidParticle;
  31. private _previousParticle: SolidParticle;
  32. private _fakeCamPos = Vector3.Zero();
  33. private _rotMatrix = new Matrix();
  34. private _invertedMatrix = new Matrix();
  35. private _rotated = Vector3.Zero();
  36. private _quaternion = new Quaternion();
  37. private _vertex = Vector3.Zero();
  38. private _yaw = 0.0;
  39. private _pitch = 0.0;
  40. private _roll = 0.0;
  41. private _halfroll = 0.0;
  42. private _halfpitch = 0.0;
  43. private _halfyaw = 0.0;
  44. private _sinRoll = 0.0;
  45. private _cosRoll = 0.0;
  46. private _sinPitch = 0.0;
  47. private _cosPitch = 0.0;
  48. private _sinYaw = 0.0;
  49. private _cosYaw = 0.0;
  50. constructor(name: string, scene: Scene) {
  51. this.name = name;
  52. this._scene = scene;
  53. this._camera = scene.activeCamera;
  54. }
  55. // build the SPS mesh : returns the mesh
  56. public buildMesh(): Mesh {
  57. if (this.nbParticles === 0) {
  58. var triangle = MeshBuilder.CreateDisc("", { radius: 1, tessellation: 3 }, this._scene);
  59. this.addShape(triangle, 1);
  60. triangle.dispose();
  61. }
  62. VertexData.ComputeNormals(this._positions, this._indices, this._normals);
  63. var vertexData = new VertexData();
  64. vertexData.positions = this._positions;
  65. vertexData.indices = this._indices;
  66. vertexData.normals = this._normals;
  67. if (this._uvs) {
  68. vertexData.uvs = this._uvs;
  69. }
  70. if (this._colors) {
  71. vertexData.colors = this._colors;
  72. }
  73. var mesh = new Mesh(name, this._scene);
  74. vertexData.applyToMesh(mesh, true);
  75. this.mesh = mesh;
  76. return mesh;
  77. }
  78. // _meshBuilder : inserts the shape model in the global SPS mesh
  79. private _meshBuilder(p, shape, positions, meshInd, indices, meshUV, uvs, meshCol, colors): void {
  80. var i;
  81. var u = 0;
  82. var c = 0;
  83. for (i = 0; i < shape.length; i++) {
  84. positions.push(shape[i].x, shape[i].y, shape[i].z);
  85. if (meshUV) {
  86. uvs.push(meshUV[u], meshUV[u + 1]);
  87. u += 2;
  88. }
  89. if (meshCol) {
  90. colors.push(meshCol[c] || 1, meshCol[c + 1] || 1, meshCol[c + 2] || 1, meshCol[c + 3] || 1);
  91. c += 4;
  92. } else {
  93. colors.push(1, 1, 1, 1);
  94. }
  95. }
  96. for (i = 0; i < meshInd.length; i++) {
  97. indices.push(p + meshInd[i]);
  98. }
  99. }
  100. // returns a shape array from positions array
  101. private _posToShape(positions): Vector3[] {
  102. var shape = [];
  103. for (var i = 0; i < positions.length; i += 3) {
  104. shape.push(new Vector3(positions[i], positions[i + 1], positions[i + 2]));
  105. }
  106. return shape;
  107. }
  108. // returns a shapeUV array from a Vector4 uvs
  109. private _uvsToShapeUV(uvs): number[] {
  110. var shapeUV = [];
  111. if (uvs) {
  112. for (var i = 0; i < uvs.length; i++)
  113. shapeUV.push(uvs[i]);
  114. }
  115. return shapeUV;
  116. }
  117. // adds a new particle object in the particles array and double links the particle (next/previous)
  118. private _addParticle(p: number, idxpos: number, shape: Vector3[], shapeUV: number[], shapeId: number): void {
  119. this._particle = new SolidParticle(p, idxpos, shape, shapeUV, shapeId);
  120. this.particles.push(this._particle);
  121. this._particle.previous = this._previousParticle;
  122. if (this._previousParticle) {
  123. this._previousParticle.next = this._particle;
  124. }
  125. this._previousParticle = this._particle;
  126. }
  127. // add solid particles from a shape model in the particles array
  128. public addShape(mesh: Mesh, nb: number): number {
  129. var meshPos = mesh.getVerticesData(VertexBuffer.PositionKind);
  130. var meshInd = mesh.getIndices();
  131. var meshUV = mesh.getVerticesData(VertexBuffer.UVKind);
  132. var meshCol = mesh.getVerticesData(VertexBuffer.ColorKind);
  133. var shape = this._posToShape(meshPos);
  134. var shapeUV = this._uvsToShapeUV(meshUV);
  135. // particles
  136. for (var i = 0; i < nb; i++) {
  137. this._meshBuilder(this._index, shape, this._positions, meshInd, this._indices, meshUV, this._uvs, meshCol, this._colors);
  138. this._addParticle(this.nbParticles + i, this._positions.length, shape, shapeUV, this._shapeCounter);
  139. this._index += shape.length;
  140. }
  141. this.nbParticles += nb;
  142. this._shapeCounter++;
  143. return this._shapeCounter;
  144. }
  145. // resets a particle back to its just built status
  146. public resetParticle(particle: SolidParticle): void {
  147. for (var pt = 0; pt < particle._shape.length; pt++) {
  148. this._positions[particle._pos + pt * 3] = particle._shape[pt].x;
  149. this._positions[particle._pos + pt * 3 + 1] = particle._shape[pt].y;
  150. this._positions[particle._pos + pt * 3 + 2] = particle._shape[pt].z;
  151. }
  152. }
  153. // sets all the particles
  154. public setParticles(start: number = 0, end: number = this.nbParticles - 1, update: boolean = true): void {
  155. // custom beforeUpdate
  156. this.beforeUpdateParticles(start, end, update);
  157. this._cam_axisX.x = 1;
  158. this._cam_axisX.y = 0;
  159. this._cam_axisX.z = 0;
  160. this._cam_axisY.x = 0;
  161. this._cam_axisY.y = 1;
  162. this._cam_axisY.z = 0;
  163. this._cam_axisZ.x = 0;
  164. this._cam_axisZ.y = 0;
  165. this._cam_axisZ.z = 1;
  166. // if the particles will always face the camera
  167. if (this.billboard) {
  168. // compute a fake camera position : un-rotate the camera position by the current mesh rotation
  169. this._yaw = this.mesh.rotation.y;
  170. this._pitch = this.mesh.rotation.x;
  171. this._roll = this.mesh.rotation.z;
  172. this._quaternionRotationYPR();
  173. this._quaternionToRotationMatrix();
  174. this._rotMatrix.invertToRef(this._invertedMatrix);
  175. Vector3.TransformCoordinatesToRef(this._camera.globalPosition, this._invertedMatrix, this._fakeCamPos);
  176. // set two orthogonal vectors (_cam_axisX and and _cam_axisY) to the cam-mesh axis (_cam_axisZ)
  177. (this._fakeCamPos).subtractToRef(this.mesh.position, this._cam_axisZ);
  178. Vector3.CrossToRef(this._cam_axisZ, this._axisX, this._cam_axisY);
  179. Vector3.CrossToRef(this._cam_axisZ, this._cam_axisY, this._cam_axisX);
  180. this._cam_axisY.normalize();
  181. this._cam_axisX.normalize();
  182. this._cam_axisZ.normalize();
  183. }
  184. Matrix.IdentityToRef(this._rotMatrix);
  185. var idx = 0;
  186. var index = 0;
  187. var colidx = 0;
  188. var colorIndex = 0;
  189. var uvidx = 0;
  190. var uvIndex = 0;
  191. // particle loop
  192. for (var p = start; p <= end; p++) {
  193. this._particle = this.particles[p];
  194. // call to custom user function to update the particle properties
  195. this.updateParticle(this._particle);
  196. // particle rotation matrix
  197. if (this.billboard) {
  198. this._particle.rotation.x = 0.0;
  199. this._particle.rotation.y = 0.0;
  200. }
  201. if (this._setParticleRotation) {
  202. if (this._particle.quaternion) {
  203. this._quaternion.x = this._particle.quaternion.x;
  204. this._quaternion.y = this._particle.quaternion.y;
  205. this._quaternion.z = this._particle.quaternion.z;
  206. this._quaternion.w = this._particle.quaternion.w;
  207. } else {
  208. this._yaw = this._particle.rotation.y;
  209. this._pitch = this._particle.rotation.x;
  210. this._roll = this._particle.rotation.z;
  211. this._quaternionRotationYPR();
  212. }
  213. this._quaternionToRotationMatrix();
  214. }
  215. for (var pt = 0; pt < this._particle._shape.length; pt++) {
  216. idx = index + pt * 3;
  217. colidx = colorIndex + pt * 4;
  218. uvidx = uvIndex + pt * 2;
  219. this._vertex.x = this._particle._shape[pt].x;
  220. this._vertex.y = this._particle._shape[pt].y;
  221. this._vertex.z = this._particle._shape[pt].z;
  222. if (this._setParticleVertex) {
  223. this.updateParticleVertex(this._particle, this._vertex, pt);
  224. }
  225. Vector3.TransformCoordinatesToRef(this._vertex, this._rotMatrix, this._rotated);
  226. this._positions[idx] = this._particle.position.x + this._cam_axisX.x * this._rotated.x * this._particle.scale.x + this._cam_axisY.x * this._rotated.y * this._particle.scale.y + this._cam_axisZ.x * this._rotated.z * this._particle.scale.z;
  227. this._positions[idx + 1] = this._particle.position.y + this._cam_axisX.y * this._rotated.x * this._particle.scale.x + this._cam_axisY.y * this._rotated.y * this._particle.scale.y + this._cam_axisZ.y * this._rotated.z * this._particle.scale.z;
  228. this._positions[idx + 2] = this._particle.position.z + this._cam_axisX.z * this._rotated.x * this._particle.scale.x + this._cam_axisY.z * this._rotated.y * this._particle.scale.y + this._cam_axisZ.z * this._rotated.z * this._particle.scale.z;
  229. if (this._setParticleColor) {
  230. this._colors[colidx] = this._particle.color.r;
  231. this._colors[colidx + 1] = this._particle.color.g;
  232. this._colors[colidx + 2] = this._particle.color.b;
  233. this._colors[colidx + 3] = this._particle.color.a;
  234. }
  235. if (this._setParticleTexture) {
  236. this._uvs[uvidx] = this._particle._shapeUV[pt * 2] * (this._particle.uvs.z - this._particle.uvs.x) + this._particle.uvs.x;
  237. this._uvs[uvidx + 1] = this._particle._shapeUV[pt * 2 + 1] * (this._particle.uvs.w - this._particle.uvs.y) + this._particle.uvs.y;
  238. }
  239. }
  240. index = idx + 3;
  241. colorIndex = colidx + 4;
  242. uvIndex = uvidx + 2;
  243. }
  244. if (update) {
  245. if (this._setParticleColor) {
  246. this.mesh.updateVerticesData(VertexBuffer.ColorKind, this._colors, false, false);
  247. }
  248. if (this._setParticleTexture) {
  249. this.mesh.updateVerticesData(VertexBuffer.UVKind, this._uvs, false, false);
  250. }
  251. this.mesh.updateVerticesData(VertexBuffer.PositionKind, this._positions, false, false);
  252. if (!this.mesh.areNormalsFrozen) {
  253. VertexData.ComputeNormals(this._positions, this._indices, this._normals);
  254. this.mesh.updateVerticesData(VertexBuffer.NormalKind, this._normals, false, false);
  255. }
  256. }
  257. this.afterUpdateParticles(start, end, update);
  258. }
  259. private _quaternionRotationYPR(): void {
  260. this._halfroll = this._roll * 0.5;
  261. this._halfpitch = this._pitch * 0.5;
  262. this._halfyaw = this._yaw * 0.5;
  263. this._sinRoll = Math.sin(this._halfroll);
  264. this._cosRoll = Math.cos(this._halfroll);
  265. this._sinPitch = Math.sin(this._halfpitch);
  266. this._cosPitch = Math.cos(this._halfpitch);
  267. this._sinYaw = Math.sin(this._halfyaw);
  268. this._cosYaw = Math.cos(this._halfyaw);
  269. this._quaternion.x = (this._cosYaw * this._sinPitch * this._cosRoll) + (this._sinYaw * this._cosPitch * this._sinRoll);
  270. this._quaternion.y = (this._sinYaw * this._cosPitch * this._cosRoll) - (this._cosYaw * this._sinPitch * this._sinRoll);
  271. this._quaternion.z = (this._cosYaw * this._cosPitch * this._sinRoll) - (this._sinYaw * this._sinPitch * this._cosRoll);
  272. this._quaternion.w = (this._cosYaw * this._cosPitch * this._cosRoll) + (this._sinYaw * this._sinPitch * this._sinRoll);
  273. }
  274. private _quaternionToRotationMatrix(): void {
  275. this._rotMatrix.m[0] = 1.0 - (2.0 * (this._quaternion.y * this._quaternion.y + this._quaternion.z * this._quaternion.z));
  276. this._rotMatrix.m[1] = 2.0 * (this._quaternion.x * this._quaternion.y + this._quaternion.z * this._quaternion.w);
  277. this._rotMatrix.m[2] = 2.0 * (this._quaternion.z * this._quaternion.x - this._quaternion.y * this._quaternion.w);
  278. this._rotMatrix.m[3] = 0;
  279. this._rotMatrix.m[4] = 2.0 * (this._quaternion.x * this._quaternion.y - this._quaternion.z * this._quaternion.w);
  280. this._rotMatrix.m[5] = 1.0 - (2.0 * (this._quaternion.z * this._quaternion.z + this._quaternion.x * this._quaternion.x));
  281. this._rotMatrix.m[6] = 2.0 * (this._quaternion.y * this._quaternion.z + this._quaternion.x * this._quaternion.w);
  282. this._rotMatrix.m[7] = 0;
  283. this._rotMatrix.m[8] = 2.0 * (this._quaternion.z * this._quaternion.x + this._quaternion.y * this._quaternion.w);
  284. this._rotMatrix.m[9] = 2.0 * (this._quaternion.y * this._quaternion.z - this._quaternion.x * this._quaternion.w);
  285. this._rotMatrix.m[10] = 1.0 - (2.0 * (this._quaternion.y * this._quaternion.y + this._quaternion.x * this._quaternion.x));
  286. this._rotMatrix.m[11] = 0;
  287. this._rotMatrix.m[12] = 0;
  288. this._rotMatrix.m[13] = 0;
  289. this._rotMatrix.m[14] = 0;
  290. this._rotMatrix.m[15] = 1.0;
  291. }
  292. // dispose the SPS
  293. public dispose(): void {
  294. this.mesh.dispose();
  295. }
  296. // Optimizer setters
  297. public set setParticleRotation(val: boolean) {
  298. this._setParticleRotation = val;
  299. }
  300. public set setParticleColor(val: boolean) {
  301. this._setParticleColor = val;
  302. }
  303. public set setParticleTexture(val: boolean) {
  304. this._setParticleTexture = val;
  305. }
  306. public set setParticleVertex(val: boolean) {
  307. this._setParticleVertex = val;
  308. }
  309. // getters
  310. public get setParticleRotation(): boolean {
  311. return this._setParticleRotation;
  312. }
  313. public get setParticleColor(): boolean {
  314. return this._setParticleColor;
  315. }
  316. public get setParticleTexture(): boolean {
  317. return this._setParticleTexture;
  318. }
  319. public get setParticleVertex(): boolean {
  320. return this._setParticleVertex;
  321. }
  322. // =======================================================================
  323. // Particle behavior logic
  324. // these following methods may be overwritten by the user to fit his needs
  325. // init : sets all particles first values and calls updateParticle to set them in space
  326. // can be overwritten by the user
  327. public initParticles(): void {
  328. }
  329. // recycles a particle : can by overwritten by the user
  330. public recycleParticle(particle: SolidParticle): SolidParticle {
  331. return particle;
  332. }
  333. // updates a particle : can be overwritten by the user
  334. // will be called on each particle by setParticles() :
  335. // ex : just set a particle position or velocity and recycle conditions
  336. public updateParticle(particle: SolidParticle): SolidParticle {
  337. return particle;
  338. }
  339. // updates a vertex of a particle : can be overwritten by the user
  340. // will be called on each vertex particle by setParticles() :
  341. // ex : just set a vertex particle position
  342. public updateParticleVertex(particle: SolidParticle, vertex: Vector3, i: number): Vector3 {
  343. return vertex;
  344. }
  345. // will be called before any other treatment by setParticles()
  346. public beforeUpdateParticles(start?: number, stop?: number, update?: boolean): void {
  347. }
  348. // will be called after all setParticles() treatments
  349. public afterUpdateParticles(start?: number, stop?: number, update?: boolean): void {
  350. }
  351. }
  352. }