Quellcode durchsuchen

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe vor 7 Jahren
Ursprung
Commit
670e7ede9b

Datei-Diff unterdrückt, da er zu groß ist
+ 4099 - 0
Playground/textures/invert.3dl


+ 13 - 3
dist/preview release/babylon.d.ts

@@ -25861,9 +25861,19 @@ declare module BABYLON {
         render(subMesh: SubMesh, enableAlphaMode: boolean): Mesh;
         private _onBeforeDraw;
         /**
-         * Normalize matrix weights so that all vertices have a total weight set to 1
+         *   Renormalize the mesh and patch it up if there are no weights
+         *   Similar to normalization by adding the weights comptue the reciprical and multiply all elements. this wil ensure that everything adds to 1. 
+         *   However in the case of 0 weights then we set just a single influence to 1. 
+         *   We check in the function for extra's present and if so we use the normalizeSkinWeightsWithExtras rather than the FourWeights version. 
          */
-        cleanMatrixWeights(): void;
+        public cleanMatrixWeights(): void;
+        /**
+         * ValidateSkinning is used to determin that a mesh has valid skinning data along with skin metrics, if missing weights, 
+         * or not normalized it is returned as invalid mesh the string can be used for console logs, or on screen messages to let
+         * the user know there was an issue with importing the mesh
+         * @returns a validation object with skinned, valid and report string
+         */
+        public validateSkinning() : {skinned:boolean, valid:boolean, report:string};
         /** @hidden */
         _checkDelayState(): Mesh;
         private _queueLoad;
@@ -31095,7 +31105,7 @@ declare module BABYLON {
     /**
      * The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
      *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.
-
+
      * The SPS is also a particle system. It provides some methods to manage the particles.
      * However it is behavior agnostic. This means it has no emitter, no particle physics, no particle recycler. You have to implement your own behavior.
      *

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

@@ -128,6 +128,8 @@
 - Added FXAA and MSAA support to the StandardRenderingPipeline ([julien-moreau](https://github.com/julien-moreau))
 - Make teleportCamera public in VR experience helper ([TrevorDev](https://github.com/TrevorDev))
 - Added optional alphaFilter parameter to ```CreateGroundFromHeightMap``` to allow for heightmaps to be created that ignore any transparent data ([Postman-nz](https://github.com/Postman-nz))
+- Fixed renormalization of mesh weights to in cleanMatrixWeights function. ([Bolloxim](https://github.com/Bolloxim))
+- Added a validationSkin function to report out any errors on skinned meshes. ([Bolloxim](https://github.com/Bolloxim))
 
 
 ### glTF Loader
@@ -140,6 +142,7 @@
 - Added support for MSFT_audio_emitter ([najadojo](http://www.github.com/najadojo))
 - Added support for custom loader extensions ([bghgary](http://www.github.com/bghgary))
 - Added support for validating assets using [glTF-Validator](https://github.com/KhronosGroup/glTF-Validator) ([bghgary](http://www.github.com/bghgary))
+- Added automatically renormalizes skinweights when loading geometry. Calls core mesh functions to do this ([Bolloxim](https://github.com/Bolloxim))
 
 ### glTF Serializer
 - Added support for exporting the scale, rotation and offset texture properties ([kcoley](http://www.github.com/kcoley))

+ 17 - 6
src/Materials/Textures/babylon.colorGradingTexture.ts

@@ -135,13 +135,24 @@ module BABYLON {
                             tempData[pixelStorageIndex + 2] = b;
                         }
 
-                        pixelIndexSlice++;
-                        if (pixelIndexSlice % size == 0) {
-                            pixelIndexH++;
-                            pixelIndexSlice = 0;
-                            if (pixelIndexH % size == 0) {
+                        // Keep for reference in case of back compat problems.
+                        // pixelIndexSlice++;
+                        // if (pixelIndexSlice % size == 0) {
+                        //     pixelIndexH++;
+                        //     pixelIndexSlice = 0;
+                        //     if (pixelIndexH % size == 0) {
+                        //         pixelIndexW++;
+                        //         pixelIndexH = 0;
+                        //     }
+                        // }
+
+                        pixelIndexH++
+                        if (pixelIndexH % size == 0) {
+                            pixelIndexSlice++;
+                            pixelIndexH = 0;
+                            if (pixelIndexSlice % size == 0) {
                                 pixelIndexW++;
-                                pixelIndexH = 0;
+                                pixelIndexSlice = 0;
                             }
                         }
                     }

+ 16 - 2
src/Materials/babylon.imageProcessingConfiguration.ts

@@ -90,11 +90,25 @@ module BABYLON {
             this._updateParameters();
         }
 
+        @serializeAsTexture("colorGradingTexture")
+        private _colorGradingTexture: Nullable<BaseTexture>;
         /**
          * Color grading LUT texture used in the effect if colorGradingEnabled is set to true 
          */
-        @serializeAsTexture()
-        public colorGradingTexture: Nullable<BaseTexture>;
+        public get colorGradingTexture(): Nullable<BaseTexture> {
+            return this._colorGradingTexture;
+        }
+        /**
+         * Color grading LUT texture used in the effect if colorGradingEnabled is set to true 
+         */
+        public set colorGradingTexture(value: Nullable<BaseTexture>) {
+            if (this._colorGradingTexture === value) {
+                return;
+            }
+
+            this._colorGradingTexture = value;
+            this._updateParameters();
+        }
 
         @serialize()
         private _colorGradingEnabled = false;

+ 143 - 59
src/Mesh/babylon.mesh.ts

@@ -1621,80 +1621,164 @@
         }
 
         /**
-         * Normalize matrix weights so that all vertices have a total weight set to 1
+         *   Renormalize the mesh and patch it up if there are no weights
+         *   Similar to normalization by adding the weights compute the reciprocal and multiply all elements, this wil ensure that everything adds to 1. 
+         *   However in the case of zero weights then we set just a single influence to 1. 
+         *   We check in the function for extra's present and if so we use the normalizeSkinWeightsWithExtras rather than the FourWeights version. 
          */
         public cleanMatrixWeights(): void {
-            const epsilon: number = 1e-3;
 
-            let noInfluenceBoneIndex = 0.0;
-            if (this.skeleton) {
-                noInfluenceBoneIndex = this.skeleton.bones.length;
-            } else {
-                return;
-            }
+            if (this.isVerticesDataPresent(VertexBuffer.MatricesWeightsKind)) {
+                if (this.isVerticesDataPresent(VertexBuffer.MatricesWeightsExtraKind)) {
+                    this.normalizeSkinWeightsAndExtra();
+                }
+                else {
+                    this.normalizeSkinFourWeights();
+                }
+            }    
+        }
+
+        // faster 4 weight version. 
+        private normalizeSkinFourWeights(): void {
 
-            let matricesIndices = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesIndicesKind));
-            let matricesIndicesExtra = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesIndicesExtraKind));
             let matricesWeights = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesWeightsKind));
-            let matricesWeightsExtra = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesWeightsExtraKind));
-            let influencers = this.numBoneInfluencers;
-            let size = matricesWeights.length;
-
-            for (var i = 0; i < size; i += 4) {
-                let weight = 0.0;
-                let firstZeroWeight = -1;
-                for (var j = 0; j < 4; j++) {
-                    let w = matricesWeights[i + j];
-                    weight += w;
-                    if (w < epsilon && firstZeroWeight < 0) {
-                        firstZeroWeight = j;
-                    }
+            let numWeights = matricesWeights.length;
+
+            for (var a = 0; a < numWeights; a += 4) {
+                // accumulate weights
+                var t = matricesWeights[a] + matricesWeights[a+1] +matricesWeights[a+2] +matricesWeights[a+3];
+                // check for invalid weight and just set it to 1.
+                if (t === 0) matricesWeights[a] = 1;
+                else{
+                    // renormalize so everything adds to 1 use reciprical
+                    let recip = 1 / t;
+                    matricesWeights[a] *= recip;
+                    matricesWeights[a+1] *= recip;
+                    matricesWeights[a+2] *= recip;
+                    matricesWeights[a+3] *= recip;
                 }
-                if (matricesWeightsExtra) {
-                    for (var j = 0; j < 4; j++) {
-                        let w = matricesWeightsExtra[i + j];
-                        weight += w;
-                        if (w < epsilon && firstZeroWeight < 0) {
-                            firstZeroWeight = j + 4;
-                        }
-                    }
+                
+            }
+            this.setVerticesData(VertexBuffer.MatricesWeightsKind, matricesWeights);
+        }
+        // handle special case of extra verts.  (in theory gltf can handle 12 influences)
+        private normalizeSkinWeightsAndExtra(): void {
+            
+            let matricesWeightsExtra = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesWeightsExtraKind));
+            let matricesWeights = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesWeightsKind));
+            let numWeights = matricesWeights.length;
+
+            for (var a = 0; a  < numWeights; a += 4){
+                // accumulate weights
+                var t = matricesWeights[a] + matricesWeights[a+1] +matricesWeights[a+2] +matricesWeights[a+3];
+                t += matricesWeightsExtra[a] + matricesWeightsExtra[a+1] +matricesWeightsExtra[a+2] +matricesWeightsExtra[a+3];
+                // check for invalid weight and just set it to 1.
+                if (t === 0) matricesWeights[a] = 1;
+                else {
+                    // renormalize so everything adds to 1 use reciprical 
+                    let recip = 1 / t;
+                    matricesWeights[a] *= recip;
+                    matricesWeights[a+1] *= recip;
+                    matricesWeights[a+2] *= recip;
+                    matricesWeights[a+3] *= recip;
+                    // same goes for extras
+                    matricesWeightsExtra[a] *= recip;
+                    matricesWeightsExtra[a+1] *= recip;
+                    matricesWeightsExtra[a+2] *= recip;
+                    matricesWeightsExtra[a+3] *= recip;
                 }
-                if (firstZeroWeight < 0 || firstZeroWeight > (influencers - 1)) {
-                    firstZeroWeight = influencers - 1;
+                
+            }
+            this.setVerticesData(VertexBuffer.MatricesWeightsKind, matricesWeights);
+            this.setVerticesData(VertexBuffer.MatricesWeightsKind, matricesWeightsExtra);
+        }
+
+        /**
+         * ValidateSkinning is used to determine that a mesh has valid skinning data along with skin metrics, if missing weights, 
+         * or not normalized it is returned as invalid mesh the string can be used for console logs, or on screen messages to let
+         * the user know there was an issue with importing the mesh
+         * @returns a validation object with skinned, valid and report string
+         */
+        public validateSkinning() : {skinned:boolean, valid:boolean, report:string} {
+
+            let matricesWeightsExtra = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesWeightsExtraKind));
+            let matricesWeights = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesWeightsKind));
+            if (matricesWeights === null || this.skeleton == null) {
+                return {skinned:false, valid: true, report:"not skinned"}
+            }
+
+            let numWeights = matricesWeights.length;
+            let numberNotSorted : number = 0;
+            let missingWeights : number  = 0;
+            let maxUsedWeights : number  = 0;
+            let numberNotNormalized :number  = 0;
+            let numInfluences : number  = matricesWeightsExtra === null ? 4 : 8;
+            var usedWeightCounts = new Array<number>();
+            for (var a = 0; a <= numInfluences; a++) { 
+                usedWeightCounts[a] = 0;
+            }
+            const toleranceEpsilon : number = 0.001;
+
+            for (var a = 0; a < numWeights; a += 4) {
+
+                let lastWeight : number = matricesWeights[a];
+                var t = lastWeight;
+                let usedWeights : number = t===0 ? 0 : 1;
+
+                for (var b = 1; b < numInfluences; b++) {
+                    var d = b < 4 ? matricesWeights[a + b] : matricesWeightsExtra[a + b-4];
+                    if (d > lastWeight) numberNotSorted++;
+                    if (d !== 0) usedWeights++;
+                    t += d;
+                    lastWeight = d;
                 }
-                if (weight > epsilon) {
-                    let mweight = 1.0 / weight;
-                    for (var j = 0; j < 4; j++) {
-                        matricesWeights[i + j] *= mweight;
-                    }
-                    if (matricesWeightsExtra) {
-                        for (var j = 0; j < 4; j++) {
-                            matricesWeightsExtra[i + j] *= mweight;
-                        }
-                    }
-                } else {
-                    if (firstZeroWeight >= 4) {
-                        matricesWeightsExtra[i + firstZeroWeight - 4] = 1.0 - weight;
-                        matricesIndicesExtra[i + firstZeroWeight - 4] = noInfluenceBoneIndex;
-                    } else {
-                        matricesWeights[i + firstZeroWeight] = 1.0 - weight;
-                        matricesIndices[i + firstZeroWeight] = noInfluenceBoneIndex;
+                // count the buffer weights usage
+                usedWeightCounts[usedWeights]++;
+
+                // max influences
+                if (usedWeights > maxUsedWeights) maxUsedWeights = usedWeights;
+
+                // check for invalid weight and just set it to 1.
+                if (t === 0) {
+                    missingWeights++;
+                } 
+                else {
+                    // renormalize so everything adds to 1 use reciprical 
+                    let recip = 1 / t;
+                    let tolerance = 0;
+                    for (b = 0; b < numInfluences; b++) {
+                        if (b < 4)
+                            tolerance += Math.abs(matricesWeights[a + b] - (matricesWeights[a + b] * recip));
+                        else
+                            tolerance += Math.abs(matricesWeightsExtra[a + b-4] - (matricesWeightsExtra[a + b-4] * recip));
                     }
-                }
+                    // arbitary epsilon value for dicdating not normalized
+                    if (tolerance > toleranceEpsilon) numberNotNormalized++;
+                }   
             }
 
-            this.setVerticesData(VertexBuffer.MatricesIndicesKind, matricesIndices);
-            if (matricesIndicesExtra) {
-                this.setVerticesData(VertexBuffer.MatricesIndicesExtraKind, matricesIndicesExtra);
+            // validate bone indices are in range of the skeleton
+            let numBones:number = this.skeleton.bones.length;
+            let matricesIndices = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesIndicesKind));
+            let matricesIndicesExtra = (<FloatArray>this.getVerticesData(VertexBuffer.MatricesIndicesExtraKind));
+            let numBadBoneIndices : number = 0;
+            for (var a = 0; a < numWeights; a++) {
+                for (var b = 0; b < numInfluences; b++) {
+                    let index = b < 4 ? matricesIndices[b] : matricesIndicesExtra[b-4];
+                    if (index >= numBones || index < 0) numBadBoneIndices++;
+                }
             }
 
-            this.setVerticesData(VertexBuffer.MatricesWeightsKind, matricesWeights);
-            if (matricesWeightsExtra) {
-                this.setVerticesData(VertexBuffer.MatricesWeightsExtraKind, matricesWeightsExtra);
-            }
+            
+            // log mesh stats
+            var output = "Number of Weights = " + numWeights/4 + "\nMaximum influences = " + maxUsedWeights + 
+                         "\nMissing Weights = " + missingWeights + "\nNot Sorted = " + numberNotSorted +
+                         "\nNot Normalized = " + numberNotNormalized + "\nWeightCounts = [" + usedWeightCounts + "]" +
+                         "\nNumber of bones = " + numBones + "\nBad Bone Indices = " + numBadBoneIndices ;
+            
+            return {skinned:true, valid: missingWeights===0 && numberNotNormalized===0 && numBadBoneIndices===0, report: output};
         }
 
-
         /** @hidden */
         public _checkDelayState(): Mesh {
             var scene = this.getScene();

BIN
tests/validation/ReferenceImages/colorGrading.png


+ 5 - 0
tests/validation/config.json

@@ -2,6 +2,11 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
+      "title": "Color Grading",
+      "playgroundId": "#8EDB5N#2",
+      "referenceImage": "colorGrading.png"
+    },
+    {
       "title": "Ribbon morphing",
       "playgroundId": "#ACKC2#1",
       "renderCount": 50,