소스 검색

Merge pull request #4111 from bghgary/bone-fix

Update glTF skin loading to allow shader compilation during load
David Catuhe 7 년 전
부모
커밋
937f5c9be3
3개의 변경된 파일113개의 추가작업 그리고 71개의 파일을 삭제
  1. 50 45
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  2. 29 19
      src/Bones/babylon.bone.ts
  3. 34 7
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

+ 50 - 45
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -527,8 +527,6 @@ module BABYLON.GLTF2 {
         }
 
         private _loadMeshAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, babylonMesh: Mesh): Promise<void> {
-            // TODO: instancing
-
             const promises = new Array<Promise<void>>();
 
             const primitives = mesh.primitives;
@@ -762,9 +760,9 @@ module BABYLON.GLTF2 {
         }
 
         private _loadSkinAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, skin: ILoaderSkin): Promise<void> {
-            const assignSkeleton = () => {
+            const assignSkeleton = (skeleton: Skeleton) => {
                 this._forEachPrimitive(node, babylonMesh => {
-                    babylonMesh.skeleton = skin._babylonSkeleton!;
+                    babylonMesh.skeleton = skeleton;
                 });
 
                 // Ignore the TRS of skinned nodes.
@@ -777,74 +775,81 @@ module BABYLON.GLTF2 {
 
             if (skin._loaded) {
                 return skin._loaded.then(() => {
-                    assignSkeleton();
+                    assignSkeleton(skin._babylonSkeleton!);
                 });
             }
 
-            // TODO: split into two parts so that bones are created before inverseBindMatricesData is loaded (for compiling materials).
+            const skeletonId = `skeleton${skin._index}`;
+            const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
+            skin._babylonSkeleton = babylonSkeleton;
+            this._loadBones(context, skin);
+            assignSkeleton(babylonSkeleton);
 
             return (skin._loaded = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(inverseBindMatricesData => {
-                const skeletonId = `skeleton${skin._index}`;
-                const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
-                skin._babylonSkeleton = babylonSkeleton;
-                this._loadBones(context, skin, inverseBindMatricesData);
-                assignSkeleton();
+                this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
             }));
         }
 
-        private _loadSkinInverseBindMatricesDataAsync(context: string, skin: ILoaderSkin): Promise<Nullable<Float32Array>> {
-            if (skin.inverseBindMatrices == undefined) {
-                return Promise.resolve(null);
-            }
-
-            const accessor = GLTFLoader._GetProperty(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
-            return this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
-                return data as Float32Array;
-            });
-        }
-
-        private _createBone(node: ILoaderNode, skin: ILoaderSkin, parent: Nullable<Bone>, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
-            const babylonBone = new Bone(node.name || `joint${node._index}`, skin._babylonSkeleton!, parent, localMatrix, null, baseMatrix, index);
-
-            node._babylonAnimationTargets = node._babylonAnimationTargets || [];
-            node._babylonAnimationTargets.push(babylonBone);
-
-            return babylonBone;
-        }
-
-        private _loadBones(context: string, skin: ILoaderSkin, inverseBindMatricesData: Nullable<Float32Array>): void {
+        private _loadBones(context: string, skin: ILoaderSkin): void {
             const babylonBones: { [index: number]: Bone } = {};
             for (const index of skin.joints) {
                 const node = GLTFLoader._GetProperty(`${context}/joints/${index}`, this._gltf.nodes, index);
-                this._loadBone(node, skin, inverseBindMatricesData, babylonBones);
+                this._loadBone(node, skin, babylonBones);
             }
         }
 
-        private _loadBone(node: ILoaderNode, skin: ILoaderSkin, inverseBindMatricesData: Nullable<Float32Array>, babylonBones: { [index: number]: Bone }): Bone {
+        private _loadBone(node: ILoaderNode, skin: ILoaderSkin, babylonBones: { [index: number]: Bone }): Bone {
             let babylonBone = babylonBones[node._index];
             if (babylonBone) {
                 return babylonBone;
             }
 
-            const boneIndex = skin.joints.indexOf(node._index);
-
-            let baseMatrix = Matrix.Identity();
-            if (inverseBindMatricesData && boneIndex !== -1) {
-                baseMatrix = Matrix.FromArray(inverseBindMatricesData, boneIndex * 16);
-                baseMatrix.invertToRef(baseMatrix);
-            }
-
             let babylonParentBone: Nullable<Bone> = null;
             if (node._parent._babylonMesh !== this._rootBabylonMesh) {
-                babylonParentBone = this._loadBone(node._parent, skin, inverseBindMatricesData, babylonBones);
-                baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
+                babylonParentBone = this._loadBone(node._parent, skin, babylonBones);
             }
 
-            babylonBone = this._createBone(node, skin, babylonParentBone, this._getNodeMatrix(node), baseMatrix, boneIndex);
+            const boneIndex = skin.joints.indexOf(node._index);
+
+            babylonBone = new Bone(node.name || `joint${node._index}`, skin._babylonSkeleton!, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
             babylonBones[node._index] = babylonBone;
+
+            node._babylonAnimationTargets = node._babylonAnimationTargets || [];
+            node._babylonAnimationTargets.push(babylonBone);
+
             return babylonBone;
         }
 
+        private _loadSkinInverseBindMatricesDataAsync(context: string, skin: ILoaderSkin): Promise<Nullable<Float32Array>> {
+            if (skin.inverseBindMatrices == undefined) {
+                return Promise.resolve(null);
+            }
+
+            const accessor = GLTFLoader._GetProperty(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
+            return this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
+                return data as Float32Array;
+            });
+        }
+
+        private _updateBoneMatrices(babylonSkeleton: Skeleton, inverseBindMatricesData: Nullable<Float32Array>): void {
+            for (const babylonBone of babylonSkeleton.bones) {
+                let baseMatrix = Matrix.Identity();
+                const boneIndex = babylonBone._index!;
+                if (inverseBindMatricesData && boneIndex !== -1) {
+                    Matrix.FromArrayToRef(inverseBindMatricesData, boneIndex * 16, baseMatrix);
+                    baseMatrix.invertToRef(baseMatrix);
+                }
+
+                const babylonParentBone = babylonBone.getParent();
+                if (babylonParentBone) {
+                    baseMatrix.multiplyToRef(babylonParentBone.getInvertedAbsoluteTransform(), baseMatrix);
+                }
+
+                babylonBone.updateMatrix(baseMatrix, false, false);
+                babylonBone._updateDifferenceMatrix(undefined, false);
+            }
+        }
+
         private _getNodeMatrix(node: ILoaderNode): Matrix {
             return node.matrix ?
                 Matrix.FromArray(node.matrix) :

+ 29 - 19
src/Bones/babylon.bone.ts

@@ -38,7 +38,7 @@
         private _parent: Nullable<Bone>;
         private _scalingDeterminant = 1;
         private _worldTransform = new Matrix();
-        
+
         private _localScaling: Vector3;
         private _localRotation: Quaternion;
         private _localPosition: Vector3;
@@ -84,7 +84,9 @@
 
             this.setParent(parentBone, false);
 
-            this._updateDifferenceMatrix();
+            if (baseMatrix || localMatrix) {
+                this._updateDifferenceMatrix();
+            }
         }
 
         // Members
@@ -202,7 +204,7 @@
         public set position(newPosition: Vector3) {
             this._decompose();
             this._localPosition.copyFrom(newPosition);
-            
+
             this._markAsDirtyAndCompose();
         }
 
@@ -267,23 +269,29 @@
         }
 
         /**
-         * Update the local matrix
-         * @param matrix defines the new local matrix
+         * Update the base and local matrices
+         * @param matrix defines the new base or local matrix
          * @param updateDifferenceMatrix defines if the difference matrix must be updated
+         * @param updateLocalMatrix defines if the local matrix should be updated
          */
-        public updateMatrix(matrix: Matrix, updateDifferenceMatrix = true): void {
+        public updateMatrix(matrix: Matrix, updateDifferenceMatrix = true, updateLocalMatrix = true): void {
             this._baseMatrix.copyFrom(matrix);
-            this._localMatrix.copyFrom(matrix);
 
             if (updateDifferenceMatrix) {
                 this._updateDifferenceMatrix();
             }
 
-            this._markAsDirtyAndDecompose();
+            if (updateLocalMatrix) {
+                this._localMatrix.copyFrom(matrix);
+                this._markAsDirtyAndDecompose();
+            }
+            else {
+                this.markAsDirty();
+            }
         }
 
         /** @ignore */
-        public _updateDifferenceMatrix(rootMatrix?: Matrix): void {
+        public _updateDifferenceMatrix(rootMatrix?: Matrix, updateChildren = true): void {
             if (!rootMatrix) {
                 rootMatrix = this._baseMatrix;
             }
@@ -296,8 +304,10 @@
 
             this._absoluteTransform.invertToRef(this._invertedAbsoluteTransform);
 
-            for (var index = 0; index < this.children.length; index++) {
-                this.children[index]._updateDifferenceMatrix();
+            if (updateChildren) {
+                for (var index = 0; index < this.children.length; index++) {
+                    this.children[index]._updateDifferenceMatrix();
+                }
             }
 
             this._scalingDeterminant = (this._absoluteTransform.determinant() < 0 ? -1 : 1);
@@ -318,7 +328,7 @@
         }
 
         private _markAsDirtyAndDecompose() {
-            this.markAsDirty();            
+            this.markAsDirty();
             this._needToDecompose = true;
         }
 
@@ -540,12 +550,12 @@
          * Set the bone scaling in local space
          * @param scale defines the scaling vector
          */
-        public setScale(scale: Vector3): void {          
+        public setScale(scale: Vector3): void {
             this._decompose();
             this._localScaling.copyFrom(scale);
             this._markAsDirtyAndCompose();
-        }    
-        
+        }
+
         /**
          * Gets the current scaling in local space
          * @returns the current scaling vector
@@ -562,7 +572,7 @@
         public getScaleToRef(result: Vector3) {
             this._decompose();
             result.copyFrom(this._localScaling);
-        }        
+        }
 
         /**
          * Set the yaw, pitch, and roll of the bone in local or world space
@@ -575,7 +585,7 @@
         public setYawPitchRoll(yaw: number, pitch: number, roll: number, space = Space.LOCAL, mesh?: AbstractMesh): void {
             if (space === Space.LOCAL) {
                 var quat = Bone._tmpQuat;
-                Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, quat);                
+                Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, quat);
                 this.setRotationQuaternion(quat, space, mesh);
                 return;
             }
@@ -622,7 +632,7 @@
             if (space === Space.LOCAL) {
                 var quat = Bone._tmpQuat;
                 Quaternion.RotationAxisToRef(axis, angle, quat);
-    
+
                 this.setRotationQuaternion(quat, space, mesh);
                 return;
             }
@@ -661,7 +671,7 @@
                 this._localRotation.copyFrom(quat);
 
                 this._markAsDirtyAndCompose();
-    
+
                 return;
             }
 

+ 34 - 7
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -170,7 +170,7 @@ describe('Babylon Scene Loader', function () {
             });
         });
 
-        it('Load CompileMaterialsTest', () => {
+        it('Load CompileMaterialsTest with compileMaterials', () => {
             const scene = new BABYLON.Scene(subject);
             const promises = new Array<Promise<void>>();
             let createShaderProgramSpy: sinon.SinonSpy;
@@ -187,12 +187,9 @@ describe('Babylon Scene Loader', function () {
                 loader.compileMaterials = true;
 
                 promises.push(loader.whenCompleteAsync().then(() => {
-                    try {
-                        expect(createShaderProgramSpy.called, "createShaderProgramSpy.called").to.be.false;
-                    }
-                    finally {
-                        createShaderProgramSpy.restore();
-                    }
+                    const called = createShaderProgramSpy.called;
+                    createShaderProgramSpy.restore();
+                    expect(called, "createShaderProgramCalled").to.be.false;
                 }));
             }, undefined, undefined, undefined, true);
 
@@ -203,6 +200,36 @@ describe('Babylon Scene Loader', function () {
             return Promise.all(promises);
         });
 
+        it('Load BrainStem with compileMaterials', () => {
+            const scene = new BABYLON.Scene(subject);
+            const promises = new Array<Promise<void>>();
+            let createShaderProgramSpy: sinon.SinonSpy;
+
+            subject.runRenderLoop(() => {
+                for (const mesh of scene.meshes) {
+                    if (mesh.material && mesh.isEnabled()) {
+                        expect(mesh.material.isReady(mesh), "mesh material is ready").to.be.true;
+                    }
+                }
+            });
+
+            BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
+                loader.compileMaterials = true;
+
+                promises.push(loader.whenCompleteAsync().then(() => {
+                    const called = createShaderProgramSpy.called;
+                    createShaderProgramSpy.restore();
+                    expect(called, "createShaderProgramCalled").to.be.false;
+                }));
+            }, undefined, undefined, undefined, true);
+
+            promises.push(BABYLON.SceneLoader.AppendAsync("/Playground/scenes/BrainStem/", "BrainStem.gltf", scene).then(() => {
+                createShaderProgramSpy = sinon.spy(subject, "createShaderProgram");
+            }));
+
+            return Promise.all(promises);
+        });
+
         it('Load Alien', () => {
             const scene = new BABYLON.Scene(subject);
             return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/Alien/", "Alien.gltf", scene).then(result => {