David Catuhe 6 năm trước cách đây
mục cha
commit
43024442ae

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

@@ -19,6 +19,7 @@
 
 ### Core Engine
 
+- Added support for bone matrix texture. Now skeletons will use a texture instead of uniforms when possible ([Deltakosh](https://github.com/deltakosh))
 - Refactored of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
 - Added utility function `Tools.BuildArray` for array initialisation ([barroij](https://github.com/barroij))
 - Introduced a new `IOfflineSupport` interface to hide IndexedDB ([Deltakosh](https://github.com/deltakosh))

+ 47 - 4
src/Bones/babylon.skeleton.ts

@@ -25,6 +25,7 @@ module BABYLON {
         private _scene: Scene;
         private _isDirty = true;
         private _transformMatrices: Float32Array;
+        private _transformMatrixTexture: Nullable<RawTexture>;
         private _meshesWithPoseMatrix = new Array<AbstractMesh>();
         private _animatables: IAnimatable[];
         private _identity = Matrix.Identity();
@@ -34,11 +35,19 @@ module BABYLON {
 
         private _lastAbsoluteTransformsUpdateId = -1;
 
+        private _canUseTextureForBones = false;
+
         /**
          * Specifies if the skeleton should be serialized
          */
         public doNotSerialize = false;
 
+        /**
+         * Gets or sets a boolean indicating that bone matrices should be stored as a texture instead of using shader uniforms (default is true).
+         * Please note that this option is not available when needInitialSkinMatrix === true or if the hardware does not support it
+         */
+        public useTextureToStoreBoneMatrices = true;
+
         private _animationPropertiesOverride: Nullable<AnimationPropertiesOverride> = null;
 
         /**
@@ -63,6 +72,13 @@ module BABYLON {
         public onBeforeComputeObservable = new Observable<Skeleton>();
 
         /**
+         * Gets a boolean indicating that the skeleton effectively stores matrices into a texture
+         */
+        public get isUsingTextureForMatrices() {
+            return this.useTextureToStoreBoneMatrices && this._canUseTextureForBones && !this.needInitialSkinMatrix;
+        }
+
+        /**
          * Creates a new skeleton
          * @param name defines the skeleton name
          * @param id defines the skeleton Id
@@ -77,10 +93,13 @@ module BABYLON {
 
             this._scene = scene || Engine.LastCreatedScene;
 
-            scene.skeletons.push(this);
+            this._scene.skeletons.push(this);
 
             //make sure it will recalculate the matrix next time prepare is called.
             this._isDirty = true;
+
+            const engineCaps = this._scene.getEngine().getCaps();
+            this._canUseTextureForBones = engineCaps.textureFloat && engineCaps.maxVertexTextureImageUnits > 0;
         }
 
         // Members
@@ -102,6 +121,14 @@ module BABYLON {
         }
 
         /**
+         * Gets the list of transform matrices to send to shaders inside a texture (one matrix per bone)
+         * @returns a raw texture containing the data
+         */
+        public getTransformMatrixTexture(): Nullable<RawTexture> {
+            return this._transformMatrixTexture;
+        }
+
+        /**
          * Gets the current hosting scene
          * @returns a scene object
          */
@@ -220,7 +247,7 @@ module BABYLON {
             var frameOffset = this._getHighestAnimationFrame() + 1;
 
             // make a dictionary of source skeleton's bones, so exact same order or doublely nested loop is not required
-            var boneDict: {[key: string]: Bone} = {};
+            var boneDict: { [key: string]: Bone } = {};
             var sourceBones = source.bones;
             var nBones: number;
             var i: number;
@@ -312,8 +339,7 @@ module BABYLON {
             }
         }
 
-        /** @hidden */
-        public _computeTransformMatrices(targetMatrix: Float32Array, initialSkinMatrix: Nullable<Matrix>): void {
+        private _computeTransformMatrices(targetMatrix: Float32Array, initialSkinMatrix: Nullable<Matrix>): void {
 
             this.onBeforeComputeObservable.notifyObservers(this);
 
@@ -378,9 +404,21 @@ module BABYLON {
             } else {
                 if (!this._transformMatrices || this._transformMatrices.length !== 16 * (this.bones.length + 1)) {
                     this._transformMatrices = new Float32Array(16 * (this.bones.length + 1));
+
+                    if (this.isUsingTextureForMatrices) {
+                        if (this._transformMatrixTexture) {
+                            this._transformMatrixTexture.dispose();
+                        }
+
+                        this._transformMatrixTexture = RawTexture.CreateRGBATexture(this._transformMatrices, (this.bones.length + 1) * 4, 1, this._scene, false, false, Engine.TEXTURE_NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT);
+                    }
                 }
 
                 this._computeTransformMatrices(this._transformMatrices, null);
+
+                if (this.isUsingTextureForMatrices && this._transformMatrixTexture) {
+                    this._transformMatrixTexture.update(this._transformMatrices);
+                }
             }
 
             this._isDirty = false;
@@ -470,6 +508,11 @@ module BABYLON {
 
             // Remove from scene
             this.getScene().removeSkeleton(this);
+
+            if (this._transformMatrixTexture) {
+                this._transformMatrixTexture.dispose();
+                this._transformMatrixTexture = null;
+            }
         }
 
         /**

+ 27 - 12
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -537,14 +537,14 @@ module BABYLON {
          */
         public setDarkness(darkness: number): ShadowGenerator {
             if (darkness >= 1.0) {
-                this._darkness = 1.0;
+                this._darkness = 1.0;
             }
             else if (darkness <= 0.0) {
-                this._darkness = 0.0;
-                 }
+                this._darkness = 0.0;
+            }
             else {
-                this._darkness = darkness;
-                 }
+                this._darkness = darkness;
+            }
             return this;
         }
 
@@ -907,8 +907,16 @@ module BABYLON {
                 }
 
                 // Bones
-                if (mesh.useBones && mesh.computeBonesUsingShaders) {
-                    this._effect.setMatrices("mBones", (<Skeleton>mesh.skeleton).getTransformMatrices((mesh)));
+                if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
+                    const skeleton = mesh.skeleton;
+
+                    if (skeleton.isUsingTextureForMatrices) {
+                        const boneTexture = skeleton.getTransformMatrixTexture();
+                        this._effect.setTexture("boneSampler", boneTexture);
+                        this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
+                    } else {
+                        this._effect.setMatrices("mBones", skeleton.getTransformMatrices((mesh)));
+                    }
                 }
 
                 // Morph targets
@@ -1074,15 +1082,22 @@ module BABYLON {
             }
 
             // Bones
-            if (mesh.useBones && mesh.computeBonesUsingShaders) {
+            if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
                 attribs.push(VertexBuffer.MatricesIndicesKind);
                 attribs.push(VertexBuffer.MatricesWeightsKind);
                 if (mesh.numBoneInfluencers > 4) {
                     attribs.push(VertexBuffer.MatricesIndicesExtraKind);
                     attribs.push(VertexBuffer.MatricesWeightsExtraKind);
                 }
+                const skeleton = mesh.skeleton;
                 defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
-                defines.push("#define BonesPerMesh " + ((<Skeleton>mesh.skeleton).bones.length + 1));
+
+                if (skeleton.isUsingTextureForMatrices) {
+                    defines.push("#define BONETEXTURE");
+                } else {
+                    defines.push("#define BonesPerMesh " + (skeleton.bones.length + 1));
+                }
+
             } else {
                 defines.push("#define NUM_BONE_INFLUENCERS 0");
             }
@@ -1095,7 +1110,7 @@ module BABYLON {
                     defines.push("#define MORPHTARGETS");
                     morphInfluencers = manager.numInfluencers;
                     defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
-                    MaterialHelper.PrepareAttributesForMorphTargets(attribs, mesh, {"NUM_MORPH_INFLUENCERS": morphInfluencers });
+                    MaterialHelper.PrepareAttributesForMorphTargets(attribs, mesh, { "NUM_MORPH_INFLUENCERS": morphInfluencers });
                 }
             }
 
@@ -1114,8 +1129,8 @@ module BABYLON {
                 this._cachedDefines = join;
                 this._effect = this._scene.getEngine().createEffect("shadowMap",
                     attribs,
-                    ["world", "mBones", "viewProjection", "diffuseMatrix", "lightData", "depthValues", "biasAndScale", "morphTargetInfluences"],
-                    ["diffuseSampler"], join,
+                    ["world", "mBones", "viewProjection", "diffuseMatrix", "lightData", "depthValues", "biasAndScale", "morphTargetInfluences", "boneTextureWidth"],
+                    ["diffuseSampler", "boneSampler"], join,
                     undefined, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
             }
 

+ 3 - 2
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -103,6 +103,7 @@ module BABYLON {
 
         public NUM_BONE_INFLUENCERS = 0;
         public BonesPerMesh = 0;
+        public BONETEXTURE = false;
 
         public NONUNIFORMSCALING = false;
 
@@ -1026,14 +1027,14 @@ module BABYLON {
                 "vSphericalXX", "vSphericalYY", "vSphericalZZ",
                 "vSphericalXY", "vSphericalYZ", "vSphericalZX",
                 "vReflectionMicrosurfaceInfos", "vRefractionMicrosurfaceInfos",
-                "vTangentSpaceParams"
+                "vTangentSpaceParams", "boneTextureWidth"
             ];
 
             var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
                 "bumpSampler", "lightmapSampler", "opacitySampler",
                 "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh",
                 "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh",
-                "microSurfaceSampler", "environmentBrdfSampler"];
+                "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
             var uniformBuffers = ["Material", "Scene"];
 
             if (ImageProcessingConfiguration) {

+ 1 - 1
src/Materials/babylon.effect.ts

@@ -623,10 +623,10 @@ module BABYLON {
             result = result.replace(/attribute[ \t]/g, "in ");
             result = result.replace(/[ \t]attribute/g, " in");
 
+            result = result.replace(/texture2D\s*\(/g, "texture(");
             if (isFragment) {
                 result = result.replace(/texture2DLodEXT\s*\(/g, "textureLod(");
                 result = result.replace(/textureCubeLodEXT\s*\(/g, "textureLod(");
-                result = result.replace(/texture2D\s*\(/g, "texture(");
                 result = result.replace(/textureCube\s*\(/g, "texture(");
                 result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
                 result = result.replace(/gl_FragColor/g, "glFragColor");

+ 20 - 4
src/Materials/babylon.materialHelper.ts

@@ -174,7 +174,15 @@ module BABYLON {
             if (useBones) {
                 if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
                     defines["NUM_BONE_INFLUENCERS"] = mesh.numBoneInfluencers;
-                    defines["BonesPerMesh"] = (mesh.skeleton.bones.length + 1);
+
+                    const materialSupportsBoneTexture = defines["BONETEXTURE"] !== undefined;
+
+                    if (mesh.skeleton.isUsingTextureForMatrices && materialSupportsBoneTexture) {
+                        defines["BONETEXTURE"] = true;
+                    } else {
+                        defines["BonesPerMesh"] = (mesh.skeleton.bones.length + 1);
+                        defines["BONETEXTURE"] = materialSupportsBoneTexture ? false : undefined;
+                    }
                 } else {
                     defines["NUM_BONE_INFLUENCERS"] = 0;
                     defines["BonesPerMesh"] = 0;
@@ -606,10 +614,18 @@ module BABYLON {
             }
 
             if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                var matrices = mesh.skeleton.getTransformMatrices(mesh);
+                const skeleton = mesh.skeleton;
 
-                if (matrices) {
-                    effect.setMatrices("mBones", matrices);
+                if (skeleton.isUsingTextureForMatrices && effect.getUniformIndex("boneTextureWidth") > -1) {
+                    const boneTexture = skeleton.getTransformMatrixTexture();
+                    effect.setTexture("boneSampler", boneTexture);
+                    effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
+                } else {
+                    const matrices = skeleton.getTransformMatrices(mesh);
+
+                    if (matrices) {
+                        effect.setMatrices("mBones", matrices);
+                    }
                 }
             }
         }

+ 19 - 3
src/Materials/babylon.shaderMaterial.ts

@@ -406,12 +406,28 @@ module BABYLON {
                     attribs.push(VertexBuffer.MatricesIndicesExtraKind);
                     attribs.push(VertexBuffer.MatricesWeightsExtraKind);
                 }
+
+                const skeleton = mesh.skeleton;
+
                 defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
-                defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
                 fallbacks.addCPUSkinningFallback(0, mesh);
 
-                if (this._options.uniforms.indexOf("mBones") === -1) {
-                    this._options.uniforms.push("mBones");
+                if (skeleton.isUsingTextureForMatrices) {
+                    defines.push("#define BONETEXTURE");
+
+                    if (this._options.uniforms.indexOf("boneTextureWidth") === -1) {
+                        this._options.uniforms.push("boneTextureWidth");
+                    }
+
+                    if (this._options.samplers.indexOf("boneSampler") === -1) {
+                        this._options.samplers.push("boneSampler");
+                    }
+                } else {
+                    defines.push("#define BonesPerMesh " + (skeleton.bones.length + 1));
+
+                    if (this._options.uniforms.indexOf("mBones") === -1) {
+                        this._options.uniforms.push("mBones");
+                    }
                 }
 
             } else {

+ 5 - 2
src/Materials/babylon.standardMaterial.ts

@@ -43,6 +43,7 @@ module BABYLON {
         public VERTEXALPHA = false;
         public NUM_BONE_INFLUENCERS = 0;
         public BonesPerMesh = 0;
+        public BONETEXTURE = false;
         public INSTANCES = false;
         public GLOSSINESS = false;
         public ROUGHNESS = false;
@@ -1080,10 +1081,12 @@ module BABYLON {
                     "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "normalMatrix", "lightmapMatrix", "refractionMatrix",
                     "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
                     "vReflectionPosition", "vReflectionSize",
-                    "logarithmicDepthConstant", "vTangentSpaceParams", "alphaCutOff"
+                    "logarithmicDepthConstant", "vTangentSpaceParams", "alphaCutOff", "boneTextureWidth"
                 ];
 
-                var samplers = ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler"];
+                var samplers = ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler",
+                    "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler",
+                    "refractionCubeSampler", "refraction2DSampler", "boneSampler"];
 
                 var uniformBuffers = ["Material", "Scene"];
 

+ 26 - 6
src/Shaders/ShadersInclude/bones300Declaration.fx

@@ -1,10 +1,30 @@
 #if NUM_BONE_INFLUENCERS > 0
-	uniform mat4 mBones[BonesPerMesh];
+	#ifdef BONETEXTURE
+		uniform sampler2D boneSampler;
+	#else
+		uniform mat4 mBones[BonesPerMesh];
+	#endif	
 
-	in vec4 matricesIndices;
-	in vec4 matricesWeights;
-	#if NUM_BONE_INFLUENCERS > 4
-		in vec4 matricesIndicesExtra;
-		in vec4 matricesWeightsExtra;
+		in vec4 matricesIndices;
+		in vec4 matricesWeights;
+		#if NUM_BONE_INFLUENCERS > 4
+			in vec4 matricesIndicesExtra;
+			in vec4 matricesWeightsExtra;
+		#endif
+
+	#ifdef BONETEXTURE
+		mat4 readMatrixFromRawSampler(sampler2D smp, float index)
+		{
+			mat4 result;
+			float offset = index  * 4.0;	
+			float dx = 1.0 / boneTextureWidth;
+
+			result[0] = texture(smp, vec2(dx * (offset + 0.5), 0.));
+			result[1] = texture(smp, vec2(dx * (offset + 1.5), 0.));
+			result[2] = texture(smp, vec2(dx * (offset + 2.5), 0.));
+			result[3] = texture(smp, vec2(dx * (offset + 3.5), 0.));
+
+			return result;
+		}
 	#endif
 #endif

+ 22 - 1
src/Shaders/ShadersInclude/bonesDeclaration.fx

@@ -1,5 +1,10 @@
 #if NUM_BONE_INFLUENCERS > 0
-	uniform mat4 mBones[BonesPerMesh];
+	#ifdef BONETEXTURE
+		uniform sampler2D boneSampler;
+		uniform float boneTextureWidth;
+	#else
+		uniform mat4 mBones[BonesPerMesh];
+	#endif	
 
 	attribute vec4 matricesIndices;
 	attribute vec4 matricesWeights;
@@ -7,4 +12,20 @@
 		attribute vec4 matricesIndicesExtra;
 		attribute vec4 matricesWeightsExtra;
 	#endif
+
+	#ifdef BONETEXTURE
+		mat4 readMatrixFromRawSampler(sampler2D smp, float index)
+		{
+			float offset = index  * 4.0;	
+			float dx = 1.0 / boneTextureWidth;
+
+			vec4 m0 = texture2D(smp, vec2(dx * (offset + 0.5), 0.));
+			vec4 m1 = texture2D(smp, vec2(dx * (offset + 1.5), 0.));
+			vec4 m2 = texture2D(smp, vec2(dx * (offset + 2.5), 0.));
+			vec4 m3 = texture2D(smp, vec2(dx * (offset + 3.5), 0.));
+
+			return mat4(m0, m1, m2, m3);
+		}
+	#endif
+
 #endif

+ 28 - 0
src/Shaders/ShadersInclude/bonesVertex.fx

@@ -1,5 +1,32 @@
 #if NUM_BONE_INFLUENCERS > 0
 	mat4 influence;
+
+#ifdef BONETEXTURE
+	influence = readMatrixFromRawSampler(boneSampler, matricesIndices[0]) * matricesWeights[0];
+
+	#if NUM_BONE_INFLUENCERS > 1
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndices[1]) * matricesWeights[1];
+	#endif	
+	#if NUM_BONE_INFLUENCERS > 2
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndices[2]) * matricesWeights[2];
+	#endif	
+	#if NUM_BONE_INFLUENCERS > 3
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndices[3]) * matricesWeights[3];
+	#endif	
+
+	#if NUM_BONE_INFLUENCERS > 4
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndicesExtra[0]) * matricesWeightsExtra[0];
+	#endif	
+	#if NUM_BONE_INFLUENCERS > 5
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndicesExtra[1]) * matricesWeightsExtra[1];
+	#endif	
+	#if NUM_BONE_INFLUENCERS > 6
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndicesExtra[2]) * matricesWeightsExtra[2];
+	#endif	
+	#if NUM_BONE_INFLUENCERS > 7
+		influence += readMatrixFromRawSampler(boneSampler, matricesIndicesExtra[3]) * matricesWeightsExtra[3];
+	#endif	
+#else	
 	influence = mBones[int(matricesIndices[0])] * matricesWeights[0];
 
 	#if NUM_BONE_INFLUENCERS > 1
@@ -24,6 +51,7 @@
 	#if NUM_BONE_INFLUENCERS > 7
 		influence += mBones[int(matricesIndicesExtra[3])] * matricesWeightsExtra[3];
 	#endif	
+#endif
 
 	finalWorld = finalWorld * influence;
 #endif

+ 1 - 1
src/Tools/babylon.tools.ts

@@ -457,7 +457,7 @@ module BABYLON {
                 if (child.nodeType === 3) {
                     result += child.textContent;
                 }
-                child = child.nextSibling;
+                child = <any>(child.nextSibling);
             }
 
             return result;

+ 1 - 1
tests/validation/validation.js

@@ -236,7 +236,7 @@ function runTest(index, done) {
         var pgRoot = "/Playground"
 
         var retryTime = 500;
-        var maxRetry = 2;
+        var maxRetry = 5;
         var retry = 0;
 
         var onError = function() {