Browse Source

Merge pull request #917 from julien-moreau/master

Updated glTF loader to 1.0
David Catuhe 9 năm trước cách đây
mục cha
commit
62102d985a

+ 6 - 3
loaders/glTF/README.md

@@ -32,16 +32,19 @@ BABYLON.SceneLoader.ImportMesh(["myMesh1", "myMesh2", "..."], "./", "duck.gltf",
     * From base64 buffers
 * Import lights
 * Import cameras
-* Import and set custom shaders (if no shaders, the Babylon.js default material is applied)
+* Import and set custom shaders (BETA) (if no shaders, the Babylon.js default material is applied)
     * Automatically bind attributes
     * Automatically bind matrices
     * Set uniforms
 * Import and set animations
-* Skinning
+* Skinning (BETA)
     * Skeletons
     * Hardware skinning (shaders support)
     * Bones import
 * Handle dummy nodes (empty nodes)
 
 ## To improve
-* Test on more models
+* Test on more geometries
+* Test on more animated models
+* Test on more skinned models
+* Improve shaders support (glitches with samplers can appear in particular configurations)

+ 147 - 48
loaders/glTF/babylon.glTFFileLoader.js

@@ -89,7 +89,7 @@ var BABYLON;
     var EBlendingFunction = BABYLON.EBlendingFunction;
     /**
     * Tokenizer. Used for shaders compatibility
-    * Automatically map world, view, projection, worldViewProjection and attributes
+    * Automatically map world, view, projection, worldViewProjection, attributes and so on
     */
     var ETokenType;
     (function (ETokenType) {
@@ -436,13 +436,120 @@ var BABYLON;
     /**
     * Returns the parent bone
     */
-    var getParentBone = function (jointName, newSkeleton) {
+    var getParentBone = function (gltfRuntime, skins, jointName, newSkeleton) {
         // Try to find
         for (var i = 0; i < newSkeleton.bones.length; i++) {
             if (newSkeleton.bones[i].id === jointName) {
                 return newSkeleton.bones[i];
             }
         }
+        // Not found, search in gltf nodes
+        var nodes = gltfRuntime.nodes;
+        for (var nde in nodes) {
+            var node = nodes[nde];
+            if (!node.jointName) {
+                continue;
+            }
+            var children = node.children;
+            for (var i = 0; i < children.length; i++) {
+                var child = gltfRuntime.nodes[children[i]];
+                if (!child.jointName) {
+                    continue;
+                }
+                if (child.jointName === jointName) {
+                    var mat = configureBoneTransformation(node);
+                    var bone = new BABYLON.Bone(node.name, newSkeleton, getParentBone(gltfRuntime, skins, node.jointName, newSkeleton), mat);
+                    bone.id = nde;
+                    return bone;
+                }
+            }
+        }
+        return null;
+    };
+    /**
+    * Returns the appropriate root node
+    */
+    var getNodeToRoot = function (nodesToRoot, id) {
+        for (var i = 0; i < nodesToRoot.length; i++) {
+            var nodeToRoot = nodesToRoot[i];
+            for (var j = 0; j < nodeToRoot.node.children.length; j++) {
+                var child = nodeToRoot.node.children[j];
+                if (child === id) {
+                    return nodeToRoot.bone;
+                }
+            }
+        }
+        return null;
+    };
+    /**
+    * Returns the node with the joint name
+    */
+    var getJointNode = function (gltfRuntime, jointName) {
+        var nodes = gltfRuntime.nodes;
+        var node = nodes[jointName];
+        if (node) {
+            return {
+                node: node,
+                id: jointName
+            };
+        }
+        for (var nde in nodes) {
+            node = nodes[nde];
+            if (node.jointName === jointName) {
+                return {
+                    node: node,
+                    id: nde
+                };
+            }
+        }
+        return null;
+    };
+    /**
+    * Checks if a nodes is in joints
+    */
+    var nodeIsInJoints = function (skins, id) {
+        for (var i = 0; i < skins.jointNames.length; i++) {
+            if (skins.jointNames[i] === id) {
+                return true;
+            }
+        }
+        return false;
+    };
+    /**
+    * Fills the nodes to root for bones and builds hierarchy
+    */
+    var getNodesToRoot = function (gltfRuntime, newSkeleton, skins, nodesToRoot) {
+        // Creates nodes for root
+        for (var nde in gltfRuntime.nodes) {
+            var node = gltfRuntime.nodes[nde];
+            var id = nde;
+            if (!node.jointName || nodeIsInJoints(skins, node.jointName)) {
+                continue;
+            }
+            // Create node to root bone
+            var mat = configureBoneTransformation(node);
+            var bone = new BABYLON.Bone(node.name, newSkeleton, null, mat);
+            bone.id = id;
+            nodesToRoot.push({ bone: bone, node: node, id: id });
+        }
+        // Parenting
+        for (var i = 0; i < nodesToRoot.length; i++) {
+            var nodeToRoot = nodesToRoot[i];
+            var children = nodeToRoot.node.children;
+            for (var j = 0; j < children.length; j++) {
+                var child = null;
+                for (var k = 0; k < nodesToRoot.length; k++) {
+                    if (nodesToRoot[k].id === children[j]) {
+                        child = nodesToRoot[k];
+                        break;
+                    }
+                }
+                if (child) {
+                    child.bone._parent = nodeToRoot.bone;
+                    nodeToRoot.bone.children.push(child.bone);
+                }
+            }
+        }
     };
     /**
     * Imports a skeleton
@@ -459,40 +566,21 @@ var BABYLON;
         var buffer = getBufferFromAccessor(gltfRuntime, accessor);
         var bindShapeMatrix = BABYLON.Matrix.FromArray(skins.bindShapeMatrix);
         newSkeleton._identity = bindShapeMatrix;
-        // Find the root bone
-        var nodeToRoot = null;
-        for (var nde in gltfRuntime.nodes) {
-            var node = gltfRuntime.nodes[nde];
-            if (!node) {
-                continue;
-            }
-            if (node.jointName) {
-                var isInJoints = false;
-                for (var i = 0; i < skins.jointNames.length; i++) {
-                    if (skins.jointNames[i] === nde) {
-                        isInJoints = true;
-                        break;
-                    }
-                }
-            }
-            if (!isInJoints) {
-                var mat = configureBoneTransformation(node);
-                nodeToRoot = new BABYLON.Bone(node.name, newSkeleton, null, mat);
-                nodeToRoot.id = nde;
-                // Remove root from bones
-                newSkeleton.bones = [];
-                break;
-            }
-        }
+        // Find the root bones
+        var nodesToRoot = [];
+        var nodesToRootToAdd = [];
+        getNodesToRoot(gltfRuntime, newSkeleton, skins, nodesToRoot);
+        newSkeleton.bones = [];
         // Joints
         for (var i = 0; i < skins.jointNames.length; i++) {
-            var node = gltfRuntime.nodes[skins.jointNames[i]];
+            var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
+            var node = jointNode.node;
             if (!node) {
                 BABYLON.Tools.Warn("Joint named " + skins.jointNames[i] + " does not exist");
                 continue;
             }
-            var id = skins.jointNames[i];
-            // Check if node exists, should never happen
+            var id = jointNode.id;
+            // Check if node already exists
             var foundBone = false;
             for (var j = 0; j < newSkeleton.bones.length; j++) {
                 if (newSkeleton.bones[j].id === id) {
@@ -506,7 +594,7 @@ var BABYLON;
             // Search for parent bone
             var parentBone = null;
             for (var j = 0; j < i; j++) {
-                var joint = gltfRuntime.nodes[skins.jointNames[j]];
+                var joint = getJointNode(gltfRuntime, skins.jointNames[j]).node;
                 if (!joint) {
                     BABYLON.Tools.Warn("Joint named " + skins.jointNames[j] + " does not exist when looking for parent");
                     continue;
@@ -515,7 +603,7 @@ var BABYLON;
                 foundBone = false;
                 for (var k = 0; k < children.length; k++) {
                     if (children[k] === id) {
-                        parentBone = getParentBone(skins.jointNames[j], newSkeleton);
+                        parentBone = getParentBone(gltfRuntime, skins, skins.jointNames[j], newSkeleton);
                         foundBone = true;
                         break;
                     }
@@ -526,12 +614,36 @@ var BABYLON;
             }
             // Create bone
             var mat = configureBoneTransformation(node);
-            var bone = new BABYLON.Bone(node.name, newSkeleton, parentBone ? parentBone : nodeToRoot, mat);
+            if (!parentBone) {
+                parentBone = getNodeToRoot(nodesToRoot, id);
+                if (parentBone) {
+                    if (nodesToRootToAdd.indexOf(parentBone) === -1) {
+                        nodesToRootToAdd.push(parentBone);
+                    }
+                }
+            }
+            var bone = new BABYLON.Bone(node.name, newSkeleton, parentBone, mat);
             bone.id = id;
         }
+        // Polish
+        var bones = newSkeleton.bones;
+        newSkeleton.bones = [];
+        for (var i = 0; i < skins.jointNames.length; i++) {
+            var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
+            if (!jointNode) {
+                continue;
+            }
+            for (var j = 0; j < bones.length; j++) {
+                if (bones[j].id === jointNode.id) {
+                    newSkeleton.bones.push(bones[j]);
+                    break;
+                }
+            }
+        }
+        // Finish
         newSkeleton.prepare();
-        if (nodeToRoot) {
-            newSkeleton.bones.push(nodeToRoot);
+        for (var i = 0; i < nodesToRootToAdd.length; i++) {
+            newSkeleton.bones.push(nodesToRootToAdd[i]);
         }
         return newSkeleton;
     };
@@ -825,18 +937,6 @@ var BABYLON;
         return lastNode;
     };
     /**
-    * Util for ImportMesh function, checks if a given node
-    * is a descendant of the included mesh(es)
-    */
-    var nodeIsDescendantOf = function (node, gltfRuntime) {
-        for (var nde in gltfRuntime.nodes) {
-            var n = gltfRuntime.nodes[nde];
-            for (var i = 0; i < n.children.length; i++) {
-            }
-        }
-        return false;
-    };
-    /**
     * Traverses nodes and creates them
     */
     var traverseNodes = function (gltfRuntime, id, parent, meshIncluded) {
@@ -1404,4 +1504,3 @@ var BABYLON;
     ;
     BABYLON.SceneLoader.RegisterPlugin(new GLTFFileLoader());
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.glTFFileLoader.js.map

+ 177 - 56
loaders/glTF/babylon.glTFFileLoader.ts

@@ -88,7 +88,7 @@
 
     /**
     * Tokenizer. Used for shaders compatibility
-    * Automatically map world, view, projection, worldViewProjection and attributes
+    * Automatically map world, view, projection, worldViewProjection, attributes and so on
     */
     enum ETokenType {
         IDENTIFIER = 1,
@@ -481,75 +481,182 @@
     /**
     * Returns the parent bone
     */
-    var getParentBone = (jointName: string, newSkeleton: Skeleton): Bone => {
+    var getParentBone = (gltfRuntime: IGLTFRuntime, skins: IGLTFSkins, jointName: string, newSkeleton: Skeleton): Bone => {
         // Try to find
         for (var i = 0; i < newSkeleton.bones.length; i++) {
             if (newSkeleton.bones[i].id === jointName) {
                 return newSkeleton.bones[i];
             }
         }
+
+        // Not found, search in gltf nodes
+        var nodes = gltfRuntime.nodes;
+        for (var nde in nodes) {
+            var node: IGLTFNode = nodes[nde];
+
+            if (!node.jointName) {
+                continue;
+            }
+
+            var children = node.children;
+            for (var i = 0; i < children.length; i++) {
+                var child: IGLTFNode = gltfRuntime.nodes[children[i]];
+                if (!child.jointName) {
+                    continue;
+                }
+
+                if (child.jointName === jointName) {
+                    var mat = configureBoneTransformation(node);
+                    var bone = new Bone(node.name, newSkeleton, getParentBone(gltfRuntime, skins, node.jointName, newSkeleton), mat);
+                    bone.id = nde;
+                    return bone;
+                }
+            }
+        }
+
+        return null;
     }
 
     /**
-    * Imports a skeleton
+    * Returns the appropriate root node
     */
-    var importSkeleton = (gltfRuntime: IGLTFRuntime, skins: IGLTFSkins, mesh: Mesh, newSkeleton: Skeleton): Skeleton => {
+    var getNodeToRoot = (nodesToRoot: INodeToRoot[], id: string): Bone => {
+        for (var i = 0; i < nodesToRoot.length; i++) {
+            var nodeToRoot = nodesToRoot[i];
+
+            for (var j = 0; j < nodeToRoot.node.children.length; j++) {
+                var child = nodeToRoot.node.children[j];
+                if (child === id) {
+                    return nodeToRoot.bone;
+                }
+            }
+        }
 
-        if (!newSkeleton) {
-            newSkeleton = new Skeleton(skins.name, "", gltfRuntime.scene);
+        return null;
+    };
+
+    /**
+    * Returns the node with the joint name
+    */
+    var getJointNode = (gltfRuntime: IGLTFRuntime, jointName: string): IJointNode => {
+        var nodes = gltfRuntime.nodes;
+        var node: IGLTFNode = nodes[jointName];
+        if (node) {
+            return {
+                node: node,
+                id: jointName
+            };
         }
 
-        if (!skins.babylonSkeleton) {
-            return newSkeleton;
+        for (var nde in nodes) {
+            node = nodes[nde];
+            if (node.jointName === jointName) {
+                return {
+                    node: node,
+                    id: nde
+                };
+            }
         }
 
-        // Matrices
-        var accessor = gltfRuntime.accessors[skins.inverseBindMatrices];
-        var buffer = getBufferFromAccessor(gltfRuntime, accessor);
-        var bindShapeMatrix = Matrix.FromArray(skins.bindShapeMatrix);
+        return null;
+    }
 
-        (<any>newSkeleton)._identity = bindShapeMatrix;
+    /**
+    * Checks if a nodes is in joints
+    */
+    var nodeIsInJoints = (skins: IGLTFSkins, id: string): boolean => {
+        for (var i = 0; i < skins.jointNames.length; i++) {
+            if (skins.jointNames[i] === id) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 
-        // Find the root bone
-        var nodeToRoot: Bone = null;
+    /**
+    * Fills the nodes to root for bones and builds hierarchy
+    */
+    var getNodesToRoot = (gltfRuntime: IGLTFRuntime, newSkeleton: Skeleton, skins: IGLTFSkins, nodesToRoot: INodeToRoot[]) => {
+        // Creates nodes for root
         for (var nde in gltfRuntime.nodes) {
             var node: IGLTFNode = gltfRuntime.nodes[nde];
+            var id = nde;
 
-            if (!node) {
+            if (!node.jointName || nodeIsInJoints(skins, node.jointName)) {
                 continue;
             }
 
-            if (node.jointName) {
-                var isInJoints = false;
-                for (var i = 0; i < skins.jointNames.length; i++) {
-                    if (skins.jointNames[i] === nde) {
-                        isInJoints = true;
+            // Create node to root bone
+            var mat = configureBoneTransformation(node);
+            var bone = new Bone(node.name, newSkeleton, null, mat);
+            bone.id = id;
+            nodesToRoot.push({ bone: bone, node: node, id: id });
+        }
+
+        // Parenting
+        for (var i = 0; i < nodesToRoot.length; i++) {
+            var nodeToRoot = nodesToRoot[i];
+            var children = nodeToRoot.node.children;
+            
+            for (var j = 0; j < children.length; j++) {
+                var child: INodeToRoot = null;
+                
+                for (var k = 0; k < nodesToRoot.length; k++) {
+                    if (nodesToRoot[k].id === children[j]) {
+                        child = nodesToRoot[k];
                         break;
                     }
                 }
+                
+                if (child) {
+                    (<any>child.bone)._parent = nodeToRoot.bone;
+                    nodeToRoot.bone.children.push(child.bone);
+                }
             }
+        }
+    };
 
-            if (!isInJoints) {
-                var mat = configureBoneTransformation(node);
-                nodeToRoot = new Bone(node.name, newSkeleton, null, mat);
-                nodeToRoot.id = nde;
-                // Remove root from bones
-                newSkeleton.bones = [];
-                break;
-            }
+    /**
+    * Imports a skeleton
+    */
+    var importSkeleton = (gltfRuntime: IGLTFRuntime, skins: IGLTFSkins, mesh: Mesh, newSkeleton: Skeleton): Skeleton => {
+
+        if (!newSkeleton) {
+            newSkeleton = new Skeleton(skins.name, "", gltfRuntime.scene);
         }
 
+        if (!skins.babylonSkeleton) {
+            return newSkeleton;
+        }
+
+        // Matrices
+        var accessor = gltfRuntime.accessors[skins.inverseBindMatrices];
+        var buffer = getBufferFromAccessor(gltfRuntime, accessor);
+
+        var bindShapeMatrix = Matrix.FromArray(skins.bindShapeMatrix);
+        (<any>newSkeleton)._identity = bindShapeMatrix;
+
+        // Find the root bones
+        var nodesToRoot: INodeToRoot[] = [];
+        var nodesToRootToAdd: Bone[] = [];
+
+        getNodesToRoot(gltfRuntime, newSkeleton, skins, nodesToRoot);
+        newSkeleton.bones = [];
+
         // Joints
         for (var i = 0; i < skins.jointNames.length; i++) {
-            var node: IGLTFNode = gltfRuntime.nodes[skins.jointNames[i]];
+            var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
+            var node = jointNode.node;
+
             if (!node) {
                 Tools.Warn("Joint named " + skins.jointNames[i] + " does not exist");
                 continue;
             }
+            
+            var id = jointNode.id;
 
-            var id = skins.jointNames[i];
-
-            // Check if node exists, should never happen
+            // Check if node already exists
             var foundBone = false;
             for (var j = 0; j < newSkeleton.bones.length; j++) {
                 if (newSkeleton.bones[j].id === id) {
@@ -563,9 +670,9 @@
             }
 
             // Search for parent bone
-            var parentBone = null;
+            var parentBone: Bone = null;
             for (var j = 0; j < i; j++) {
-                var joint: IGLTFNode = gltfRuntime.nodes[skins.jointNames[j]];
+                var joint: IGLTFNode = getJointNode(gltfRuntime, skins.jointNames[j]).node;
 
                 if (!joint) {
                     Tools.Warn("Joint named " + skins.jointNames[j] + " does not exist when looking for parent");
@@ -577,12 +684,12 @@
 
                 for (var k = 0; k < children.length; k++) {
                     if (children[k] === id) {
-                        parentBone = getParentBone(skins.jointNames[j], newSkeleton);
+                        parentBone = getParentBone(gltfRuntime, skins, skins.jointNames[j], newSkeleton);
                         foundBone = true;
                         break;
                     }
                 }
-
+                
                 if (foundBone) {
                     break;
                 }
@@ -591,14 +698,44 @@
             // Create bone
             var mat = configureBoneTransformation(node);
 
-            var bone = new Bone(node.name, newSkeleton, parentBone ? parentBone : nodeToRoot, mat);
+            if (!parentBone) {
+                parentBone = getNodeToRoot(nodesToRoot, id);
+
+                if (parentBone) {
+                    if (nodesToRootToAdd.indexOf(parentBone) === -1) {
+                        nodesToRootToAdd.push(parentBone);
+                    }
+                }
+            }
+
+            var bone = new Bone(node.name, newSkeleton, parentBone, mat);
             bone.id = id;
         }
 
+        // Polish
+        var bones = newSkeleton.bones;
+        newSkeleton.bones = [];
+
+        for (var i = 0; i < skins.jointNames.length; i++) {
+            var jointNode: IJointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
+
+            if (!jointNode) {
+                continue;
+            }
+
+            for (var j = 0; j < bones.length; j++) {
+                if (bones[j].id === jointNode.id) {
+                    newSkeleton.bones.push(bones[j]);
+                    break;
+                }
+            }
+        }
+
+        // Finish
         newSkeleton.prepare();
 
-        if (nodeToRoot) {
-            newSkeleton.bones.push(nodeToRoot);
+        for (var i = 0; i < nodesToRootToAdd.length; i++) {
+            newSkeleton.bones.push(nodesToRootToAdd[i]);
         }
 
         return newSkeleton;
@@ -959,22 +1096,6 @@
     };
 
     /**
-    * Util for ImportMesh function, checks if a given node
-    * is a descendant of the included mesh(es)
-    */
-    var nodeIsDescendantOf = (node: IGLTFRuntime, gltfRuntime: IGLTFRuntime): boolean => {
-        for (var nde in gltfRuntime.nodes) {
-            var n: IGLTFNode = gltfRuntime.nodes[nde];
-
-            for (var i = 0; i < n.children.length; i++) {
-
-            }
-        }
-
-        return false;
-    }
-
-    /**
     * Traverses nodes and creates them
     */
     var traverseNodes = (gltfRuntime: IGLTFRuntime, id: string, parent: Node, meshIncluded?: boolean) => {

+ 0 - 1
loaders/glTF/babylon.glTFFileLoaderInterfaces.js

@@ -1 +0,0 @@
-//# sourceMappingURL=babylon.glTFFileLoaderInterfaces.js.map

+ 14 - 0
loaders/glTF/babylon.glTFFileLoaderInterfaces.ts

@@ -267,4 +267,18 @@
 
         dummyNodes: Node[];
     }
+
+    /**
+    * Bones
+    */
+    export interface INodeToRoot {
+        bone: Bone;
+        node: IGLTFNode;
+        id: string;
+    }
+
+    export interface IJointNode {
+        node: IGLTFNode;
+        id: string;
+    }
 }