solidParticleSystem.ts 88 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859
  1. import { Nullable, IndicesArray, FloatArray } from "../types";
  2. import { Vector3, Matrix, TmpVectors, Quaternion } from "../Maths/math.vector";
  3. import { Color4 } from '../Maths/math.color';
  4. import { VertexBuffer } from "../Meshes/buffer";
  5. import { VertexData } from "../Meshes/mesh.vertexData";
  6. import { Mesh } from "../Meshes/mesh";
  7. import { DiscBuilder } from "../Meshes/Builders/discBuilder";
  8. import { EngineStore } from "../Engines/engineStore";
  9. import { Scene, IDisposable } from "../scene";
  10. import { DepthSortedParticle, SolidParticle, ModelShape, SolidParticleVertex } from "./solidParticle";
  11. import { TargetCamera } from "../Cameras/targetCamera";
  12. import { BoundingInfo } from "../Culling/boundingInfo";
  13. import { Axis } from '../Maths/math.axis';
  14. import { SubMesh } from '../Meshes/subMesh';
  15. import { Material } from '../Materials/material';
  16. import { StandardMaterial } from '../Materials/standardMaterial';
  17. import { MultiMaterial } from '../Materials/multiMaterial';
  18. import { PickingInfo } from '../Collisions/pickingInfo';
  19. /**
  20. * The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
  21. *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.
  22. * The SPS is also a particle system. It provides some methods to manage the particles.
  23. * However it is behavior agnostic. This means it has no emitter, no particle physics, no particle recycler. You have to implement your own behavior.
  24. *
  25. * Full documentation here : http://doc.babylonjs.com/how_to/Solid_Particle_System
  26. */
  27. export class SolidParticleSystem implements IDisposable {
  28. /**
  29. * The SPS array of Solid Particle objects. Just access each particle as with any classic array.
  30. * Example : var p = SPS.particles[i];
  31. */
  32. public particles: SolidParticle[] = new Array<SolidParticle>();
  33. /**
  34. * The SPS total number of particles. Read only. Use SPS.counter instead if you need to set your own value.
  35. */
  36. public nbParticles: number = 0;
  37. /**
  38. * If the particles must ever face the camera (default false). Useful for planar particles.
  39. */
  40. public billboard: boolean = false;
  41. /**
  42. * Recompute normals when adding a shape
  43. */
  44. public recomputeNormals: boolean = false;
  45. /**
  46. * This a counter ofr your own usage. It's not set by any SPS functions.
  47. */
  48. public counter: number = 0;
  49. /**
  50. * The SPS name. This name is also given to the underlying mesh.
  51. */
  52. public name: string;
  53. /**
  54. * The SPS mesh. It's a standard BJS Mesh, so all the methods from the Mesh class are avalaible.
  55. */
  56. public mesh: Mesh;
  57. /**
  58. * This empty object is intended to store some SPS specific or temporary values in order to lower the Garbage Collector activity.
  59. * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#garbage-collector-concerns
  60. */
  61. public vars: any = {};
  62. /**
  63. * This array is populated when the SPS is set as 'pickable'.
  64. * Each key of this array is a `faceId` value that you can get from a pickResult object.
  65. * Each element of this array is an object `{idx: int, faceId: int}`.
  66. * `idx` is the picked particle index in the `SPS.particles` array
  67. * `faceId` is the picked face index counted within this particle.
  68. * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
  69. * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
  70. * Use the method SPS.pickedParticle(pickingInfo) instead.
  71. * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
  72. */
  73. public pickedParticles: { idx: number; faceId: number }[];
  74. /**
  75. * This array is populated when the SPS is set as 'pickable'
  76. * Each key of this array is a submesh index.
  77. * Each element of this array is a second array defined like this :
  78. * Each key of this second array is a `faceId` value that you can get from a pickResult object.
  79. * Each element of this second array is an object `{idx: int, faceId: int}`.
  80. * `idx` is the picked particle index in the `SPS.particles` array
  81. * `faceId` is the picked face index counted within this particle.
  82. * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
  83. * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
  84. */
  85. public pickedBySubMesh: { idx: number; faceId: number}[][];
  86. /**
  87. * This array is populated when `enableDepthSort` is set to true.
  88. * Each element of this array is an instance of the class DepthSortedParticle.
  89. */
  90. public depthSortedParticles: DepthSortedParticle[];
  91. /**
  92. * If the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster). (Internal use only)
  93. * @hidden
  94. */
  95. public _bSphereOnly: boolean = false;
  96. /**
  97. * A number to multiply the boundind sphere radius by in order to reduce it for instance. (Internal use only)
  98. * @hidden
  99. */
  100. public _bSphereRadiusFactor: number = 1.0;
  101. private _scene: Scene;
  102. private _positions: number[] = new Array<number>();
  103. private _indices: number[] = new Array<number>();
  104. private _normals: number[] = new Array<number>();
  105. private _colors: number[] = new Array<number>();
  106. private _uvs: number[] = new Array<number>();
  107. private _indices32: IndicesArray; // used as depth sorted array if depth sort enabled, else used as typed indices
  108. private _positions32: Float32Array; // updated positions for the VBO
  109. private _normals32: Float32Array; // updated normals for the VBO
  110. private _fixedNormal32: Float32Array; // initial normal references
  111. private _colors32: Float32Array;
  112. private _uvs32: Float32Array;
  113. private _index: number = 0; // indices index
  114. private _updatable: boolean = true;
  115. private _pickable: boolean = false;
  116. private _isVisibilityBoxLocked = false;
  117. private _alwaysVisible: boolean = false;
  118. private _depthSort: boolean = false;
  119. private _expandable: boolean = false;
  120. private _shapeCounter: number = 0;
  121. private _copy: SolidParticle = new SolidParticle(0, 0, 0, 0, null, 0, 0, this);
  122. private _color: Color4 = new Color4(0, 0, 0, 0);
  123. private _computeParticleColor: boolean = true;
  124. private _computeParticleTexture: boolean = true;
  125. private _computeParticleRotation: boolean = true;
  126. private _computeParticleVertex: boolean = false;
  127. private _computeBoundingBox: boolean = false;
  128. private _depthSortParticles: boolean = true;
  129. private _camera: TargetCamera;
  130. private _mustUnrotateFixedNormals = false;
  131. private _particlesIntersect: boolean = false;
  132. private _needs32Bits: boolean = false;
  133. private _isNotBuilt: boolean = true;
  134. private _lastParticleId: number = 0;
  135. private _idxOfId: number[] = []; // array : key = particle.id / value = particle.idx
  136. private _multimaterialEnabled: boolean = false;
  137. private _useModelMaterial: boolean = false;
  138. private _indicesByMaterial: number[];
  139. private _materialIndexes: number[];
  140. private _depthSortFunction = (p1: DepthSortedParticle, p2: DepthSortedParticle) => p2.sqDistance - p1.sqDistance;
  141. private _materialSortFunction = (p1: DepthSortedParticle, p2: DepthSortedParticle) => p1.materialIndex - p2.materialIndex;
  142. private _materials: Material[];
  143. private _multimaterial: MultiMaterial;
  144. private _materialIndexesById: any;
  145. private _defaultMaterial: Material;
  146. private _autoUpdateSubMeshes: boolean = false;
  147. private _tmpVertex: SolidParticleVertex;
  148. /**
  149. * Creates a SPS (Solid Particle System) object.
  150. * @param name (String) is the SPS name, this will be the underlying mesh name.
  151. * @param scene (Scene) is the scene in which the SPS is added.
  152. * @param options defines the options of the sps e.g.
  153. * * updatable (optional boolean, default true) : if the SPS must be updatable or immutable.
  154. * * isPickable (optional boolean, default false) : if the solid particles must be pickable.
  155. * * enableDepthSort (optional boolean, default false) : if the solid particles must be sorted in the geometry according to their distance to the camera.
  156. * * useModelMaterial (optional boolean, defaut false) : if the model materials must be used to create the SPS multimaterial. This enables the multimaterial supports of the SPS.
  157. * * enableMultiMaterial (optional boolean, default false) : if the solid particles can be given different materials.
  158. * * expandable (optional boolean, default false) : if particles can still be added after the initial SPS mesh creation.
  159. * * particleIntersection (optional boolean, default false) : if the solid particle intersections must be computed.
  160. * * boundingSphereOnly (optional boolean, default false) : if the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster).
  161. * * bSphereRadiusFactor (optional float, default 1.0) : a number to multiply the boundind sphere radius by in order to reduce it for instance.
  162. * @example bSphereRadiusFactor = 1.0 / Math.sqrt(3.0) => the bounding sphere exactly matches a spherical mesh.
  163. */
  164. constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; enableDepthSort?: boolean; particleIntersection?: boolean; boundingSphereOnly?: boolean; bSphereRadiusFactor?: number; expandable?: boolean; useModelMaterial?: boolean; enableMultiMaterial?: boolean; }) {
  165. this.name = name;
  166. this._scene = scene || EngineStore.LastCreatedScene;
  167. this._camera = <TargetCamera>scene.activeCamera;
  168. this._pickable = options ? <boolean>options.isPickable : false;
  169. this._depthSort = options ? <boolean>options.enableDepthSort : false;
  170. this._multimaterialEnabled = options ? <boolean>options.enableMultiMaterial : false;
  171. this._useModelMaterial = options ? <boolean>options.useModelMaterial : false;
  172. this._multimaterialEnabled = (this._useModelMaterial) ? true : this._multimaterialEnabled;
  173. this._expandable = options ? <boolean>options.expandable : false;
  174. this._particlesIntersect = options ? <boolean>options.particleIntersection : false;
  175. this._bSphereOnly = options ? <boolean>options.boundingSphereOnly : false;
  176. this._bSphereRadiusFactor = (options && options.bSphereRadiusFactor) ? options.bSphereRadiusFactor : 1.0;
  177. if (options && options.updatable !== undefined) {
  178. this._updatable = options.updatable;
  179. } else {
  180. this._updatable = true;
  181. }
  182. if (this._pickable) {
  183. this.pickedBySubMesh = [[]];
  184. this.pickedParticles = this.pickedBySubMesh[0];
  185. }
  186. if (this._depthSort || this._multimaterialEnabled) {
  187. this.depthSortedParticles = [];
  188. }
  189. if (this._multimaterialEnabled) {
  190. this._multimaterial = new MultiMaterial(this.name + "MultiMaterial", this._scene);
  191. this._materials = [];
  192. this._materialIndexesById = {};
  193. }
  194. this._tmpVertex = new SolidParticleVertex();
  195. }
  196. /**
  197. * Builds the SPS underlying mesh. Returns a standard Mesh.
  198. * If no model shape was added to the SPS, the returned mesh is just a single triangular plane.
  199. * @returns the created mesh
  200. */
  201. public buildMesh(): Mesh {
  202. if (!this._isNotBuilt && this.mesh) {
  203. return this.mesh;
  204. }
  205. if (this.nbParticles === 0 && !this.mesh) {
  206. var triangle = DiscBuilder.CreateDisc("", { radius: 1, tessellation: 3 }, this._scene);
  207. this.addShape(triangle, 1);
  208. triangle.dispose();
  209. }
  210. this._indices32 = (this._needs32Bits) ? new Uint32Array(this._indices) : new Uint16Array(this._indices);
  211. this._positions32 = new Float32Array(this._positions);
  212. this._uvs32 = new Float32Array(this._uvs);
  213. this._colors32 = new Float32Array(this._colors);
  214. if (!this.mesh) { // in case it's already expanded
  215. var mesh = new Mesh(this.name, this._scene);
  216. this.mesh = mesh;
  217. }
  218. if (!this._updatable && this._multimaterialEnabled) {
  219. this._sortParticlesByMaterial(); // this may reorder the indices32
  220. }
  221. if (this.recomputeNormals) {
  222. VertexData.ComputeNormals(this._positions32, this._indices32, this._normals);
  223. }
  224. this._normals32 = new Float32Array(this._normals);
  225. this._fixedNormal32 = new Float32Array(this._normals);
  226. if (this._mustUnrotateFixedNormals) { // the particles could be created already rotated in the mesh with a positionFunction
  227. this._unrotateFixedNormals();
  228. }
  229. var vertexData = new VertexData();
  230. vertexData.indices = (this._depthSort) ? this._indices : this._indices32;
  231. vertexData.set(this._positions32, VertexBuffer.PositionKind);
  232. vertexData.set(this._normals32, VertexBuffer.NormalKind);
  233. if (this._uvs32.length > 0) {
  234. vertexData.set(this._uvs32, VertexBuffer.UVKind);
  235. }
  236. if (this._colors32.length > 0) {
  237. vertexData.set(this._colors32, VertexBuffer.ColorKind);
  238. }
  239. vertexData.applyToMesh(this.mesh, this._updatable);
  240. this.mesh.isPickable = this._pickable;
  241. if (this._pickable) {
  242. let faceId = 0;
  243. for (let p = 0; p < this.nbParticles; p++) {
  244. let part = this.particles[p];
  245. let lind = part._model._indicesLength;
  246. for (let i = 0; i < lind; i++) {
  247. let f = i % 3;
  248. if (f == 0) {
  249. const pickedData = {idx: part.idx, faceId: faceId};
  250. this.pickedParticles[faceId] = pickedData;
  251. faceId++;
  252. }
  253. }
  254. }
  255. }
  256. if (this._multimaterialEnabled) {
  257. this.setMultiMaterial(this._materials);
  258. }
  259. if (!this._expandable) {
  260. // free memory
  261. if (!this._depthSort && !this._multimaterialEnabled) {
  262. (<any>this._indices) = null;
  263. }
  264. (<any>this._positions) = null;
  265. (<any>this._normals) = null;
  266. (<any>this._uvs) = null;
  267. (<any>this._colors) = null;
  268. if (!this._updatable) {
  269. this.particles.length = 0;
  270. }
  271. }
  272. this._isNotBuilt = false;
  273. this.recomputeNormals = false;
  274. return this.mesh;
  275. }
  276. /**
  277. * Digests the mesh and generates as many solid particles in the system as wanted. Returns the SPS.
  278. * These particles will have the same geometry than the mesh parts and will be positioned at the same localisation than the mesh original places.
  279. * Thus the particles generated from `digest()` have their property `position` set yet.
  280. * @param mesh ( Mesh ) is the mesh to be digested
  281. * @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
  282. * {delta} (optional integer, default 0) is the random extra number of facets per particle , each particle will have between `facetNb` and `facetNb + delta` facets
  283. * {number} (optional positive integer) is the wanted number of particles : each particle is built with `mesh_total_facets / number` facets
  284. * {storage} (optional existing array) is an array where the particles will be stored for a further use instead of being inserted in the SPS.
  285. * @returns the current SPS
  286. */
  287. public digest(mesh: Mesh, options?: { facetNb?: number; number?: number; delta?: number; storage?: [] }): SolidParticleSystem {
  288. var size: number = (options && options.facetNb) || 1;
  289. var number: number = (options && options.number) || 0;
  290. var delta: number = (options && options.delta) || 0;
  291. var meshPos = <FloatArray>mesh.getVerticesData(VertexBuffer.PositionKind);
  292. var meshInd = <IndicesArray>mesh.getIndices();
  293. var meshUV = <FloatArray>mesh.getVerticesData(VertexBuffer.UVKind);
  294. var meshCol = <FloatArray>mesh.getVerticesData(VertexBuffer.ColorKind);
  295. var meshNor = <FloatArray>mesh.getVerticesData(VertexBuffer.NormalKind);
  296. var storage = (options && options.storage) ? options.storage : null;
  297. var f: number = 0; // facet counter
  298. var totalFacets: number = meshInd.length / 3; // a facet is a triangle, so 3 indices
  299. // compute size from number
  300. if (number) {
  301. number = (number > totalFacets) ? totalFacets : number;
  302. size = Math.round(totalFacets / number);
  303. delta = 0;
  304. } else {
  305. size = (size > totalFacets) ? totalFacets : size;
  306. }
  307. var facetPos: number[] = []; // submesh positions
  308. var facetNor: number[] = [];
  309. var facetInd: number[] = []; // submesh indices
  310. var facetUV: number[] = []; // submesh UV
  311. var facetCol: number[] = []; // submesh colors
  312. var barycenter: Vector3 = Vector3.Zero();
  313. var sizeO: number = size;
  314. while (f < totalFacets) {
  315. size = sizeO + Math.floor((1 + delta) * Math.random());
  316. if (f > totalFacets - size) {
  317. size = totalFacets - f;
  318. }
  319. // reset temp arrays
  320. facetPos.length = 0;
  321. facetNor.length = 0;
  322. facetInd.length = 0;
  323. facetUV.length = 0;
  324. facetCol.length = 0;
  325. // iterate over "size" facets
  326. var fi: number = 0;
  327. for (var j = f * 3; j < (f + size) * 3; j++) {
  328. facetInd.push(fi);
  329. var i: number = meshInd[j];
  330. var i3: number = i * 3;
  331. facetPos.push(meshPos[i3], meshPos[i3 + 1], meshPos[i3 + 2]);
  332. facetNor.push(meshNor[i3], meshNor[i3 + 1], meshNor[i3 + 2]);
  333. if (meshUV) {
  334. var i2: number = i * 2;
  335. facetUV.push(meshUV[i2], meshUV[i2 + 1]);
  336. }
  337. if (meshCol) {
  338. var i4: number = i * 4;
  339. facetCol.push(meshCol[i4], meshCol[i4 + 1], meshCol[i4 + 2], meshCol[i4 + 3]);
  340. }
  341. fi++;
  342. }
  343. // create a model shape for each single particle
  344. var idx: number = this.nbParticles;
  345. var shape: Vector3[] = this._posToShape(facetPos);
  346. var shapeUV: number[] = this._uvsToShapeUV(facetUV);
  347. var shapeInd = Array.from(facetInd);
  348. var shapeCol = Array.from(facetCol);
  349. var shapeNor = Array.from(facetNor);
  350. // compute the barycenter of the shape
  351. barycenter.copyFromFloats(0, 0, 0);
  352. var v: number;
  353. for (v = 0; v < shape.length; v++) {
  354. barycenter.addInPlace(shape[v]);
  355. }
  356. barycenter.scaleInPlace(1 / shape.length);
  357. // shift the shape from its barycenter to the origin
  358. // and compute the BBox required for intersection.
  359. var minimum: Vector3 = new Vector3(Infinity, Infinity, Infinity);
  360. var maximum: Vector3 = new Vector3(-Infinity, -Infinity, -Infinity);
  361. for (v = 0; v < shape.length; v++) {
  362. shape[v].subtractInPlace(barycenter);
  363. minimum.minimizeInPlaceFromFloats(shape[v].x, shape[v].y, shape[v].z);
  364. maximum.maximizeInPlaceFromFloats(shape[v].x, shape[v].y, shape[v].z);
  365. }
  366. var bInfo;
  367. if (this._particlesIntersect) {
  368. bInfo = new BoundingInfo(minimum, maximum);
  369. }
  370. var material = null;
  371. if (this._useModelMaterial) {
  372. material = (mesh.material) ? mesh.material : this._setDefaultMaterial();
  373. }
  374. var modelShape = new ModelShape(this._shapeCounter, shape, shapeInd, shapeNor, shapeCol, shapeUV, null, null, material);
  375. // add the particle in the SPS
  376. var currentPos = this._positions.length;
  377. var currentInd = this._indices.length;
  378. this._meshBuilder(this._index, currentInd, shape, this._positions, shapeInd, this._indices, facetUV, this._uvs, shapeCol, this._colors, shapeNor, this._normals, idx, 0, null, modelShape);
  379. this._addParticle(idx, this._lastParticleId, currentPos, currentInd, modelShape, this._shapeCounter, 0, bInfo, storage);
  380. // initialize the particle position
  381. this.particles[this.nbParticles].position.addInPlace(barycenter);
  382. if (!storage) {
  383. this._index += shape.length;
  384. idx++;
  385. this.nbParticles++;
  386. this._lastParticleId++;
  387. }
  388. this._shapeCounter++;
  389. f += size;
  390. }
  391. this._isNotBuilt = true; // buildMesh() is now expected for setParticles() to work
  392. return this;
  393. }
  394. /**
  395. * Unrotate the fixed normals in case the mesh was built with pre-rotated particles, ex : use of positionFunction in addShape()
  396. * @hidden
  397. */
  398. private _unrotateFixedNormals() {
  399. var index = 0;
  400. var idx = 0;
  401. const tmpNormal = TmpVectors.Vector3[0];
  402. const quaternion = TmpVectors.Quaternion[0];
  403. const invertedRotMatrix = TmpVectors.Matrix[0];
  404. for (var p = 0; p < this.particles.length; p++) {
  405. const particle = this.particles[p];
  406. const shape = particle._model._shape;
  407. // computing the inverse of the rotation matrix from the quaternion
  408. // is equivalent to computing the matrix of the inverse quaternion, i.e of the conjugate quaternion
  409. if (particle.rotationQuaternion) {
  410. particle.rotationQuaternion.conjugateToRef(quaternion);
  411. }
  412. else {
  413. const rotation = particle.rotation;
  414. Quaternion.RotationYawPitchRollToRef(rotation.y, rotation.x, rotation.z, quaternion);
  415. quaternion.conjugateInPlace();
  416. }
  417. quaternion.toRotationMatrix(invertedRotMatrix);
  418. for (var pt = 0; pt < shape.length; pt++) {
  419. idx = index + pt * 3;
  420. Vector3.TransformNormalFromFloatsToRef(this._normals32[idx], this._normals32[idx + 1], this._normals32[idx + 2], invertedRotMatrix, tmpNormal);
  421. tmpNormal.toArray(this._fixedNormal32, idx);
  422. }
  423. index = idx + 3;
  424. }
  425. }
  426. /**
  427. * Resets the temporary working copy particle
  428. * @hidden
  429. */
  430. private _resetCopy() {
  431. const copy = this._copy;
  432. copy.position.setAll(0);
  433. copy.rotation.setAll(0);
  434. copy.rotationQuaternion = null;
  435. copy.scaling.setAll(1);
  436. copy.uvs.copyFromFloats(0.0, 0.0, 1.0, 1.0);
  437. copy.color = null;
  438. copy.translateFromPivot = false;
  439. copy.shapeId = 0;
  440. copy.materialIndex = null;
  441. }
  442. /**
  443. * Inserts the shape model geometry in the global SPS mesh by updating the positions, indices, normals, colors, uvs arrays
  444. * @param p the current index in the positions array to be updated
  445. * @param ind the current index in the indices array
  446. * @param shape a Vector3 array, the shape geometry
  447. * @param positions the positions array to be updated
  448. * @param meshInd the shape indices array
  449. * @param indices the indices array to be updated
  450. * @param meshUV the shape uv array
  451. * @param uvs the uv array to be updated
  452. * @param meshCol the shape color array
  453. * @param colors the color array to be updated
  454. * @param meshNor the shape normals array
  455. * @param normals the normals array to be updated
  456. * @param idx the particle index
  457. * @param idxInShape the particle index in its shape
  458. * @param options the addShape() method passed options
  459. * @model the particle model
  460. * @hidden
  461. */
  462. private _meshBuilder(p: number, ind: 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, model: ModelShape): SolidParticle {
  463. var i;
  464. var u = 0;
  465. var c = 0;
  466. var n = 0;
  467. this._resetCopy();
  468. const copy = this._copy;
  469. const storeApart = (options && options.storage) ? true : false;
  470. copy.idx = idx;
  471. copy.idxInShape = idxInShape;
  472. copy.shapeId = model.shapeID;
  473. if (this._useModelMaterial) {
  474. var materialId = model._material!.uniqueId;
  475. const materialIndexesById = this._materialIndexesById;
  476. if (!materialIndexesById.hasOwnProperty(materialId)) {
  477. materialIndexesById[materialId] = this._materials.length;
  478. this._materials.push(model._material!);
  479. }
  480. var matIdx = materialIndexesById[materialId];
  481. copy.materialIndex = matIdx;
  482. }
  483. if (options && options.positionFunction) { // call to custom positionFunction
  484. options.positionFunction(copy, idx, idxInShape);
  485. this._mustUnrotateFixedNormals = true;
  486. }
  487. // in case the particle geometry must NOT be inserted in the SPS mesh geometry
  488. if (storeApart) {
  489. return copy;
  490. }
  491. const rotMatrix = TmpVectors.Matrix[0];
  492. const tmpVertex = this._tmpVertex;
  493. const tmpVector = tmpVertex.position;
  494. const tmpColor = tmpVertex.color;
  495. const tmpUV = tmpVertex.uv;
  496. const tmpRotated = TmpVectors.Vector3[1];
  497. const pivotBackTranslation = TmpVectors.Vector3[2];
  498. const scaledPivot = TmpVectors.Vector3[3];
  499. Matrix.IdentityToRef(rotMatrix);
  500. copy.getRotationMatrix(rotMatrix);
  501. copy.pivot.multiplyToRef(copy.scaling, scaledPivot);
  502. if (copy.translateFromPivot) {
  503. pivotBackTranslation.setAll(0.0);
  504. }
  505. else {
  506. pivotBackTranslation.copyFrom(scaledPivot);
  507. }
  508. var someVertexFunction = (options && options.vertexFunction);
  509. for (i = 0; i < shape.length; i++) {
  510. tmpVector.copyFrom(shape[i]);
  511. if (copy.color) {
  512. tmpColor.copyFrom(copy.color);
  513. }
  514. if (meshUV) {
  515. tmpUV.copyFromFloats(meshUV[u], meshUV[u + 1]);
  516. }
  517. if (someVertexFunction) {
  518. options.vertexFunction(copy, tmpVertex, i);
  519. }
  520. tmpVector.multiplyInPlace(copy.scaling).subtractInPlace(scaledPivot);
  521. Vector3.TransformCoordinatesToRef(tmpVector, rotMatrix, tmpRotated);
  522. tmpRotated.addInPlace(pivotBackTranslation).addInPlace(copy.position);
  523. positions.push(tmpRotated.x, tmpRotated.y, tmpRotated.z);
  524. if (meshUV) {
  525. const copyUvs = copy.uvs;
  526. uvs.push((copyUvs.z - copyUvs.x) * tmpUV.x + copyUvs.x, (copyUvs.w - copyUvs.y) * tmpUV.y + copyUvs.y);
  527. u += 2;
  528. }
  529. if (copy.color) {
  530. this._color.copyFrom(tmpColor);
  531. } else {
  532. const color = this._color;
  533. if (meshCol && meshCol[c] !== undefined) {
  534. color.r = meshCol[c];
  535. color.g = meshCol[c + 1];
  536. color.b = meshCol[c + 2];
  537. color.a = meshCol[c + 3];
  538. } else {
  539. color.r = 1.0;
  540. color.g = 1.0;
  541. color.b = 1.0;
  542. color.a = 1.0;
  543. }
  544. }
  545. colors.push(this._color.r, this._color.g, this._color.b, this._color.a);
  546. c += 4;
  547. if (!this.recomputeNormals && meshNor) {
  548. Vector3.TransformNormalFromFloatsToRef(meshNor[n], meshNor[n + 1], meshNor[n + 2], rotMatrix, tmpVector);
  549. normals.push(tmpVector.x, tmpVector.y, tmpVector.z);
  550. n += 3;
  551. }
  552. }
  553. for (i = 0; i < meshInd.length; i++) {
  554. var current_ind = p + meshInd[i];
  555. indices.push(current_ind);
  556. if (current_ind > 65535) {
  557. this._needs32Bits = true;
  558. }
  559. }
  560. if (this._depthSort || this._multimaterialEnabled) {
  561. var matIndex = (copy.materialIndex !== null) ? copy.materialIndex : 0;
  562. this.depthSortedParticles.push(new DepthSortedParticle(idx, ind, meshInd.length, matIndex));
  563. }
  564. return copy;
  565. }
  566. /**
  567. * Returns a shape Vector3 array from positions float array
  568. * @param positions float array
  569. * @returns a vector3 array
  570. * @hidden
  571. */
  572. private _posToShape(positions: number[] | Float32Array): Vector3[] {
  573. var shape = [];
  574. for (var i = 0; i < positions.length; i += 3) {
  575. shape.push(Vector3.FromArray(positions, i));
  576. }
  577. return shape;
  578. }
  579. /**
  580. * Returns a shapeUV array from a float uvs (array deep copy)
  581. * @param uvs as a float array
  582. * @returns a shapeUV array
  583. * @hidden
  584. */
  585. private _uvsToShapeUV(uvs: number[] | Float32Array): number[] {
  586. var shapeUV = [];
  587. if (uvs) {
  588. for (var i = 0; i < uvs.length; i++) {
  589. shapeUV.push(uvs[i]);
  590. }
  591. }
  592. return shapeUV;
  593. }
  594. /**
  595. * Adds a new particle object in the particles array
  596. * @param idx particle index in particles array
  597. * @param id particle id
  598. * @param idxpos positionIndex : the starting index of the particle vertices in the SPS "positions" array
  599. * @param idxind indiceIndex : he starting index of the particle indices in the SPS "indices" array
  600. * @param model particle ModelShape object
  601. * @param shapeId model shape identifier
  602. * @param idxInShape index of the particle in the current model
  603. * @param bInfo model bounding info object
  604. * @param storage target storage array, if any
  605. * @hidden
  606. */
  607. private _addParticle(idx: number, id: number, idxpos: number, idxind: number, model: ModelShape, shapeId: number, idxInShape: number, bInfo: Nullable<BoundingInfo> = null, storage: Nullable<[]> = null): SolidParticle {
  608. var sp = new SolidParticle(idx, id, idxpos, idxind, model, shapeId, idxInShape, this, bInfo);
  609. var target = (storage) ? storage : this.particles;
  610. target.push(sp);
  611. return sp;
  612. }
  613. /**
  614. * Adds some particles to the SPS from the model shape. Returns the shape id.
  615. * Please read the doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#create-an-immutable-sps
  616. * @param mesh is any Mesh object that will be used as a model for the solid particles.
  617. * @param nb (positive integer) the number of particles to be created from this model
  618. * @param options {positionFunction} is an optional javascript function to called for each particle on SPS creation.
  619. * {vertexFunction} is an optional javascript function to called for each vertex of each particle on SPS creation
  620. * {storage} (optional existing array) is an array where the particles will be stored for a further use instead of being inserted in the SPS.
  621. * @returns the number of shapes in the system
  622. */
  623. public addShape(mesh: Mesh, nb: number, options?: { positionFunction?: any; vertexFunction?: any; storage?: [] }): number {
  624. var meshPos = <FloatArray>mesh.getVerticesData(VertexBuffer.PositionKind);
  625. var meshInd = <IndicesArray>mesh.getIndices();
  626. var meshUV = <FloatArray>mesh.getVerticesData(VertexBuffer.UVKind);
  627. var meshCol = <FloatArray>mesh.getVerticesData(VertexBuffer.ColorKind);
  628. var meshNor = <FloatArray>mesh.getVerticesData(VertexBuffer.NormalKind);
  629. this.recomputeNormals = (meshNor) ? false : true;
  630. var indices = Array.from(meshInd);
  631. var shapeNormals = Array.from(meshNor);
  632. var shapeColors = (meshCol) ? Array.from(meshCol) : [];
  633. var storage = (options && options.storage) ? options.storage : null;
  634. var bbInfo: Nullable<BoundingInfo> = null;
  635. if (this._particlesIntersect) {
  636. bbInfo = mesh.getBoundingInfo();
  637. }
  638. var shape = this._posToShape(meshPos);
  639. var shapeUV = this._uvsToShapeUV(meshUV);
  640. var posfunc = options ? options.positionFunction : null;
  641. var vtxfunc = options ? options.vertexFunction : null;
  642. var material = null;
  643. if (this._useModelMaterial) {
  644. material = (mesh.material) ? mesh.material : this._setDefaultMaterial();
  645. }
  646. var modelShape = new ModelShape(this._shapeCounter, shape, indices, shapeNormals, shapeColors, shapeUV, posfunc, vtxfunc, material);
  647. // particles
  648. for (var i = 0; i < nb; i++) {
  649. this._insertNewParticle(this.nbParticles, i, modelShape, shape, meshInd, meshUV, meshCol, meshNor, bbInfo, storage, options);
  650. }
  651. this._shapeCounter++;
  652. this._isNotBuilt = true; // buildMesh() call is now expected for setParticles() to work
  653. return this._shapeCounter - 1;
  654. }
  655. /**
  656. * Rebuilds a particle back to its just built status : if needed, recomputes the custom positions and vertices
  657. * @hidden
  658. */
  659. private _rebuildParticle(particle: SolidParticle, reset: boolean = false): void {
  660. this._resetCopy();
  661. const copy = this._copy;
  662. if (particle._model._positionFunction) { // recall to stored custom positionFunction
  663. particle._model._positionFunction(copy, particle.idx, particle.idxInShape);
  664. }
  665. const rotMatrix = TmpVectors.Matrix[0];
  666. const tmpVertex = TmpVectors.Vector3[0];
  667. const tmpRotated = TmpVectors.Vector3[1];
  668. const pivotBackTranslation = TmpVectors.Vector3[2];
  669. const scaledPivot = TmpVectors.Vector3[3];
  670. copy.getRotationMatrix(rotMatrix);
  671. particle.pivot.multiplyToRef(particle.scaling, scaledPivot);
  672. if (copy.translateFromPivot) {
  673. pivotBackTranslation.copyFromFloats(0.0, 0.0, 0.0);
  674. }
  675. else {
  676. pivotBackTranslation.copyFrom(scaledPivot);
  677. }
  678. const shape = particle._model._shape;
  679. for (var pt = 0; pt < shape.length; pt++) {
  680. tmpVertex.copyFrom(shape[pt]);
  681. if (particle._model._vertexFunction) {
  682. particle._model._vertexFunction(copy, tmpVertex, pt); // recall to stored vertexFunction
  683. }
  684. tmpVertex.multiplyInPlace(copy.scaling).subtractInPlace(scaledPivot);
  685. Vector3.TransformCoordinatesToRef(tmpVertex, rotMatrix, tmpRotated);
  686. tmpRotated.addInPlace(pivotBackTranslation).addInPlace(copy.position).toArray(this._positions32, particle._pos + pt * 3);
  687. }
  688. if (reset) {
  689. particle.position.setAll(0.0);
  690. particle.rotation.setAll(0.0);
  691. particle.rotationQuaternion = null;
  692. particle.scaling.setAll(1.0);
  693. particle.uvs.setAll(0.0);
  694. particle.pivot.setAll(0.0);
  695. particle.translateFromPivot = false;
  696. particle.parentId = null;
  697. }
  698. }
  699. /**
  700. * Rebuilds the whole mesh and updates the VBO : custom positions and vertices are recomputed if needed.
  701. * @param reset boolean, default false : if the particles must be reset at position and rotation zero, scaling 1, color white, initial UVs and not parented.
  702. * @returns the SPS.
  703. */
  704. public rebuildMesh(reset: boolean = false): SolidParticleSystem {
  705. for (var p = 0; p < this.particles.length; p++) {
  706. this._rebuildParticle(this.particles[p], reset);
  707. }
  708. this.mesh.updateVerticesData(VertexBuffer.PositionKind, this._positions32, false, false);
  709. return this;
  710. }
  711. /** Removes the particles from the start-th to the end-th included from an expandable SPS (required).
  712. * Returns an array with the removed particles.
  713. * If the number of particles to remove is lower than zero or greater than the global remaining particle number, then an empty array is returned.
  714. * The SPS can't be empty so at least one particle needs to remain in place.
  715. * Under the hood, the VertexData array, so the VBO buffer, is recreated each call.
  716. * @param start index of the first particle to remove
  717. * @param end index of the last particle to remove (included)
  718. * @returns an array populated with the removed particles
  719. */
  720. public removeParticles(start: number, end: number): SolidParticle[] {
  721. var nb = end - start + 1;
  722. if (!this._expandable || nb <= 0 || nb >= this.nbParticles || !this._updatable) {
  723. return [];
  724. }
  725. const particles = this.particles;
  726. const currentNb = this.nbParticles;
  727. if (end < currentNb - 1) { // update the particle indexes in the positions array in case they're remaining particles after the last removed
  728. var firstRemaining = end + 1;
  729. var shiftPos = particles[firstRemaining]._pos - particles[start]._pos;
  730. var shifInd = particles[firstRemaining]._ind - particles[start]._ind;
  731. for (var i = firstRemaining; i < currentNb; i++) {
  732. var part = particles[i];
  733. part._pos -= shiftPos;
  734. part._ind -= shifInd;
  735. }
  736. }
  737. var removed = particles.splice(start, nb);
  738. this._positions.length = 0;
  739. this._indices.length = 0;
  740. this._colors.length = 0;
  741. this._uvs.length = 0;
  742. this._normals.length = 0;
  743. this._index = 0;
  744. this._idxOfId.length = 0;
  745. if (this._depthSort || this._multimaterialEnabled) {
  746. this.depthSortedParticles = [];
  747. }
  748. var ind = 0;
  749. const particlesLength = particles.length;
  750. for (var p = 0; p < particlesLength; p++) {
  751. var particle = particles[p];
  752. var model = particle._model;
  753. var shape = model._shape;
  754. var modelIndices = model._indices;
  755. var modelNormals = model._normals;
  756. var modelColors = model._shapeColors;
  757. var modelUVs = model._shapeUV;
  758. particle.idx = p;
  759. this._idxOfId[particle.id] = p;
  760. this._meshBuilder(this._index, ind, shape, this._positions, modelIndices, this._indices, modelUVs, this._uvs, modelColors, this._colors, modelNormals, this._normals, particle.idx, particle.idxInShape, null, model);
  761. this._index += shape.length;
  762. ind += modelIndices.length;
  763. }
  764. this.nbParticles -= nb;
  765. this._isNotBuilt = true; // buildMesh() call is now expected for setParticles() to work
  766. return removed;
  767. }
  768. /**
  769. * Inserts some pre-created particles in the solid particle system so that they can be managed by setParticles().
  770. * @param solidParticleArray an array populated with Solid Particles objects
  771. * @returns the SPS
  772. */
  773. public insertParticlesFromArray(solidParticleArray: SolidParticle[]): SolidParticleSystem {
  774. if (!this._expandable) {
  775. return this;
  776. }
  777. var idxInShape = 0;
  778. var currentShapeId = solidParticleArray[0].shapeId;
  779. const nb = solidParticleArray.length;
  780. for (var i = 0; i < nb; i++) {
  781. var sp = solidParticleArray[i];
  782. var model = sp._model;
  783. var shape = model._shape;
  784. var meshInd = model._indices;
  785. var meshUV = model._shapeUV;
  786. var meshCol = model._shapeColors;
  787. var meshNor = model._normals;
  788. var noNor = (meshNor) ? false : true;
  789. this.recomputeNormals = (noNor || this.recomputeNormals);
  790. var bbInfo = sp._boundingInfo;
  791. var newPart = this._insertNewParticle(this.nbParticles, idxInShape, model, shape, meshInd, meshUV, meshCol, meshNor, bbInfo, null, null);
  792. sp.copyToRef(newPart!);
  793. idxInShape++;
  794. if (currentShapeId != sp.shapeId) {
  795. currentShapeId = sp.shapeId;
  796. idxInShape = 0;
  797. }
  798. }
  799. this._isNotBuilt = true; // buildMesh() call is now expected for setParticles() to work
  800. return this;
  801. }
  802. /**
  803. * Creates a new particle and modifies the SPS mesh geometry :
  804. * - calls _meshBuilder() to increase the SPS mesh geometry step by step
  805. * - calls _addParticle() to populate the particle array
  806. * factorized code from addShape() and insertParticlesFromArray()
  807. * @param idx particle index in the particles array
  808. * @param i particle index in its shape
  809. * @param modelShape particle ModelShape object
  810. * @param shape shape vertex array
  811. * @param meshInd shape indices array
  812. * @param meshUV shape uv array
  813. * @param meshCol shape color array
  814. * @param meshNor shape normals array
  815. * @param bbInfo shape bounding info
  816. * @param storage target particle storage
  817. * @options addShape() passed options
  818. * @hidden
  819. */
  820. private _insertNewParticle(idx: number, i: number, modelShape: ModelShape, shape: Vector3[], meshInd: IndicesArray, meshUV: number[] | Float32Array, meshCol: number[] | Float32Array, meshNor: number[] | Float32Array, bbInfo: Nullable<BoundingInfo>, storage: Nullable<[]> , options: any): Nullable<SolidParticle> {
  821. var currentPos = this._positions.length;
  822. var currentInd = this._indices.length;
  823. var currentCopy = this._meshBuilder(this._index, currentInd, shape, this._positions, meshInd, this._indices, meshUV, this._uvs, meshCol, this._colors, meshNor, this._normals, idx, i, options, modelShape);
  824. var sp: Nullable<SolidParticle> = null;
  825. if (this._updatable) {
  826. sp = this._addParticle(this.nbParticles, this._lastParticleId, currentPos, currentInd, modelShape, this._shapeCounter, i, bbInfo, storage);
  827. sp.position.copyFrom(currentCopy.position);
  828. sp.rotation.copyFrom(currentCopy.rotation);
  829. if (currentCopy.rotationQuaternion) {
  830. if (sp.rotationQuaternion) {
  831. sp.rotationQuaternion.copyFrom(currentCopy.rotationQuaternion);
  832. }
  833. else {
  834. sp.rotationQuaternion = currentCopy.rotationQuaternion.clone();
  835. }
  836. }
  837. if (currentCopy.color) {
  838. if (sp.color) {
  839. sp.color.copyFrom(currentCopy.color);
  840. }
  841. else {
  842. sp.color = currentCopy.color.clone();
  843. }
  844. }
  845. sp.scaling.copyFrom(currentCopy.scaling);
  846. sp.uvs.copyFrom(currentCopy.uvs);
  847. if (currentCopy.materialIndex !== null) {
  848. sp.materialIndex = currentCopy.materialIndex;
  849. }
  850. if (this.expandable) {
  851. this._idxOfId[sp.id] = sp.idx;
  852. }
  853. }
  854. if (!storage) {
  855. this._index += shape.length;
  856. this.nbParticles++;
  857. this._lastParticleId++;
  858. }
  859. return sp;
  860. }
  861. /**
  862. * Sets all the particles : this method actually really updates the mesh according to the particle positions, rotations, colors, textures, etc.
  863. * This method calls `updateParticle()` for each particle of the SPS.
  864. * For an animated SPS, it is usually called within the render loop.
  865. * This methods does nothing if called on a non updatable or not yet built SPS. Example : buildMesh() not called after having added or removed particles from an expandable SPS.
  866. * @param start The particle index in the particle array where to start to compute the particle property values _(default 0)_
  867. * @param end The particle index in the particle array where to stop to compute the particle property values _(default nbParticle - 1)_
  868. * @param update If the mesh must be finally updated on this call after all the particle computations _(default true)_
  869. * @returns the SPS.
  870. */
  871. public setParticles(start: number = 0, end: number = this.nbParticles - 1, update: boolean = true): SolidParticleSystem {
  872. if (!this._updatable || this._isNotBuilt) {
  873. return this;
  874. }
  875. // custom beforeUpdate
  876. this.beforeUpdateParticles(start, end, update);
  877. const rotMatrix = TmpVectors.Matrix[0];
  878. const invertedMatrix = TmpVectors.Matrix[1];
  879. const mesh = this.mesh;
  880. const colors32 = this._colors32;
  881. const positions32 = this._positions32;
  882. const normals32 = this._normals32;
  883. const uvs32 = this._uvs32;
  884. const indices32 = this._indices32;
  885. const indices = this._indices;
  886. const fixedNormal32 = this._fixedNormal32;
  887. const tempVectors = TmpVectors.Vector3;
  888. const camAxisX = tempVectors[5].copyFromFloats(1.0, 0.0, 0.0);
  889. const camAxisY = tempVectors[6].copyFromFloats(0.0, 1.0, 0.0);
  890. const camAxisZ = tempVectors[7].copyFromFloats(0.0, 0.0, 1.0);
  891. const minimum = tempVectors[8].setAll(Number.MAX_VALUE);
  892. const maximum = tempVectors[9].setAll(-Number.MAX_VALUE);
  893. const camInvertedPosition = tempVectors[10].setAll(0);
  894. const tmpVertex = this._tmpVertex;
  895. const tmpVector = tmpVertex.position;
  896. const tmpColor = tmpVertex.color;
  897. const tmpUV = tmpVertex.uv;
  898. // cases when the World Matrix is to be computed first
  899. if (this.billboard || this._depthSort) {
  900. this.mesh.computeWorldMatrix(true);
  901. this.mesh._worldMatrix.invertToRef(invertedMatrix);
  902. }
  903. // if the particles will always face the camera
  904. if (this.billboard) {
  905. // compute the camera position and un-rotate it by the current mesh rotation
  906. const tmpVector0 = tempVectors[0];
  907. this._camera.getDirectionToRef(Axis.Z, tmpVector0);
  908. Vector3.TransformNormalToRef(tmpVector0, invertedMatrix, camAxisZ);
  909. camAxisZ.normalize();
  910. // same for camera up vector extracted from the cam view matrix
  911. var view = this._camera.getViewMatrix(true);
  912. Vector3.TransformNormalFromFloatsToRef(view.m[1], view.m[5], view.m[9], invertedMatrix, camAxisY);
  913. Vector3.CrossToRef(camAxisY, camAxisZ, camAxisX);
  914. camAxisY.normalize();
  915. camAxisX.normalize();
  916. }
  917. // if depthSort, compute the camera global position in the mesh local system
  918. if (this._depthSort) {
  919. Vector3.TransformCoordinatesToRef(this._camera.globalPosition, invertedMatrix, camInvertedPosition); // then un-rotate the camera
  920. }
  921. Matrix.IdentityToRef(rotMatrix);
  922. var idx = 0; // current position index in the global array positions32
  923. var index = 0; // position start index in the global array positions32 of the current particle
  924. var colidx = 0; // current color index in the global array colors32
  925. var colorIndex = 0; // color start index in the global array colors32 of the current particle
  926. var uvidx = 0; // current uv index in the global array uvs32
  927. var uvIndex = 0; // uv start index in the global array uvs32 of the current particle
  928. var pt = 0; // current index in the particle model shape
  929. if (this.mesh.isFacetDataEnabled) {
  930. this._computeBoundingBox = true;
  931. }
  932. end = (end >= this.nbParticles) ? this.nbParticles - 1 : end;
  933. if (this._computeBoundingBox) {
  934. if (start != 0 || end != this.nbParticles - 1) { // only some particles are updated, then use the current existing BBox basis. Note : it can only increase.
  935. const boundingInfo = this.mesh._boundingInfo;
  936. if (boundingInfo) {
  937. minimum.copyFrom(boundingInfo.minimum);
  938. maximum.copyFrom(boundingInfo.maximum);
  939. }
  940. }
  941. }
  942. // particle loop
  943. index = this.particles[start]._pos;
  944. const vpos = (index / 3) | 0;
  945. colorIndex = vpos * 4;
  946. uvIndex = vpos * 2;
  947. for (var p = start; p <= end; p++) {
  948. const particle = this.particles[p];
  949. // call to custom user function to update the particle properties
  950. this.updateParticle(particle);
  951. const shape = particle._model._shape;
  952. const shapeUV = particle._model._shapeUV;
  953. const particleRotationMatrix = particle._rotationMatrix;
  954. const particlePosition = particle.position;
  955. const particleRotation = particle.rotation;
  956. const particleScaling = particle.scaling;
  957. const particleGlobalPosition = particle._globalPosition;
  958. // camera-particle distance for depth sorting
  959. if (this._depthSort && this._depthSortParticles) {
  960. var dsp = this.depthSortedParticles[p];
  961. dsp.idx = particle.idx;
  962. dsp.ind = particle._ind;
  963. dsp.indicesLength = particle._model._indicesLength;
  964. dsp.sqDistance = Vector3.DistanceSquared(particle.position, camInvertedPosition);
  965. }
  966. // skip the computations for inactive or already invisible particles
  967. if (!particle.alive || (particle._stillInvisible && !particle.isVisible)) {
  968. // increment indexes for the next particle
  969. pt = shape.length;
  970. index += pt * 3;
  971. colorIndex += pt * 4;
  972. uvIndex += pt * 2;
  973. continue;
  974. }
  975. if (particle.isVisible) {
  976. particle._stillInvisible = false; // un-mark permanent invisibility
  977. const scaledPivot = tempVectors[12];
  978. particle.pivot.multiplyToRef(particleScaling, scaledPivot);
  979. // particle rotation matrix
  980. if (this.billboard) {
  981. particleRotation.x = 0.0;
  982. particleRotation.y = 0.0;
  983. }
  984. if (this._computeParticleRotation || this.billboard) {
  985. particle.getRotationMatrix(rotMatrix);
  986. }
  987. const particleHasParent = (particle.parentId !== null);
  988. if (particleHasParent) {
  989. const parent = this.getParticleById(particle.parentId!);
  990. if (parent) {
  991. const parentRotationMatrix = parent._rotationMatrix;
  992. const parentGlobalPosition = parent._globalPosition;
  993. const rotatedY = particlePosition.x * parentRotationMatrix[1] + particlePosition.y * parentRotationMatrix[4] + particlePosition.z * parentRotationMatrix[7];
  994. const rotatedX = particlePosition.x * parentRotationMatrix[0] + particlePosition.y * parentRotationMatrix[3] + particlePosition.z * parentRotationMatrix[6];
  995. const rotatedZ = particlePosition.x * parentRotationMatrix[2] + particlePosition.y * parentRotationMatrix[5] + particlePosition.z * parentRotationMatrix[8];
  996. particleGlobalPosition.x = parentGlobalPosition.x + rotatedX;
  997. particleGlobalPosition.y = parentGlobalPosition.y + rotatedY;
  998. particleGlobalPosition.z = parentGlobalPosition.z + rotatedZ;
  999. if (this._computeParticleRotation || this.billboard) {
  1000. const rotMatrixValues = rotMatrix.m;
  1001. particleRotationMatrix[0] = rotMatrixValues[0] * parentRotationMatrix[0] + rotMatrixValues[1] * parentRotationMatrix[3] + rotMatrixValues[2] * parentRotationMatrix[6];
  1002. particleRotationMatrix[1] = rotMatrixValues[0] * parentRotationMatrix[1] + rotMatrixValues[1] * parentRotationMatrix[4] + rotMatrixValues[2] * parentRotationMatrix[7];
  1003. particleRotationMatrix[2] = rotMatrixValues[0] * parentRotationMatrix[2] + rotMatrixValues[1] * parentRotationMatrix[5] + rotMatrixValues[2] * parentRotationMatrix[8];
  1004. particleRotationMatrix[3] = rotMatrixValues[4] * parentRotationMatrix[0] + rotMatrixValues[5] * parentRotationMatrix[3] + rotMatrixValues[6] * parentRotationMatrix[6];
  1005. particleRotationMatrix[4] = rotMatrixValues[4] * parentRotationMatrix[1] + rotMatrixValues[5] * parentRotationMatrix[4] + rotMatrixValues[6] * parentRotationMatrix[7];
  1006. particleRotationMatrix[5] = rotMatrixValues[4] * parentRotationMatrix[2] + rotMatrixValues[5] * parentRotationMatrix[5] + rotMatrixValues[6] * parentRotationMatrix[8];
  1007. particleRotationMatrix[6] = rotMatrixValues[8] * parentRotationMatrix[0] + rotMatrixValues[9] * parentRotationMatrix[3] + rotMatrixValues[10] * parentRotationMatrix[6];
  1008. particleRotationMatrix[7] = rotMatrixValues[8] * parentRotationMatrix[1] + rotMatrixValues[9] * parentRotationMatrix[4] + rotMatrixValues[10] * parentRotationMatrix[7];
  1009. particleRotationMatrix[8] = rotMatrixValues[8] * parentRotationMatrix[2] + rotMatrixValues[9] * parentRotationMatrix[5] + rotMatrixValues[10] * parentRotationMatrix[8];
  1010. }
  1011. }
  1012. else { // in case the parent were removed at some moment
  1013. particle.parentId = null;
  1014. }
  1015. }
  1016. else {
  1017. particleGlobalPosition.x = particlePosition.x;
  1018. particleGlobalPosition.y = particlePosition.y;
  1019. particleGlobalPosition.z = particlePosition.z;
  1020. if (this._computeParticleRotation || this.billboard) {
  1021. const rotMatrixValues = rotMatrix.m;
  1022. particleRotationMatrix[0] = rotMatrixValues[0];
  1023. particleRotationMatrix[1] = rotMatrixValues[1];
  1024. particleRotationMatrix[2] = rotMatrixValues[2];
  1025. particleRotationMatrix[3] = rotMatrixValues[4];
  1026. particleRotationMatrix[4] = rotMatrixValues[5];
  1027. particleRotationMatrix[5] = rotMatrixValues[6];
  1028. particleRotationMatrix[6] = rotMatrixValues[8];
  1029. particleRotationMatrix[7] = rotMatrixValues[9];
  1030. particleRotationMatrix[8] = rotMatrixValues[10];
  1031. }
  1032. }
  1033. const pivotBackTranslation = tempVectors[11];
  1034. if (particle.translateFromPivot) {
  1035. pivotBackTranslation.setAll(0.0);
  1036. }
  1037. else {
  1038. pivotBackTranslation.copyFrom(scaledPivot);
  1039. }
  1040. // particle vertex loop
  1041. for (pt = 0; pt < shape.length; pt++) {
  1042. idx = index + pt * 3;
  1043. colidx = colorIndex + pt * 4;
  1044. uvidx = uvIndex + pt * 2;
  1045. const iu = 2 * pt;
  1046. const iv = iu + 1;
  1047. tmpVector.copyFrom(shape[pt]);
  1048. if (this._computeParticleColor && particle.color) {
  1049. tmpColor.copyFrom(particle.color);
  1050. }
  1051. if (this._computeParticleTexture) {
  1052. tmpUV.copyFromFloats(shapeUV[iu], shapeUV[iv]);
  1053. }
  1054. if (this._computeParticleVertex) {
  1055. this.updateParticleVertex(particle, tmpVertex, pt);
  1056. }
  1057. // positions
  1058. const vertexX = tmpVector.x * particleScaling.x - scaledPivot.x;
  1059. const vertexY = tmpVector.y * particleScaling.y - scaledPivot.y;
  1060. const vertexZ = tmpVector.z * particleScaling.z - scaledPivot.z;
  1061. let rotatedX = vertexX * particleRotationMatrix[0] + vertexY * particleRotationMatrix[3] + vertexZ * particleRotationMatrix[6];
  1062. let rotatedY = vertexX * particleRotationMatrix[1] + vertexY * particleRotationMatrix[4] + vertexZ * particleRotationMatrix[7];
  1063. let rotatedZ = vertexX * particleRotationMatrix[2] + vertexY * particleRotationMatrix[5] + vertexZ * particleRotationMatrix[8];
  1064. rotatedX += pivotBackTranslation.x;
  1065. rotatedY += pivotBackTranslation.y;
  1066. rotatedZ += pivotBackTranslation.z;
  1067. const px = positions32[idx] = particleGlobalPosition.x + camAxisX.x * rotatedX + camAxisY.x * rotatedY + camAxisZ.x * rotatedZ;
  1068. const py = positions32[idx + 1] = particleGlobalPosition.y + camAxisX.y * rotatedX + camAxisY.y * rotatedY + camAxisZ.y * rotatedZ;
  1069. const pz = positions32[idx + 2] = particleGlobalPosition.z + camAxisX.z * rotatedX + camAxisY.z * rotatedY + camAxisZ.z * rotatedZ;
  1070. if (this._computeBoundingBox) {
  1071. minimum.minimizeInPlaceFromFloats(px, py, pz);
  1072. maximum.maximizeInPlaceFromFloats(px, py, pz);
  1073. }
  1074. // normals : if the particles can't be morphed then just rotate the normals, what is much more faster than ComputeNormals()
  1075. if (!this._computeParticleVertex) {
  1076. const normalx = fixedNormal32[idx];
  1077. const normaly = fixedNormal32[idx + 1];
  1078. const normalz = fixedNormal32[idx + 2];
  1079. const rotatedx = normalx * particleRotationMatrix[0] + normaly * particleRotationMatrix[3] + normalz * particleRotationMatrix[6];
  1080. const rotatedy = normalx * particleRotationMatrix[1] + normaly * particleRotationMatrix[4] + normalz * particleRotationMatrix[7];
  1081. const rotatedz = normalx * particleRotationMatrix[2] + normaly * particleRotationMatrix[5] + normalz * particleRotationMatrix[8];
  1082. normals32[idx] = camAxisX.x * rotatedx + camAxisY.x * rotatedy + camAxisZ.x * rotatedz;
  1083. normals32[idx + 1] = camAxisX.y * rotatedx + camAxisY.y * rotatedy + camAxisZ.y * rotatedz;
  1084. normals32[idx + 2] = camAxisX.z * rotatedx + camAxisY.z * rotatedy + camAxisZ.z * rotatedz;
  1085. }
  1086. if (this._computeParticleColor && particle.color) {
  1087. const colors32 = this._colors32;
  1088. colors32[colidx] = tmpColor.r;
  1089. colors32[colidx + 1] = tmpColor.g;
  1090. colors32[colidx + 2] = tmpColor.b;
  1091. colors32[colidx + 3] = tmpColor.a;
  1092. }
  1093. if (this._computeParticleTexture) {
  1094. const uvs = particle.uvs;
  1095. uvs32[uvidx] = tmpUV.x * (uvs.z - uvs.x) + uvs.x;
  1096. uvs32[uvidx + 1] = tmpUV.y * (uvs.w - uvs.y) + uvs.y;
  1097. }
  1098. }
  1099. }
  1100. // particle just set invisible : scaled to zero and positioned at the origin
  1101. else {
  1102. particle._stillInvisible = true; // mark the particle as invisible
  1103. for (pt = 0; pt < shape.length; pt++) {
  1104. idx = index + pt * 3;
  1105. colidx = colorIndex + pt * 4;
  1106. uvidx = uvIndex + pt * 2;
  1107. positions32[idx] = positions32[idx + 1] = positions32[idx + 2] = 0;
  1108. normals32[idx] = normals32[idx + 1] = normals32[idx + 2] = 0;
  1109. if (this._computeParticleColor && particle.color) {
  1110. const color = particle.color;
  1111. colors32[colidx] = color.r;
  1112. colors32[colidx + 1] = color.g;
  1113. colors32[colidx + 2] = color.b;
  1114. colors32[colidx + 3] = color.a;
  1115. }
  1116. if (this._computeParticleTexture) {
  1117. const uvs = particle.uvs;
  1118. uvs32[uvidx] = shapeUV[pt * 2] * (uvs.z - uvs.x) + uvs.x;
  1119. uvs32[uvidx + 1] = shapeUV[pt * 2 + 1] * (uvs.w - uvs.y) + uvs.y;
  1120. }
  1121. }
  1122. }
  1123. // if the particle intersections must be computed : update the bbInfo
  1124. if (this._particlesIntersect) {
  1125. const bInfo = particle._boundingInfo;
  1126. const bBox = bInfo.boundingBox;
  1127. const bSphere = bInfo.boundingSphere;
  1128. const modelBoundingInfo = particle._modelBoundingInfo;
  1129. if (!this._bSphereOnly) {
  1130. // place, scale and rotate the particle bbox within the SPS local system, then update it
  1131. const modelBoundingInfoVectors = modelBoundingInfo.boundingBox.vectors;
  1132. const tempMin = tempVectors[1];
  1133. const tempMax = tempVectors[2];
  1134. tempMin.setAll(Number.MAX_VALUE);
  1135. tempMax.setAll(-Number.MAX_VALUE);
  1136. for (var b = 0; b < 8; b++) {
  1137. const scaledX = modelBoundingInfoVectors[b].x * particleScaling.x;
  1138. const scaledY = modelBoundingInfoVectors[b].y * particleScaling.y;
  1139. const scaledZ = modelBoundingInfoVectors[b].z * particleScaling.z;
  1140. const rotatedX = scaledX * particleRotationMatrix[0] + scaledY * particleRotationMatrix[3] + scaledZ * particleRotationMatrix[6];
  1141. const rotatedY = scaledX * particleRotationMatrix[1] + scaledY * particleRotationMatrix[4] + scaledZ * particleRotationMatrix[7];
  1142. const rotatedZ = scaledX * particleRotationMatrix[2] + scaledY * particleRotationMatrix[5] + scaledZ * particleRotationMatrix[8];
  1143. const x = particlePosition.x + camAxisX.x * rotatedX + camAxisY.x * rotatedY + camAxisZ.x * rotatedZ;
  1144. const y = particlePosition.y + camAxisX.y * rotatedX + camAxisY.y * rotatedY + camAxisZ.y * rotatedZ;
  1145. const z = particlePosition.z + camAxisX.z * rotatedX + camAxisY.z * rotatedY + camAxisZ.z * rotatedZ;
  1146. tempMin.minimizeInPlaceFromFloats(x, y, z);
  1147. tempMax.maximizeInPlaceFromFloats(x, y, z);
  1148. }
  1149. bBox.reConstruct(tempMin, tempMax, mesh._worldMatrix);
  1150. }
  1151. // place and scale the particle bouding sphere in the SPS local system, then update it
  1152. const minBbox = modelBoundingInfo.minimum.multiplyToRef(particleScaling, tempVectors[1]);
  1153. const maxBbox = modelBoundingInfo.maximum.multiplyToRef(particleScaling, tempVectors[2]);
  1154. const bSphereCenter = maxBbox.addToRef(minBbox, tempVectors[3]).scaleInPlace(0.5).addInPlace(particleGlobalPosition);
  1155. const halfDiag = maxBbox.subtractToRef(minBbox, tempVectors[4]).scaleInPlace(0.5 * this._bSphereRadiusFactor);
  1156. const bSphereMinBbox = bSphereCenter.subtractToRef(halfDiag, tempVectors[1]);
  1157. const bSphereMaxBbox = bSphereCenter.addToRef(halfDiag, tempVectors[2]);
  1158. bSphere.reConstruct(bSphereMinBbox, bSphereMaxBbox, mesh._worldMatrix);
  1159. }
  1160. // increment indexes for the next particle
  1161. index = idx + 3;
  1162. colorIndex = colidx + 4;
  1163. uvIndex = uvidx + 2;
  1164. }
  1165. // if the VBO must be updated
  1166. if (update) {
  1167. if (this._computeParticleColor) {
  1168. mesh.updateVerticesData(VertexBuffer.ColorKind, colors32, false, false);
  1169. }
  1170. if (this._computeParticleTexture) {
  1171. mesh.updateVerticesData(VertexBuffer.UVKind, uvs32, false, false);
  1172. }
  1173. mesh.updateVerticesData(VertexBuffer.PositionKind, positions32, false, false);
  1174. if (!mesh.areNormalsFrozen || mesh.isFacetDataEnabled) {
  1175. if (this._computeParticleVertex || mesh.isFacetDataEnabled) {
  1176. // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
  1177. var params = mesh.isFacetDataEnabled ? mesh.getFacetDataParameters() : null;
  1178. VertexData.ComputeNormals(positions32, indices32, normals32, params);
  1179. for (var i = 0; i < normals32.length; i++) {
  1180. fixedNormal32[i] = normals32[i];
  1181. }
  1182. }
  1183. if (!mesh.areNormalsFrozen) {
  1184. mesh.updateVerticesData(VertexBuffer.NormalKind, normals32, false, false);
  1185. }
  1186. }
  1187. if (this._depthSort && this._depthSortParticles) {
  1188. const depthSortedParticles = this.depthSortedParticles;
  1189. depthSortedParticles.sort(this._depthSortFunction);
  1190. const dspl = depthSortedParticles.length;
  1191. let sid = 0;
  1192. let faceId = 0;
  1193. for (let sorted = 0; sorted < dspl; sorted++) {
  1194. const sortedParticle = depthSortedParticles[sorted];
  1195. const lind = sortedParticle.indicesLength;
  1196. const sind = sortedParticle.ind;
  1197. for (var i = 0; i < lind; i++) {
  1198. indices32[sid] = indices[sind + i];
  1199. sid++;
  1200. if (this._pickable) {
  1201. let f = i % 3;
  1202. if (f == 0) {
  1203. let pickedData = this.pickedParticles[faceId];
  1204. pickedData.idx = sortedParticle.idx;
  1205. pickedData.faceId = faceId;
  1206. faceId++;
  1207. }
  1208. }
  1209. }
  1210. }
  1211. mesh.updateIndices(indices32);
  1212. }
  1213. }
  1214. if (this._computeBoundingBox) {
  1215. if (mesh._boundingInfo) {
  1216. mesh._boundingInfo.reConstruct(minimum, maximum, mesh._worldMatrix);
  1217. }
  1218. else {
  1219. mesh._boundingInfo = new BoundingInfo(minimum, maximum, mesh._worldMatrix);
  1220. }
  1221. }
  1222. if (this._autoUpdateSubMeshes) {
  1223. this.computeSubMeshes();
  1224. }
  1225. this.afterUpdateParticles(start, end, update);
  1226. return this;
  1227. }
  1228. /**
  1229. * Disposes the SPS.
  1230. */
  1231. public dispose(): void {
  1232. this.mesh.dispose();
  1233. this.vars = null;
  1234. // drop references to internal big arrays for the GC
  1235. (<any>this._positions) = null;
  1236. (<any>this._indices) = null;
  1237. (<any>this._normals) = null;
  1238. (<any>this._uvs) = null;
  1239. (<any>this._colors) = null;
  1240. (<any>this._indices32) = null;
  1241. (<any>this._positions32) = null;
  1242. (<any>this._normals32) = null;
  1243. (<any>this._fixedNormal32) = null;
  1244. (<any>this._uvs32) = null;
  1245. (<any>this._colors32) = null;
  1246. (<any>this.pickedParticles) = null;
  1247. (<any>this.pickedBySubMesh) = null;
  1248. (<any>this._materials) = null;
  1249. (<any>this._materialIndexes) = null;
  1250. (<any>this._indicesByMaterial) = null;
  1251. (<any>this._idxOfId) = null;
  1252. }
  1253. /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
  1254. * idx is the particle index in the SPS
  1255. * faceId is the picked face index counted within this particle.
  1256. * Returns null if the pickInfo can't identify a picked particle.
  1257. * @param pickingInfo (PickingInfo object)
  1258. * @returns {idx: number, faceId: number} or null
  1259. */
  1260. public pickedParticle(pickingInfo: PickingInfo): Nullable<{idx: number, faceId: number}> {
  1261. if (pickingInfo.hit) {
  1262. const subMesh = pickingInfo.subMeshId;
  1263. const faceId = pickingInfo.faceId;
  1264. const picked = this.pickedBySubMesh;
  1265. if (picked[subMesh] && picked[subMesh][faceId]) {
  1266. return picked[subMesh][faceId];
  1267. }
  1268. }
  1269. return null;
  1270. }
  1271. /**
  1272. * Returns a SolidParticle object from its identifier : particle.id
  1273. * @param id (integer) the particle Id
  1274. * @returns the searched particle or null if not found in the SPS.
  1275. */
  1276. public getParticleById(id: number): Nullable<SolidParticle> {
  1277. const p = this.particles[id];
  1278. if (p && p.id == id) {
  1279. return p;
  1280. }
  1281. const particles = this.particles;
  1282. const idx = this._idxOfId[id];
  1283. if (idx !== undefined) {
  1284. return particles[idx];
  1285. }
  1286. var i = 0;
  1287. const nb = this.nbParticles;
  1288. while (i < nb) {
  1289. var particle = particles[i];
  1290. if (particle.id == id) {
  1291. return particle;
  1292. }
  1293. i++;
  1294. }
  1295. return null;
  1296. }
  1297. /**
  1298. * Returns a new array populated with the particles having the passed shapeId.
  1299. * @param shapeId (integer) the shape identifier
  1300. * @returns a new solid particle array
  1301. */
  1302. public getParticlesByShapeId(shapeId: number): SolidParticle[] {
  1303. var ref: SolidParticle[] = [];
  1304. this.getParticlesByShapeIdToRef(shapeId, ref);
  1305. return ref;
  1306. }
  1307. /**
  1308. * Populates the passed array "ref" with the particles having the passed shapeId.
  1309. * @param shapeId the shape identifier
  1310. * @returns the SPS
  1311. * @param ref
  1312. */
  1313. public getParticlesByShapeIdToRef(shapeId: number, ref: SolidParticle[]): SolidParticleSystem {
  1314. ref.length = 0;
  1315. for (var i = 0; i < this.nbParticles; i++) {
  1316. var p = this.particles[i];
  1317. if (p.shapeId == shapeId) {
  1318. ref.push(p);
  1319. }
  1320. }
  1321. return this;
  1322. }
  1323. /**
  1324. * Computes the required SubMeshes according the materials assigned to the particles.
  1325. * @returns the solid particle system.
  1326. * Does nothing if called before the SPS mesh is built.
  1327. */
  1328. public computeSubMeshes(): SolidParticleSystem {
  1329. if (!this.mesh || !this._multimaterialEnabled) {
  1330. return this;
  1331. }
  1332. const depthSortedParticles = this.depthSortedParticles;
  1333. if (this.particles.length > 0) {
  1334. for (let p = 0; p < this.particles.length; p++) {
  1335. let part = this.particles[p];
  1336. if (!part.materialIndex) {
  1337. part.materialIndex = 0;
  1338. }
  1339. let sortedPart = depthSortedParticles[p];
  1340. sortedPart.materialIndex = part.materialIndex;
  1341. sortedPart.ind = part._ind;
  1342. sortedPart.indicesLength = part._model._indicesLength;
  1343. sortedPart.idx = part.idx;
  1344. }
  1345. }
  1346. this._sortParticlesByMaterial();
  1347. const indicesByMaterial = this._indicesByMaterial;
  1348. const materialIndexes = this._materialIndexes;
  1349. const mesh = this.mesh;
  1350. mesh.subMeshes = [];
  1351. const vcount = mesh.getTotalVertices();
  1352. for (let m = 0; m < materialIndexes.length; m++) {
  1353. let start = indicesByMaterial[m];
  1354. let count = indicesByMaterial[m + 1] - start;
  1355. let matIndex = materialIndexes[m];
  1356. new SubMesh(matIndex, 0, vcount, start, count, mesh);
  1357. }
  1358. return this;
  1359. }
  1360. /**
  1361. * Sorts the solid particles by material when MultiMaterial is enabled.
  1362. * Updates the indices32 array.
  1363. * Updates the indicesByMaterial array.
  1364. * Updates the mesh indices array.
  1365. * @returns the SPS
  1366. * @hidden
  1367. */
  1368. private _sortParticlesByMaterial(): SolidParticleSystem {
  1369. const indicesByMaterial = [0];
  1370. this._indicesByMaterial = indicesByMaterial;
  1371. const materialIndexes: number[] = [];
  1372. this._materialIndexes = materialIndexes;
  1373. const depthSortedParticles = this.depthSortedParticles;
  1374. depthSortedParticles.sort(this._materialSortFunction);
  1375. const length = depthSortedParticles.length;
  1376. const indices32 = this._indices32;
  1377. const indices = this._indices;
  1378. let subMeshIndex = 0;
  1379. let subMeshFaceId = 0;
  1380. let sid = 0;
  1381. let lastMatIndex = depthSortedParticles[0].materialIndex;
  1382. materialIndexes.push(lastMatIndex);
  1383. if (this._pickable) {
  1384. this.pickedBySubMesh = [[]];
  1385. this.pickedParticles = this.pickedBySubMesh[0];
  1386. }
  1387. for (let sorted = 0; sorted < length; sorted++) {
  1388. let sortedPart = depthSortedParticles[sorted];
  1389. let lind = sortedPart.indicesLength;
  1390. let sind = sortedPart.ind;
  1391. if (sortedPart.materialIndex !== lastMatIndex) {
  1392. lastMatIndex = sortedPart.materialIndex;
  1393. indicesByMaterial.push(sid);
  1394. materialIndexes.push(lastMatIndex);
  1395. if (this._pickable) {
  1396. subMeshIndex++;
  1397. this.pickedBySubMesh[subMeshIndex] = [];
  1398. subMeshFaceId = 0;
  1399. }
  1400. }
  1401. let faceId = 0;
  1402. for (let i = 0; i < lind; i++) {
  1403. indices32[sid] = indices[sind + i];
  1404. if (this._pickable) {
  1405. let f = i % 3;
  1406. if (f == 0) {
  1407. let pickedData = this.pickedBySubMesh[subMeshIndex][subMeshFaceId];
  1408. if (pickedData) {
  1409. pickedData.idx = sortedPart.idx;
  1410. pickedData.faceId = faceId;
  1411. }
  1412. else {
  1413. this.pickedBySubMesh[subMeshIndex][subMeshFaceId] = {idx: sortedPart.idx, faceId: faceId};
  1414. }
  1415. subMeshFaceId++;
  1416. faceId++;
  1417. }
  1418. }
  1419. sid++;
  1420. }
  1421. }
  1422. indicesByMaterial.push(indices32.length); // add the last number to ease the indices start/count values for subMeshes creation
  1423. if (this._updatable) {
  1424. this.mesh.updateIndices(indices32);
  1425. }
  1426. return this;
  1427. }
  1428. /**
  1429. * Sets the material indexes by id materialIndexesById[id] = materialIndex
  1430. * @hidden
  1431. */
  1432. private _setMaterialIndexesById() {
  1433. this._materialIndexesById = {};
  1434. for (var i = 0; i < this._materials.length; i++) {
  1435. var id = this._materials[i].uniqueId;
  1436. this._materialIndexesById[id] = i;
  1437. }
  1438. }
  1439. /**
  1440. * Returns an array with unique values of Materials from the passed array
  1441. * @param array the material array to be checked and filtered
  1442. * @hidden
  1443. */
  1444. private _filterUniqueMaterialId(array: Material[]): Material[] {
  1445. var filtered = array.filter(function(value, index, self) {
  1446. return self.indexOf(value) === index;
  1447. });
  1448. return filtered;
  1449. }
  1450. /**
  1451. * Sets a new Standard Material as _defaultMaterial if not already set.
  1452. * @hidden
  1453. */
  1454. private _setDefaultMaterial(): Material {
  1455. if (!this._defaultMaterial) {
  1456. this._defaultMaterial = new StandardMaterial(this.name + "DefaultMaterial", this._scene);
  1457. }
  1458. return this._defaultMaterial;
  1459. }
  1460. /**
  1461. * Visibilty helper : Recomputes the visible size according to the mesh bounding box
  1462. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
  1463. * @returns the SPS.
  1464. */
  1465. public refreshVisibleSize(): SolidParticleSystem {
  1466. if (!this._isVisibilityBoxLocked) {
  1467. this.mesh.refreshBoundingInfo();
  1468. }
  1469. return this;
  1470. }
  1471. /**
  1472. * Visibility helper : Sets the size of a visibility box, this sets the underlying mesh bounding box.
  1473. * @param size the size (float) of the visibility box
  1474. * note : this doesn't lock the SPS mesh bounding box.
  1475. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
  1476. */
  1477. public setVisibilityBox(size: number): void {
  1478. var vis = size / 2;
  1479. this.mesh._boundingInfo = new BoundingInfo(new Vector3(-vis, -vis, -vis), new Vector3(vis, vis, vis));
  1480. }
  1481. /**
  1482. * Gets whether the SPS as always visible or not
  1483. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
  1484. */
  1485. public get isAlwaysVisible(): boolean {
  1486. return this._alwaysVisible;
  1487. }
  1488. /**
  1489. * Sets the SPS as always visible or not
  1490. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
  1491. */
  1492. public set isAlwaysVisible(val: boolean) {
  1493. this._alwaysVisible = val;
  1494. this.mesh.alwaysSelectAsActiveMesh = val;
  1495. }
  1496. /**
  1497. * Sets the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
  1498. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
  1499. */
  1500. public set isVisibilityBoxLocked(val: boolean) {
  1501. this._isVisibilityBoxLocked = val;
  1502. let boundingInfo = this.mesh.getBoundingInfo();
  1503. boundingInfo.isLocked = val;
  1504. }
  1505. /**
  1506. * Gets if the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
  1507. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
  1508. */
  1509. public get isVisibilityBoxLocked(): boolean {
  1510. return this._isVisibilityBoxLocked;
  1511. }
  1512. /**
  1513. * Tells to `setParticles()` to compute the particle rotations or not.
  1514. * Default value : true. The SPS is faster when it's set to false.
  1515. * Note : the particle rotations aren't stored values, so setting `computeParticleRotation` to false will prevents the particle to rotate.
  1516. */
  1517. public set computeParticleRotation(val: boolean) {
  1518. this._computeParticleRotation = val;
  1519. }
  1520. /**
  1521. * Tells to `setParticles()` to compute the particle colors or not.
  1522. * Default value : true. The SPS is faster when it's set to false.
  1523. * Note : the particle colors are stored values, so setting `computeParticleColor` to false will keep yet the last colors set.
  1524. */
  1525. public set computeParticleColor(val: boolean) {
  1526. this._computeParticleColor = val;
  1527. }
  1528. public set computeParticleTexture(val: boolean) {
  1529. this._computeParticleTexture = val;
  1530. }
  1531. /**
  1532. * Tells to `setParticles()` to call the vertex function for each vertex of each particle, or not.
  1533. * Default value : false. The SPS is faster when it's set to false.
  1534. * Note : the particle custom vertex positions aren't stored values.
  1535. */
  1536. public set computeParticleVertex(val: boolean) {
  1537. this._computeParticleVertex = val;
  1538. }
  1539. /**
  1540. * Tells to `setParticles()` to compute or not the mesh bounding box when computing the particle positions.
  1541. */
  1542. public set computeBoundingBox(val: boolean) {
  1543. this._computeBoundingBox = val;
  1544. }
  1545. /**
  1546. * Tells to `setParticles()` to sort or not the distance between each particle and the camera.
  1547. * Skipped when `enableDepthSort` is set to `false` (default) at construction time.
  1548. * Default : `true`
  1549. */
  1550. public set depthSortParticles(val: boolean) {
  1551. this._depthSortParticles = val;
  1552. }
  1553. /**
  1554. * Gets if `setParticles()` computes the particle rotations or not.
  1555. * Default value : true. The SPS is faster when it's set to false.
  1556. * Note : the particle rotations aren't stored values, so setting `computeParticleRotation` to false will prevents the particle to rotate.
  1557. */
  1558. public get computeParticleRotation(): boolean {
  1559. return this._computeParticleRotation;
  1560. }
  1561. /**
  1562. * Gets if `setParticles()` computes the particle colors or not.
  1563. * Default value : true. The SPS is faster when it's set to false.
  1564. * Note : the particle colors are stored values, so setting `computeParticleColor` to false will keep yet the last colors set.
  1565. */
  1566. public get computeParticleColor(): boolean {
  1567. return this._computeParticleColor;
  1568. }
  1569. /**
  1570. * Gets if `setParticles()` computes the particle textures or not.
  1571. * Default value : true. The SPS is faster when it's set to false.
  1572. * Note : the particle textures are stored values, so setting `computeParticleTexture` to false will keep yet the last colors set.
  1573. */
  1574. public get computeParticleTexture(): boolean {
  1575. return this._computeParticleTexture;
  1576. }
  1577. /**
  1578. * Gets if `setParticles()` calls the vertex function for each vertex of each particle, or not.
  1579. * Default value : false. The SPS is faster when it's set to false.
  1580. * Note : the particle custom vertex positions aren't stored values.
  1581. */
  1582. public get computeParticleVertex(): boolean {
  1583. return this._computeParticleVertex;
  1584. }
  1585. /**
  1586. * Gets if `setParticles()` computes or not the mesh bounding box when computing the particle positions.
  1587. */
  1588. public get computeBoundingBox(): boolean {
  1589. return this._computeBoundingBox;
  1590. }
  1591. /**
  1592. * Gets if `setParticles()` sorts or not the distance between each particle and the camera.
  1593. * Skipped when `enableDepthSort` is set to `false` (default) at construction time.
  1594. * Default : `true`
  1595. */
  1596. public get depthSortParticles(): boolean {
  1597. return this._depthSortParticles;
  1598. }
  1599. /**
  1600. * Gets if the SPS is created as expandable at construction time.
  1601. * Default : `false`
  1602. */
  1603. public get expandable(): boolean {
  1604. return this._expandable;
  1605. }
  1606. /**
  1607. * Gets if the SPS supports the Multi Materials
  1608. */
  1609. public get multimaterialEnabled(): boolean {
  1610. return this._multimaterialEnabled;
  1611. }
  1612. /**
  1613. * Gets if the SPS uses the model materials for its own multimaterial.
  1614. */
  1615. public get useModelMaterial(): boolean {
  1616. return this._useModelMaterial;
  1617. }
  1618. /**
  1619. * The SPS used material array.
  1620. */
  1621. public get materials(): Material[] {
  1622. return this._materials;
  1623. }
  1624. /**
  1625. * Sets the SPS MultiMaterial from the passed materials.
  1626. * Note : the passed array is internally copied and not used then by reference.
  1627. * @param materials an array of material objects. This array indexes are the materialIndex values of the particles.
  1628. */
  1629. public setMultiMaterial(materials: Material[]) {
  1630. this._materials = this._filterUniqueMaterialId(materials);
  1631. this._setMaterialIndexesById();
  1632. if (this._multimaterial) {
  1633. this._multimaterial.dispose();
  1634. }
  1635. this._multimaterial = new MultiMaterial(this.name + "MultiMaterial", this._scene);
  1636. for (var m = 0; m < this._materials.length; m++) {
  1637. this._multimaterial.subMaterials.push(this._materials[m]);
  1638. }
  1639. this.computeSubMeshes();
  1640. this.mesh.material = this._multimaterial;
  1641. }
  1642. /**
  1643. * The SPS computed multimaterial object
  1644. */
  1645. public get multimaterial(): MultiMaterial {
  1646. return this._multimaterial;
  1647. }
  1648. public set multimaterial(mm) {
  1649. this._multimaterial = mm;
  1650. }
  1651. /**
  1652. * If the subMeshes must be updated on the next call to setParticles()
  1653. */
  1654. public get autoUpdateSubMeshes(): boolean {
  1655. return this._autoUpdateSubMeshes;
  1656. }
  1657. public set autoUpdateSubMeshes(val: boolean) {
  1658. this._autoUpdateSubMeshes = val;
  1659. }
  1660. // =======================================================================
  1661. // Particle behavior logic
  1662. // these following methods may be overwritten by the user to fit his needs
  1663. /**
  1664. * This function does nothing. It may be overwritten to set all the particle first values.
  1665. * The SPS doesn't call this function, you may have to call it by your own.
  1666. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#particle-management
  1667. */
  1668. public initParticles(): void {
  1669. }
  1670. /**
  1671. * This function does nothing. It may be overwritten to recycle a particle.
  1672. * The SPS doesn't call this function, you may have to call it by your own.
  1673. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#particle-management
  1674. * @param particle The particle to recycle
  1675. * @returns the recycled particle
  1676. */
  1677. public recycleParticle(particle: SolidParticle): SolidParticle {
  1678. return particle;
  1679. }
  1680. /**
  1681. * Updates a particle : this function should be overwritten by the user.
  1682. * It is called on each particle by `setParticles()`. This is the place to code each particle behavior.
  1683. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#particle-management
  1684. * @example : just set a particle position or velocity and recycle conditions
  1685. * @param particle The particle to update
  1686. * @returns the updated particle
  1687. */
  1688. public updateParticle(particle: SolidParticle): SolidParticle {
  1689. return particle;
  1690. }
  1691. /**
  1692. * Updates a vertex of a particle : it can be overwritten by the user.
  1693. * This will be called on each vertex particle by `setParticles()` if `computeParticleVertex` is set to true only.
  1694. * @param particle the current particle
  1695. * @param vertex the current vertex of the current particle : a SolidParticleVertex object
  1696. * @param pt the index of the current vertex in the particle shape
  1697. * doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#update-each-particle-shape
  1698. * @example : just set a vertex particle position or color
  1699. * @returns the sps
  1700. */
  1701. public updateParticleVertex(particle: SolidParticle, vertex: SolidParticleVertex, pt: number): SolidParticleSystem {
  1702. return this;
  1703. }
  1704. /**
  1705. * This will be called before any other treatment by `setParticles()` and will be passed three parameters.
  1706. * This does nothing and may be overwritten by the user.
  1707. * @param start the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1708. * @param stop the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1709. * @param update the boolean update value actually passed to setParticles()
  1710. */
  1711. public beforeUpdateParticles(start?: number, stop?: number, update?: boolean): void {
  1712. }
  1713. /**
  1714. * This will be called by `setParticles()` after all the other treatments and just before the actual mesh update.
  1715. * This will be passed three parameters.
  1716. * This does nothing and may be overwritten by the user.
  1717. * @param start the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1718. * @param stop the particle index in the particle array where to stop to iterate, same than the value passed to setParticle()
  1719. * @param update the boolean update value actually passed to setParticles()
  1720. */
  1721. public afterUpdateParticles(start?: number, stop?: number, update?: boolean): void {
  1722. }
  1723. }