|
@@ -11,8 +11,7 @@ import { DepthSortedParticle, SolidParticle, ModelShape } from "./solidParticle"
|
|
|
import { TargetCamera } from "../Cameras/targetCamera";
|
|
|
import { BoundingInfo } from "../Culling/boundingInfo";
|
|
|
import { Axis } from '../Maths/math.axis';
|
|
|
-
|
|
|
-const depthSortFunction = (p1: DepthSortedParticle, p2: DepthSortedParticle) => p2.sqDistance - p1.sqDistance;
|
|
|
+import { SubMesh } from '../Meshes/subMesh';
|
|
|
|
|
|
/**
|
|
|
* The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
|
|
@@ -119,6 +118,11 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
private _isNotBuilt: boolean = true;
|
|
|
private _lastParticleId: number = 0;
|
|
|
private _idxOfId: number[] = []; // array : key = particle.id / value = particle.idx
|
|
|
+ private _multimaterialEnabled: boolean = false;
|
|
|
+ private _indicesByMaterial: number[];
|
|
|
+ private _materialIndexes: number[];
|
|
|
+ private _depthSortFunction = (p1: DepthSortedParticle, p2: DepthSortedParticle) => p2.sqDistance - p1.sqDistance;
|
|
|
+ private _materialSortFunction = (p1: DepthSortedParticle, p2: DepthSortedParticle) => p1.materialIndex - p2.materialIndex;
|
|
|
|
|
|
/**
|
|
|
* Creates a SPS (Solid Particle System) object.
|
|
@@ -128,18 +132,20 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
* * updatable (optional boolean, default true) : if the SPS must be updatable or immutable.
|
|
|
* * isPickable (optional boolean, default false) : if the solid particles must be pickable.
|
|
|
* * enableDepthSort (optional boolean, default false) : if the solid particles must be sorted in the geometry according to their distance to the camera.
|
|
|
+ * * enableMultiMaterial (optional boolean, default false) : if the solid particles can be given different materials.
|
|
|
* * expandable (optional boolean, default false) : if particles can still be added after the initial SPS mesh creation.
|
|
|
* * particleIntersection (optional boolean, default false) : if the solid particle intersections must be computed.
|
|
|
* * boundingSphereOnly (optional boolean, default false) : if the particle intersection must be computed only with the bounding sphere (no bounding box computation, so faster).
|
|
|
* * bSphereRadiusFactor (optional float, default 1.0) : a number to multiply the boundind sphere radius by in order to reduce it for instance.
|
|
|
* @example bSphereRadiusFactor = 1.0 / Math.sqrt(3.0) => the bounding sphere exactly matches a spherical mesh.
|
|
|
*/
|
|
|
- constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; enableDepthSort?: boolean; particleIntersection?: boolean; boundingSphereOnly?: boolean; bSphereRadiusFactor?: number; expandable?: boolean }) {
|
|
|
+ constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; enableDepthSort?: boolean; particleIntersection?: boolean; boundingSphereOnly?: boolean; bSphereRadiusFactor?: number; expandable?: boolean; enableMultiMaterial?: boolean }) {
|
|
|
this.name = name;
|
|
|
this._scene = scene || EngineStore.LastCreatedScene;
|
|
|
this._camera = <TargetCamera>scene.activeCamera;
|
|
|
this._pickable = options ? <boolean>options.isPickable : false;
|
|
|
this._depthSort = options ? <boolean>options.enableDepthSort : false;
|
|
|
+ this._multimaterialEnabled = options ? <boolean>options.enableMultiMaterial : false;
|
|
|
this._expandable = options ? <boolean>options.expandable : false;
|
|
|
this._particlesIntersect = options ? <boolean>options.particleIntersection : false;
|
|
|
this._bSphereOnly = options ? <boolean>options.boundingSphereOnly : false;
|
|
@@ -152,7 +158,7 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
if (this._pickable) {
|
|
|
this.pickedParticles = [];
|
|
|
}
|
|
|
- if (this._depthSort) {
|
|
|
+ if (this._depthSort || this._multimaterialEnabled) {
|
|
|
this.depthSortedParticles = [];
|
|
|
}
|
|
|
}
|
|
@@ -204,7 +210,7 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
|
|
|
if (!this._expandable) {
|
|
|
// free memory
|
|
|
- if (!this._depthSort) {
|
|
|
+ if (!this._depthSort && !this._multimaterialEnabled) {
|
|
|
(<any>this._indices) = null;
|
|
|
}
|
|
|
(<any>this._positions) = null;
|
|
@@ -393,6 +399,7 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
copy.uvs.copyFromFloats(0.0, 0.0, 1.0, 1.0);
|
|
|
copy.color = null;
|
|
|
copy.translateFromPivot = false;
|
|
|
+ copy.materialIndex = null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -511,8 +518,9 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (this._depthSort) {
|
|
|
- this.depthSortedParticles.push(new DepthSortedParticle());
|
|
|
+ if (this._depthSort || this._multimaterialEnabled) {
|
|
|
+ var matIndex = (copy.materialIndex !== null) ? copy.materialIndex : 0;
|
|
|
+ this.depthSortedParticles.push(new DepthSortedParticle(matIndex));
|
|
|
}
|
|
|
|
|
|
return copy;
|
|
@@ -694,11 +702,13 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
const particles = this.particles;
|
|
|
const currentNb = this.nbParticles;
|
|
|
if (end < currentNb - 1) { // update the particle indexes in the positions array in case they're remaining particles after the last removed
|
|
|
- var startPositionIndex = particles[start]._pos;
|
|
|
var firstRemaining = end + 1;
|
|
|
- var shift = particles[firstRemaining]._pos - startPositionIndex;
|
|
|
+ var shiftPos = particles[firstRemaining]._pos - particles[start]._pos;
|
|
|
+ var shifInd = particles[firstRemaining]._ind - particles[start]._ind;
|
|
|
for (var i = firstRemaining; i < currentNb; i++) {
|
|
|
- particles[i]._pos -= shift;
|
|
|
+ var part = particles[i];
|
|
|
+ part._pos -= shiftPos;
|
|
|
+ part._ind -= shifInd;
|
|
|
}
|
|
|
}
|
|
|
var removed = particles.splice(start, nb);
|
|
@@ -709,8 +719,8 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
this._normals.length = 0;
|
|
|
this._index = 0;
|
|
|
this._idxOfId.length = 0;
|
|
|
- if (this._depthSort) {
|
|
|
- this.depthSortedParticles.length = 0;
|
|
|
+ if (this._depthSort || this._multimaterialEnabled) {
|
|
|
+ this.depthSortedParticles = [];
|
|
|
}
|
|
|
const particlesLength = particles.length;
|
|
|
for (var p = 0; p < particlesLength; p++) {
|
|
@@ -811,6 +821,9 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
}
|
|
|
sp.scaling.copyFrom(currentCopy.scaling);
|
|
|
sp.uvs.copyFrom(currentCopy.uvs);
|
|
|
+ if (currentCopy.materialIndex !== null) {
|
|
|
+ sp.materialIndex = currentCopy.materialIndex;
|
|
|
+ }
|
|
|
if (this.expandable) {
|
|
|
this._idxOfId[sp.id] = sp.idx;
|
|
|
}
|
|
@@ -1183,7 +1196,7 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
}
|
|
|
if (this._depthSort && this._depthSortParticles) {
|
|
|
const depthSortedParticles = this.depthSortedParticles;
|
|
|
- depthSortedParticles.sort(depthSortFunction);
|
|
|
+ depthSortedParticles.sort(this._depthSortFunction);
|
|
|
const dspl = depthSortedParticles.length;
|
|
|
let sid = 0;
|
|
|
for (let sorted = 0; sorted < dspl; sorted++) {
|
|
@@ -1284,7 +1297,81 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
}
|
|
|
return this;
|
|
|
}
|
|
|
-
|
|
|
+ /**
|
|
|
+ * Computes the required SubMeshes according the materials assigned to the particles.
|
|
|
+ * @returns the solid particle system.
|
|
|
+ * Does nothing if called before the SPS mesh is built.
|
|
|
+ */
|
|
|
+ public computeSubMeshes(): SolidParticleSystem {
|
|
|
+ if (!this.mesh || !this._multimaterialEnabled) {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ const depthSortedParticles = this.depthSortedParticles;
|
|
|
+ if (this.particles.length > 0) {
|
|
|
+ for (let p = 0; p < this.particles.length; p++) {
|
|
|
+ let part = this.particles[p];
|
|
|
+ if (!part.materialIndex) {
|
|
|
+ part.materialIndex = 0;
|
|
|
+ }
|
|
|
+ let sortedPart = depthSortedParticles[p];
|
|
|
+ sortedPart.materialIndex = part.materialIndex;
|
|
|
+ sortedPart.ind = part._ind;
|
|
|
+ sortedPart.indicesLength = part._model._indicesLength;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this._sortParticlesByMaterial();
|
|
|
+ const indicesByMaterial = this._indicesByMaterial;
|
|
|
+ const materialIndexes = this._materialIndexes;
|
|
|
+ const mesh = this.mesh;
|
|
|
+ mesh.subMeshes = [];
|
|
|
+ const vcount = mesh.getTotalVertices();
|
|
|
+ for (let m = 0; m < materialIndexes.length; m++) {
|
|
|
+ let start = indicesByMaterial[m];
|
|
|
+ let count = indicesByMaterial[m + 1] - start;
|
|
|
+ let matIndex = materialIndexes[m];
|
|
|
+ new SubMesh(matIndex, 0, vcount, start, count, mesh);
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Sorts the solid particles by material when MultiMaterial is enabled.
|
|
|
+ * Updates the indices32 array.
|
|
|
+ * Updates the indicesByMaterial array.
|
|
|
+ * Updates the mesh indices array.
|
|
|
+ * @returns the SPS
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ private _sortParticlesByMaterial(): SolidParticleSystem {
|
|
|
+ const indicesByMaterial = [0];
|
|
|
+ this._indicesByMaterial = indicesByMaterial;
|
|
|
+ const materialIndexes: number[] = [];
|
|
|
+ this._materialIndexes = materialIndexes;
|
|
|
+ const depthSortedParticles = this.depthSortedParticles;
|
|
|
+ depthSortedParticles.sort(this._materialSortFunction);
|
|
|
+ const length = depthSortedParticles.length;
|
|
|
+ const indices32 = this._indices32;
|
|
|
+ const indices = this._indices;
|
|
|
+ let sid = 0;
|
|
|
+ let lastMatIndex = depthSortedParticles[0].materialIndex;
|
|
|
+ materialIndexes.push(lastMatIndex);
|
|
|
+ for (let sorted = 0; sorted < length; sorted++) {
|
|
|
+ let sortedPart = depthSortedParticles[sorted];
|
|
|
+ let lind = sortedPart.indicesLength;
|
|
|
+ let sind = sortedPart.ind;
|
|
|
+ if (sortedPart.materialIndex !== lastMatIndex) {
|
|
|
+ lastMatIndex = sortedPart.materialIndex;
|
|
|
+ indicesByMaterial.push(sid);
|
|
|
+ materialIndexes.push(lastMatIndex);
|
|
|
+ }
|
|
|
+ for (let i = 0; i < lind; i++) {
|
|
|
+ indices32[sid] = indices[sind + i];
|
|
|
+ sid++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ indicesByMaterial.push(indices32.length); // add the last number to ease the indices start/count values for subMeshes creation
|
|
|
+ this.mesh.updateIndices(indices32);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
/**
|
|
|
* Visibilty helper : Recomputes the visible size according to the mesh bounding box
|
|
|
* doc : http://doc.babylonjs.com/how_to/Solid_Particle_System#sps-visibility
|
|
@@ -1448,6 +1535,12 @@ export class SolidParticleSystem implements IDisposable {
|
|
|
public get expandable(): boolean {
|
|
|
return this._expandable;
|
|
|
}
|
|
|
+ /**
|
|
|
+ * Gets if the SPS supports the Multi Materials
|
|
|
+ */
|
|
|
+ public get multimaterialEnabled(): boolean {
|
|
|
+ return this._multimaterialEnabled;
|
|
|
+ }
|
|
|
|
|
|
// =======================================================================
|
|
|
// Particle behavior logic
|