Bläddra i källkod

Added support for MultiMaterials to the SPS

Jérôme Bousquié 5 år sedan
förälder
incheckning
3c5e4b7049
3 ändrade filer med 133 tillägg och 15 borttagningar
  1. 1 0
      dist/preview release/what's new.md
  2. 25 1
      src/Particles/solidParticle.ts
  3. 107 14
      src/Particles/solidParticleSystem.ts

+ 1 - 0
dist/preview release/what's new.md

@@ -164,6 +164,7 @@
 - Added the feature `expandable` to the Solid Particle System ([jerome](https://github.com/jbousquie/))
 - Added the feature `removeParticles()` to the Solid Particle System ([jerome](https://github.com/jbousquie/))
 - Added the feature "storable particles" and `insertParticlesFromArray()` to the Solid Particle System ([jerome](https://github.com/jbousquie/))
+- Added the support for MultiMaterials to the Solid Particle System ([jerome](https://github.com/jbousquie/))
 
 ### Navigation Mesh
 

+ 25 - 1
src/Particles/solidParticle.ts

@@ -112,6 +112,10 @@ export class SolidParticle {
      */
     public parentId: Nullable<number> = null;
     /**
+     * The particle material identifier (integer) when MultiMaterials are enabled in the SPS.
+     */
+    public materialIndex: Nullable<number> = null;
+    /**
      * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
      * The possible values are :
      * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -140,8 +144,9 @@ export class SolidParticle {
      * @param idxInShape (integer) is the index of the particle in the current model (ex: the 10th box of addShape(box, 30))
      * @param sps defines the sps it is associated to
      * @param modelBoundingInfo is the reference to the model BoundingInfo used for intersection computations.
+     * @param materialIndex is the particle material identifier (integer) when the MultiMaterials are enabled in the SPS.
      */
-    constructor(particleIndex: number, particleId: number, positionIndex: number, indiceIndex: number, model: Nullable<ModelShape>, shapeId: number, idxInShape: number, sps: SolidParticleSystem, modelBoundingInfo: Nullable<BoundingInfo> = null) {
+    constructor(particleIndex: number, particleId: number, positionIndex: number, indiceIndex: number, model: Nullable<ModelShape>, shapeId: number, idxInShape: number, sps: SolidParticleSystem, modelBoundingInfo: Nullable<BoundingInfo> = null, materialIndex: Nullable<number> = null) {
         this.idx = particleIndex;
         this.id = particleId;
         this._pos = positionIndex;
@@ -154,6 +159,9 @@ export class SolidParticle {
             this._modelBoundingInfo = modelBoundingInfo;
             this._boundingInfo = new BoundingInfo(modelBoundingInfo.minimum, modelBoundingInfo.maximum);
         }
+        if (materialIndex !== null) {
+            this.materialIndex = materialIndex;
+        }
     }
     /**
      * Copies the particle property values into the existing target : position, rotation, scaling, uvs, colors, pivot, parent, visibility, alive
@@ -188,6 +196,9 @@ export class SolidParticle {
         target.isVisible = this.isVisible;
         target.parentId = this.parentId;
         target.cullingStrategy = this.cullingStrategy;
+        if (this.materialIndex !== null) {
+            target.materialIndex = this.materialIndex;
+        }
         return this;
     }
     /**
@@ -335,6 +346,7 @@ export class ModelShape {
 
 /**
  * Represents a Depth Sorted Particle in the solid particle system.
+ * @hidden
  */
 export class DepthSortedParticle {
     /**
@@ -349,4 +361,16 @@ export class DepthSortedParticle {
      * Squared distance from the particle to the camera
      */
     public sqDistance: number = 0.0;
+    /**
+     * Material index when used with MultiMaterials
+     */
+    public materialIndex: number = 0;
+
+    /**
+     * Creates a new sorted particle
+     * @param materialIndex
+     */
+    constructor(materialIndex: number) {
+        this.materialIndex = materialIndex;
+    }
 }

+ 107 - 14
src/Particles/solidParticleSystem.ts

@@ -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