瀏覽代碼

Merge pull request #708 from julien-moreau/master

glTF File Loader, improvements and skinned meshes
David Catuhe 10 年之前
父節點
當前提交
652ed9a940

+ 6 - 6
loaders/glTF/README.md

@@ -28,15 +28,15 @@ BABYLON.SceneLoader.Load("./", "duck.gltf", engine, function (scene) {
     * Automatically bind matrices
     * Set uniforms
 * Import and set animations
-
-## Unsupported features
-* ImportMesh function
 * Skinning
     * Skeletons
-    * Bones
     * Hardware skinning (shaders support)
 * Handle dummy nodes (empty nodes)
 
+## Unsupported features
+* ImportMesh function
+* Skinning
+    * Bones animations
+
 ## To improve
-* Y is UP
-* Test on more animations
+* Bones import

+ 226 - 127
loaders/glTF/babylon.glTFFileLoader.js

@@ -235,7 +235,7 @@ var BABYLON;
             default: return BABYLON.Texture.BILINEAR_SAMPLINGMODE;
         }
     };
-    var getBufferFromAccessor = function (gltfRuntime, accessor, scalar) {
+    var getBufferFromAccessor = function (gltfRuntime, accessor) {
         var bufferView = gltfRuntime.bufferViews[accessor.bufferView];
         var arrayBuffer = gltfRuntime.arrayBuffers[bufferView.buffer];
         var byteOffset = accessor.byteOffset + bufferView.byteOffset;
@@ -248,11 +248,6 @@ var BABYLON;
             default: return new Float32Array(arrayBuffer, byteOffset, count);
         }
     };
-    var normalizeBuffer = function (buffer) {
-        for (var i = 0; i < buffer.length; i++) {
-            buffer[i] *= -1;
-        }
-    };
     var normalizeUVs = function (buffer) {
         for (var i = 0; i < buffer.length / 2; i++) {
             buffer[i * 2 + 1] = 1.0 - buffer[i * 2 + 1];
@@ -285,14 +280,22 @@ var BABYLON;
     var isBase64 = function (uri) {
         return uri.length < 5 ? false : uri.substr(0, 5) === "data:";
     };
-    var getTextureFromName = function (gltfRuntime, name) {
-        var textures = gltfRuntime.scene.textures;
-        for (var i = 0; i < textures.length; i++) {
-            if (textures[i].name === name) {
-                return textures[i];
+    var textureHasAlpha = function (texture) {
+        var image = new Image();
+        image.onload = function (ev) {
+            var canvas = document.createElement('canvas');
+            var context = canvas.getContext('2d');
+            context.drawImage(image, 0, 0);
+            var data = context.getImageData(0, 0, image.width, image.height).data;
+            var foundAlpha = false;
+            for (var i = 0; i < data.length; i += 4) {
+                if (data[i + 3] === 0) {
+                    texture.hasAlpha = true;
+                    break;
+                }
             }
-        }
-        return null;
+        };
+        image.src = texture.url;
     };
     /**
     * Load animations
@@ -321,25 +324,29 @@ var BABYLON;
                 var targetID = channel.target.id;
                 var targetNode = gltfRuntime.scene.getNodeByID(targetID);
                 if (targetNode === null) {
-                    targetNode = gltfRuntime.scene.getNodeByName(targetID);
-                }
-                if (targetNode === null) {
                     BABYLON.Tools.Warn("Creating animation named " + anim + ". But cannot find node named " + targetID + " to attach to");
                     continue;
                 }
+                var isBone = targetNode instanceof BABYLON.Bone;
                 // Get target path (position, rotation or scaling)
                 var targetPath = channel.target.path;
                 var targetPathIndex = glTFAnimationPaths.indexOf(targetPath);
                 if (targetPathIndex !== -1) {
                     targetPath = babylonAnimationPaths[targetPathIndex];
                 }
-                // Create key frames and animation
-                var animationType = BABYLON.Animation.ANIMATIONTYPE_VECTOR3;
-                if (targetPath === "rotationQuaternion") {
-                    animationType = BABYLON.Animation.ANIMATIONTYPE_QUATERNION;
-                    targetNode.rotationQuaternion = new BABYLON.Quaternion();
+                // Determine animation type
+                var animationType = BABYLON.Animation.ANIMATIONTYPE_MATRIX;
+                if (!isBone) {
+                    if (targetPath === "rotationQuaternion") {
+                        animationType = BABYLON.Animation.ANIMATIONTYPE_QUATERNION;
+                        targetNode.rotationQuaternion = new BABYLON.Quaternion();
+                    }
+                    else {
+                        animationType = BABYLON.Animation.ANIMATIONTYPE_VECTOR3;
+                    }
                 }
-                var babylonAnimation = new BABYLON.Animation(anim, targetPath, 1, animationType, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
+                // Create animation and key frames
+                var babylonAnimation = new BABYLON.Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
                 var keys = [];
                 for (var i = 0; i < bufferInput.length; i++) {
                     var value = null;
@@ -348,8 +355,27 @@ var BABYLON;
                     }
                     else {
                         value = BABYLON.Vector3.FromArray([bufferOutput[i * 3], bufferOutput[i * 3 + 1], bufferOutput[i * 3 + 2]]);
-                        // Y is up
-                        value.z *= -1;
+                    }
+                    if (isBone) {
+                        var translation = BABYLON.Vector3.Zero();
+                        var rotationQuaternion = new BABYLON.Quaternion();
+                        var scaling = BABYLON.Vector3.Zero();
+                        var bone = targetNode;
+                        var mat = bone.getLocalMatrix();
+                        mat.decompose(scaling, rotationQuaternion, translation);
+                        if (targetPath === "position") {
+                            translation = value;
+                        }
+                        else if (targetPath === "rotationQuaternion") {
+                            rotationQuaternion = value;
+                        }
+                        else {
+                            scaling = value;
+                        }
+                        if (targetNode instanceof BABYLON.Mesh) {
+                            targetNode.a;
+                        }
+                        value = BABYLON.Matrix.Compose(scaling, rotationQuaternion, translation);
                     }
                     keys.push({
                         frame: bufferInput[i],
@@ -359,13 +385,53 @@ var BABYLON;
                 // Finish
                 babylonAnimation.setKeys(keys);
                 targetNode.animations.push(babylonAnimation);
-                gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true);
+                if (!(targetNode instanceof BABYLON.Bone)) {
+                    gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true);
+                }
             }
         }
     };
     /**
-    * Load geometries and nodes
+    * Import skeletons and bones
     */
+    var configureBoneTransformation = function (node) {
+        var mat = null;
+        if (node.translation && node.rotation && node.scale) {
+            mat = BABYLON.Matrix.Compose(BABYLON.Vector3.FromArray(node.scale), BABYLON.Quaternion.RotationAxis(BABYLON.Vector3.FromArray([node.rotation[0], node.rotation[1], node.rotation[2]]).normalize(), node.rotation[3]), BABYLON.Vector3.FromArray(node.translation));
+        }
+        else {
+            mat = BABYLON.Matrix.FromArray(node.matrix);
+        }
+        return mat;
+    };
+    var getParentBone = function (gltfRuntime, jointName, newSkeleton) {
+        for (var nde in gltfRuntime.nodes) {
+            var node = gltfRuntime.nodes[nde];
+            if (!node || !node.jointName) {
+                continue;
+            }
+            for (var i = 0; i < node.children.length; i++) {
+                var child = gltfRuntime.nodes[node.children[i]];
+                if (!child || !node.jointName) {
+                    continue;
+                }
+                if (child.jointName === jointName) {
+                    var parent = gltfRuntime.scene.getNodeByID(nde);
+                    if (parent instanceof BABYLON.Bone) {
+                        return parent;
+                    }
+                    return null;
+                }
+            }
+        }
+        return null;
+        for (var i = 0; i < newSkeleton.bones.length; i++) {
+            if (newSkeleton.bones[i].id === jointName) {
+                return newSkeleton.bones[i];
+            }
+        }
+        return null;
+    };
     var importSkeleton = function (gltfRuntime, skins) {
         var newSkeleton = new BABYLON.Skeleton(skins.name, "", gltfRuntime.scene);
         // Matrices
@@ -378,20 +444,24 @@ var BABYLON;
                 BABYLON.Tools.Warn("Joint named " + skins.jointNames[i] + " does not exist");
                 continue;
             }
-            var mat = BABYLON.Matrix.Identity();
-            if (node.translation && node.rotation && node.scale) {
-                mat = BABYLON.Matrix.Compose(BABYLON.Vector3.FromArray(node.scale), BABYLON.Quaternion.FromArray(node.rotation), BABYLON.Vector3.FromArray(node.translation));
-            }
-            var bone = new BABYLON.Bone(node.name, newSkeleton, null, mat);
-            bone.id = skins.jointNames[i];
-            bone.getInvertedAbsoluteTransform().copyFrom(BABYLON.Matrix.FromArray(buffer.subarray(i * 16, i * 16 + 16)));
+            // Transform
+            var mat = configureBoneTransformation(node);
+            // Parent bone
+            var boneID = skins.jointNames[i];
+            var parentBone = getParentBone(gltfRuntime, boneID, newSkeleton);
+            // Create bone
+            var bone = new BABYLON.Bone(node.name, newSkeleton, parentBone, mat);
+            bone.id = boneID;
         }
+        newSkeleton.prepare();
         return newSkeleton;
     };
-    var importMesh = function (gltfRuntime, mesh, id) {
-        var newMesh = new BABYLON.Mesh(mesh.name, gltfRuntime.scene);
+    /**
+    * Load geometries and nodes
+    */
+    var importMesh = function (gltfRuntime, node, meshes, id, skin) {
+        var newMesh = new BABYLON.Mesh(node.name, gltfRuntime.scene);
         newMesh.id = id;
-        newMesh.material = null;
         newMesh.layerMask = 0x0FFFFFFF;
         newMesh.subMeshes = [];
         var multiMat = new BABYLON.MultiMaterial("multimat" + id, gltfRuntime.scene);
@@ -403,78 +473,93 @@ var BABYLON;
         var verticesCounts = [];
         var indexStarts = [];
         var indexCounts = [];
-        // Positions, normals and UVs
-        for (var i = 0; i < mesh.primitives.length; i++) {
-            // Temporary vertex data
-            var tempVertexData = new BABYLON.VertexData();
-            var primitive = mesh.primitives[i];
-            var attributes = primitive.attributes;
-            var accessor = null;
-            var buffer = null;
-            var verticesCount = 0;
-            // Set positions, normal and uvs
-            for (var semantic in attributes) {
-                // Link accessor and buffer view
-                accessor = gltfRuntime.accessors[attributes[semantic]];
-                buffer = getBufferFromAccessor(gltfRuntime, accessor);
-                if (semantic === "NORMAL") {
-                    tempVertexData.set(buffer, BABYLON.VertexBuffer.NormalKind);
-                }
-                else if (semantic === "POSITION") {
-                    verticesCounts.push(buffer.length);
-                    normalizeBuffer(buffer);
-                    tempVertexData.set(buffer, BABYLON.VertexBuffer.PositionKind);
-                }
-                else if (semantic.indexOf("TEXCOORD_") !== -1) {
-                    var channel = Number(semantic.split("_")[1]);
-                    var uvKind = BABYLON.VertexBuffer.UVKind + (channel === 0 ? "" : (channel + 1));
-                    normalizeUVs(buffer);
-                    tempVertexData.set(buffer, uvKind);
-                }
-                else if (semantic === "JOINT") {
-                    tempVertexData.set(buffer, BABYLON.VertexBuffer.MatricesIndicesKind);
+        for (var meshIndex = 0; meshIndex < meshes.length; meshIndex++) {
+            var meshID = meshes[meshIndex];
+            var mesh = gltfRuntime.meshes[meshID];
+            if (!mesh) {
+                continue;
+            }
+            // Positions, normals and UVs
+            for (var i = 0; i < mesh.primitives.length; i++) {
+                // Temporary vertex data
+                var tempVertexData = new BABYLON.VertexData();
+                var primitive = mesh.primitives[i];
+                if (primitive.primitive !== 4) {
+                    continue;
                 }
-                else if (semantic === "WEIGHT") {
-                    tempVertexData.set(buffer, BABYLON.VertexBuffer.MatricesWeightsKind);
+                var attributes = primitive.attributes;
+                var accessor = null;
+                var buffer = null;
+                // Set positions, normal and uvs
+                for (var semantic in attributes) {
+                    // Link accessor and buffer view
+                    accessor = gltfRuntime.accessors[attributes[semantic]];
+                    buffer = getBufferFromAccessor(gltfRuntime, accessor);
+                    if (semantic === "NORMAL") {
+                        tempVertexData.set(buffer, BABYLON.VertexBuffer.NormalKind);
+                    }
+                    else if (semantic === "POSITION") {
+                        verticesCounts.push(buffer.length);
+                        tempVertexData.set(buffer, BABYLON.VertexBuffer.PositionKind);
+                    }
+                    else if (semantic.indexOf("TEXCOORD_") !== -1) {
+                        var channel = Number(semantic.split("_")[1]);
+                        var uvKind = BABYLON.VertexBuffer.UVKind + (channel === 0 ? "" : (channel + 1));
+                        normalizeUVs(buffer);
+                        tempVertexData.set(buffer, uvKind);
+                    }
+                    else if (semantic === "JOINT") {
+                        tempVertexData.set(buffer, BABYLON.VertexBuffer.MatricesIndicesKind);
+                    }
+                    else if (semantic === "WEIGHT") {
+                        tempVertexData.set(buffer, BABYLON.VertexBuffer.MatricesWeightsKind);
+                    }
                 }
+                // Indices
+                accessor = gltfRuntime.accessors[primitive.indices];
+                buffer = getBufferFromAccessor(gltfRuntime, accessor);
+                tempVertexData.indices = buffer;
+                indexCounts.push(buffer.length);
+                vertexData.merge(tempVertexData);
+                tempVertexData = undefined;
+                // Sub material
+                var material = gltfRuntime.scene.getMaterialByID(primitive.material);
+                multiMat.subMaterials.push(material === null ? gltfRuntime.scene.defaultMaterial : material);
+                // Update vertices start and index start
+                verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
+                indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
             }
-            // Indices
-            accessor = gltfRuntime.accessors[primitive.indices];
-            buffer = getBufferFromAccessor(gltfRuntime, accessor, true);
-            tempVertexData.indices = buffer;
-            indexCounts.push(buffer.length);
-            vertexData.merge(tempVertexData);
-            tempVertexData = undefined;
-            // Sub material
-            var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-            multiMat.subMaterials.push(material === null ? gltfRuntime.scene.defaultMaterial : material);
-            // Update vertices start and index start
-            verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
-            indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
         }
         // Apply geometry
         geometry.setAllVerticesData(vertexData);
         geometry.applyToMesh(newMesh);
+        newMesh.flipFaces(true);
         // Apply submeshes
         newMesh.subMeshes = [];
-        for (var i = 0; i < mesh.primitives.length; i++) {
-            var subMesh = new BABYLON.SubMesh(i, verticesStarts[i], verticesCounts[i], indexStarts[i], indexCounts[i], newMesh);
+        var index = 0;
+        for (var meshIndex = 0; meshIndex < meshes.length; meshIndex++) {
+            var meshID = meshes[meshIndex];
+            var mesh = gltfRuntime.meshes[meshID];
+            if (!mesh) {
+                continue;
+            }
+            for (var i = 0; i < mesh.primitives.length; i++) {
+                if (mesh.primitives[i].primitive !== 4) {
+                    continue;
+                }
+                var subMesh = BABYLON.SubMesh.CreateFromIndices(index, indexStarts[index], indexCounts[index], newMesh, newMesh);
+                index++;
+            }
         }
         // Finish
-        newMesh._updateBoundingInfo();
         return newMesh;
     };
     var configureNode = function (newNode, position, rotation, scaling) {
         if (newNode.position) {
             newNode.position = position;
-            if (newNode.rotation) {
-                newNode.position.x *= -1;
-                newNode.position.y *= -1;
-                newNode.position.z *= -1;
-            }
         }
-        if (newNode.rotation) {
-            newNode.rotation = rotation.toEulerAngles().addInPlace(new BABYLON.Vector3(0, 0, Math.PI));
+        if (newNode.rotationQuaternion || newNode.rotation) {
+            newNode.rotationQuaternion = rotation;
         }
         if (newNode.scaling) {
             newNode.scaling = scaling;
@@ -485,25 +570,33 @@ var BABYLON;
             var position = new BABYLON.Vector3(0, 0, 0);
             var rotation = new BABYLON.Quaternion();
             var scaling = new BABYLON.Vector3(0, 0, 0);
-            BABYLON.Matrix.FromArray(node.matrix).decompose(scaling, rotation, position);
+            var mat = BABYLON.Matrix.FromArray(node.matrix);
+            mat.decompose(scaling, rotation, position);
             configureNode(newNode, position, rotation, scaling);
+            if (newNode instanceof BABYLON.TargetCamera) {
+                newNode.setTarget(BABYLON.Vector3.Zero());
+            }
         }
         else {
-            configureNode(newNode, BABYLON.Vector3.FromArray(node.translation), BABYLON.Quaternion.FromArray(node.rotation), BABYLON.Vector3.FromArray(node.scale));
+            configureNode(newNode, BABYLON.Vector3.FromArray(node.translation), BABYLON.Quaternion.RotationAxis(BABYLON.Vector3.FromArray(node.rotation).normalize(), node.rotation[3]), BABYLON.Vector3.FromArray(node.scale));
         }
     };
-    var importNode = function (gltfRuntime, node) {
+    var importNode = function (gltfRuntime, node, id) {
         var lastNode = null;
         // Meshes
         if (node.instanceSkin) {
             var instanceSkin = node.instanceSkin;
-            for (var i = 0; i < instanceSkin.meshes.length; i++) {
-                var meshID = instanceSkin.meshes[i];
-                var newMesh = importMesh(gltfRuntime, gltfRuntime.meshes[meshID], meshID);
-                newMesh.material = null;
-                //newMesh.useBones = true;
-                //newMesh.computeBonesUsingShaders = true;
-                //newMesh.skeleton = gltfRuntime.scene.getLastSkeletonByID(instanceSkin.skin);
+            if (instanceSkin.meshes) {
+                var skin = gltfRuntime.skins[instanceSkin.skin];
+                var newMesh = importMesh(gltfRuntime, node, instanceSkin.meshes, id, skin);
+                newMesh.skeleton = gltfRuntime.scene.getLastSkeletonByID(instanceSkin.skin);
+                if (newMesh.skeleton === null) {
+                    newMesh.skeleton = importSkeleton(gltfRuntime, gltfRuntime.skins[instanceSkin.skin]);
+                }
+                if (newMesh.skeleton !== null) {
+                    newMesh.useBones = true;
+                    newMesh.applySkeleton(newMesh.skeleton);
+                }
                 lastNode = newMesh;
             }
         }
@@ -511,11 +604,8 @@ var BABYLON;
             /**
             * Improve meshes property
             */
-            for (var i = 0; i < node.meshes.length; i++) {
-                var meshID = node.meshes[i];
-                var newMesh = importMesh(gltfRuntime, gltfRuntime.meshes[meshID], meshID);
-                lastNode = newMesh;
-            }
+            var newMesh = importMesh(gltfRuntime, node, node.mesh ? [node.mesh] : node.meshes, id);
+            lastNode = newMesh;
         }
         else if (node.light) {
             var light = gltfRuntime.lights[node.light];
@@ -580,8 +670,12 @@ var BABYLON;
                     var persCamera = new BABYLON.FreeCamera(node.camera, BABYLON.Vector3.Zero(), gltfRuntime.scene);
                     persCamera.name = node.name;
                     persCamera.attachControl(gltfRuntime.scene.getEngine().getRenderingCanvas());
-                    if (perspectiveCamera.yfov && perspectiveCamera.aspectRatio && perspectiveCamera.znear && perspectiveCamera.zfar) {
-                        persCamera.getViewMatrix().copyFrom(BABYLON.Matrix.PerspectiveFovLH(perspectiveCamera.yfov, perspectiveCamera.aspectRatio, perspectiveCamera.znear, perspectiveCamera.zfar));
+                    if (!perspectiveCamera.aspectRatio) {
+                        perspectiveCamera.aspectRatio = gltfRuntime.scene.getEngine().getRenderWidth() / gltfRuntime.scene.getEngine().getRenderHeight();
+                    }
+                    if (perspectiveCamera.znear && perspectiveCamera.zfar) {
+                        persCamera.maxZ = perspectiveCamera.zfar;
+                        persCamera.minZ = perspectiveCamera.znear;
                     }
                     lastNode = persCamera;
                 }
@@ -589,7 +683,8 @@ var BABYLON;
         }
         // Empty node
         if (lastNode === null && !node.jointName) {
-            lastNode = new BABYLON.Node(node.name, gltfRuntime.scene);
+            var dummy = new BABYLON.Mesh(node.name, gltfRuntime.scene);
+            lastNode = dummy;
         }
         if (lastNode !== null) {
             if (node.matrix) {
@@ -606,20 +701,12 @@ var BABYLON;
     * Load buffers
     */
     var onBuffersLoaded = function (gltfRuntime) {
-        // Skins
-        /*
-        for (var skin in gltfRuntime.skins) {
-            var skins: IGLTFSkins = gltfRuntime.skins[skin];
-            var skeleton = importSkeleton(gltfRuntime, skins);
-            skeleton.id = skin;
-        }
-        */
         // Nodes
         var parsedNodes = gltfRuntime.nodes;
         var currentScene = gltfRuntime.currentScene;
         for (var nde in parsedNodes) {
             var node = parsedNodes[nde];
-            var newNode = importNode(gltfRuntime, node);
+            var newNode = importNode(gltfRuntime, node, nde);
             if (newNode !== null) {
                 newNode.id = nde;
             }
@@ -630,12 +717,10 @@ var BABYLON;
             var parent = gltfRuntime.scene.getNodeByID(nde);
             if (node.children && parent !== null) {
                 for (var i = 0; i < node.children.length; i++) {
-                    var child = gltfRuntime.scene.getNodeByID(node.children[i]);
-                    if (child === null) {
-                        child = gltfRuntime.scene.getNodeByName(node.children[i]);
-                    }
-                    if (child !== null) {
-                        child.parent = parent;
+                    var child = gltfRuntime.nodes[node.children[i]];
+                    var childNode = gltfRuntime.scene.getNodeByID(node.children[i]);
+                    if (childNode !== null) {
+                        childNode.parent = parent;
                     }
                     else {
                         BABYLON.Tools.Warn("Node named " + node.name + " as a children named " + node.children[i] + " but does not exists");
@@ -720,7 +805,7 @@ var BABYLON;
                     continue;
                 }
                 if (type === EParameterType.SAMPLER_2D) {
-                    var texture = getTextureFromName(gltfRuntime, value);
+                    var texture = gltfRuntime.textures[value].babylonTexture;
                     if (texture === null) {
                         continue;
                     }
@@ -761,6 +846,8 @@ var BABYLON;
                     newTexture = new BABYLON.Texture(gltfRuntime.rootUrl + source.uri, gltfRuntime.scene, true);
                 }
                 newTexture.name = value;
+                textureHasAlpha(newTexture);
+                texture.babylonTexture = newTexture;
                 if (texture.internalFormat && (texture.internalFormat === ETextureFormat.ALPHA || texture.internalFormat === ETextureFormat.RGBA)) {
                     newTexture.hasAlpha = true;
                 }
@@ -817,6 +904,7 @@ var BABYLON;
             var pass = technique.passes[technique.pass];
             var instanceProgram = pass.instanceProgram;
             var program = gltfRuntime.programs[instanceProgram.program];
+            var states = pass.states;
             var vertexShader = BABYLON.Effect.ShadersStore[program.vertexShader + "VertexShader"];
             var pixelShader = BABYLON.Effect.ShadersStore[program.fragmentShader + "PixelShader"];
             var newVertexShader = "";
@@ -894,7 +982,8 @@ var BABYLON;
             var options = {
                 attributes: attributes,
                 uniforms: uniforms,
-                samplers: samplers
+                samplers: samplers,
+                needAlphaBlending: states.functions && states.functions.blendEquationSeparate
             };
             BABYLON.Effect.ShadersStore[program.vertexShader + "VertexShader"] = newVertexShader;
             BABYLON.Effect.ShadersStore[program.fragmentShader + "PixelShader"] = newPixelShader;
@@ -984,6 +1073,7 @@ var BABYLON;
                 buffersCount: 0,
                 shaderscount: 0,
                 scene: scene,
+                dummyNodes: [],
                 loadedBuffers: 0,
                 loadedShaders: 0,
                 rootUrl: rootUrl,
@@ -1041,8 +1131,17 @@ var BABYLON;
             if (parsedData.scene && parsedData.scenes) {
                 gltfRuntime.currentScene = parsedData.scenes[parsedData.scene];
             }
-            // Load buffers
+            // Load shaders and buffers
             load(gltfRuntime);
+            // Test on bones
+            for (var i = 0; i < scene.meshes.length; i++) {
+                var mesh = scene.meshes[i];
+                if (mesh.skeleton) {
+                    scene.beginAnimation(mesh.skeleton, 0, 20, true, 0.5, function () {
+                        console.log("finished");
+                    });
+                }
+            }
             // Finish
             return true;
         };

+ 266 - 136
loaders/glTF/babylon.glTFFileLoader.ts

@@ -248,7 +248,7 @@
         }
     };
 
-    var getBufferFromAccessor = (gltfRuntime: IGLTFRuntime, accessor: IGLTFAccessor, scalar?: boolean): any => {
+    var getBufferFromAccessor = (gltfRuntime: IGLTFRuntime, accessor: IGLTFAccessor): any => {
         var bufferView: IGLTFBufferView = gltfRuntime.bufferViews[accessor.bufferView];
         var arrayBuffer: ArrayBuffer = gltfRuntime.arrayBuffers[bufferView.buffer];
 
@@ -264,12 +264,6 @@
         }
     };
 
-    var normalizeBuffer = (buffer: any) => {
-        for (var i = 0; i < buffer.length; i++) {
-            buffer[i] *= -1;
-        }
-    };
-
     var normalizeUVs = (buffer: any) => {
         for (var i = 0; i < buffer.length / 2; i++) {
             buffer[i * 2 + 1] = 1.0 - buffer[i * 2 + 1];
@@ -307,16 +301,26 @@
         return uri.length < 5 ? false : uri.substr(0, 5) === "data:";
     };
 
-    var getTextureFromName = (gltfRuntime: IGLTFRuntime, name: string): BaseTexture => {
-        var textures = gltfRuntime.scene.textures;
+    var textureHasAlpha = (texture: Texture) => {
+        var image = new Image();
+
+        image.onload = (ev: Event) => {
+            var canvas = document.createElement('canvas');
+            var context = canvas.getContext('2d');
+            context.drawImage(image, 0, 0);
 
-        for (var i = 0; i < textures.length; i++) {
-            if (textures[i].name === name) {
-                return textures[i];
+            var data = context.getImageData(0, 0, image.width, image.height).data;
+            var foundAlpha = false;
+
+            for (var i = 0; i < data.length; i+=4) {
+                if (data[i + 3] === 0) {
+                    texture.hasAlpha = true;
+                    break;
+                }
             }
-        }
+        };
 
-        return null;
+        image.src = texture.url;
     };
 
     /**
@@ -355,29 +359,35 @@
                 var targetNode: any = gltfRuntime.scene.getNodeByID(targetID);
 
                 if (targetNode === null) {
-                    targetNode = gltfRuntime.scene.getNodeByName(targetID);
-                }
-
-                if (targetNode === null) {
                     Tools.Warn("Creating animation named " + anim + ". But cannot find node named " + targetID + " to attach to");
                     continue;
                 }
 
+                var isBone = targetNode instanceof Bone;
+
                 // Get target path (position, rotation or scaling)
                 var targetPath = channel.target.path;
                 var targetPathIndex = glTFAnimationPaths.indexOf(targetPath);
+
                 if (targetPathIndex !== -1) {
                     targetPath = babylonAnimationPaths[targetPathIndex];
                 }
 
-                // Create key frames and animation
-                var animationType = Animation.ANIMATIONTYPE_VECTOR3;
-                if (targetPath === "rotationQuaternion") {
-                    animationType = Animation.ANIMATIONTYPE_QUATERNION;
-                    targetNode.rotationQuaternion = new Quaternion();
+                // Determine animation type
+                var animationType = Animation.ANIMATIONTYPE_MATRIX;
+
+                if (!isBone) {
+                    if (targetPath === "rotationQuaternion") {
+                        animationType = Animation.ANIMATIONTYPE_QUATERNION;
+                        targetNode.rotationQuaternion = new Quaternion();
+                    }
+                    else {
+                        animationType = Animation.ANIMATIONTYPE_VECTOR3;
+                    }
                 }
 
-                var babylonAnimation = new Animation(anim, targetPath, 1, animationType, Animation.ANIMATIONLOOPMODE_CYCLE);
+                // Create animation and key frames
+                var babylonAnimation = new Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, Animation.ANIMATIONLOOPMODE_CYCLE);
                 var keys = [];
 
                 for (var i = 0; i < bufferInput.length; i++) {
@@ -388,8 +398,32 @@
                     }
                     else { // Position and scaling are VEC3
                         value = Vector3.FromArray([bufferOutput[i * 3], bufferOutput[i * 3 + 1], bufferOutput[i * 3 + 2]]);
-                        // Y is up
-                        value.z *= -1;
+                    }
+
+                    if (isBone) {
+                        var translation = Vector3.Zero();
+                        var rotationQuaternion = new Quaternion();
+                        var scaling = Vector3.Zero();
+                        var bone = <Bone>targetNode;
+
+                        var mat = bone.getLocalMatrix();
+                        mat.decompose(scaling, rotationQuaternion, translation);
+
+                        if (targetPath === "position") {
+                            translation = value;
+                        }
+                        else if (targetPath === "rotationQuaternion") {
+                            rotationQuaternion = value;
+                        }
+                        else {
+                            scaling = value;
+                        }
+
+                        if (targetNode instanceof Mesh) {
+                            targetNode.a
+                        }
+
+                        value = Matrix.Compose(scaling, rotationQuaternion, translation);
                     }
 
                     keys.push({
@@ -401,14 +435,68 @@
                 // Finish
                 babylonAnimation.setKeys(keys);
                 targetNode.animations.push(babylonAnimation);
-                gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true);
+
+                if (!(targetNode instanceof Bone)) {
+                    gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true);
+                }
             }
         }
     };
 
     /**
-    * Load geometries and nodes
+    * Import skeletons and bones
     */
+    var configureBoneTransformation = (node: IGLTFNode): Matrix => {
+        var mat: Matrix = null;
+        if (node.translation && node.rotation && node.scale) {
+            mat = Matrix.Compose(Vector3.FromArray(node.scale),
+                Quaternion.RotationAxis(Vector3.FromArray([node.rotation[0], node.rotation[1], node.rotation[2]]).normalize(), node.rotation[3]),
+                Vector3.FromArray(node.translation));
+        }
+        else {
+            mat = Matrix.FromArray(node.matrix);
+        }
+
+        return mat;
+    };
+
+    var getParentBone = (gltfRuntime: IGLTFRuntime, jointName: string, newSkeleton: Skeleton): Bone => {
+        
+        for (var nde in gltfRuntime.nodes) {
+            var node: IGLTFNode = gltfRuntime.nodes[nde];
+            if (!node || !node.jointName) {
+                continue;
+            }
+
+            for (var i = 0; i < node.children.length; i++) {
+                var child: IGLTFNode = gltfRuntime.nodes[node.children[i]];
+                if (!child || !node.jointName) {
+                    continue;
+                }
+
+                if (child.jointName === jointName) {
+                    var parent = gltfRuntime.scene.getNodeByID(nde);
+
+                    if (parent instanceof Bone) {
+                        return parent;
+                    }
+
+                    return null;
+                }
+            }
+        }
+
+        return null;
+        
+        for (var i = 0; i < newSkeleton.bones.length; i++) {
+            if (newSkeleton.bones[i].id === jointName) {
+                return newSkeleton.bones[i];
+            }
+        }
+
+        return null;
+    };
+
     var importSkeleton = (gltfRuntime: IGLTFRuntime, skins: IGLTFSkins): Skeleton => {
         var newSkeleton = new Skeleton(skins.name, "", gltfRuntime.scene);
 
@@ -424,24 +512,29 @@
                 continue;
             }
 
-            var mat = Matrix.Identity();
+            // Transform
+            var mat = configureBoneTransformation(node);
 
-            if (node.translation && node.rotation && node.scale) {
-                mat = Matrix.Compose(Vector3.FromArray(node.scale), Quaternion.FromArray(node.rotation), Vector3.FromArray(node.translation));
-            }
+            // Parent bone
+            var boneID = skins.jointNames[i];
+            var parentBone: Bone = getParentBone(gltfRuntime, boneID, newSkeleton);
 
-            var bone = new Bone(node.name, newSkeleton, null, mat);
-            bone.id = skins.jointNames[i];
-            bone.getInvertedAbsoluteTransform().copyFrom(Matrix.FromArray(buffer.subarray(i * 16, i * 16 + 16)));
+            // Create bone
+            var bone = new Bone(node.name, newSkeleton, parentBone, mat);
+            bone.id = boneID;
         }
 
+        newSkeleton.prepare();
+
         return newSkeleton;
     };
 
-    var importMesh = (gltfRuntime: IGLTFRuntime, mesh: IGLTFMesh, id: string): Mesh => {
-        var newMesh = new Mesh(mesh.name, gltfRuntime.scene);
+    /**
+    * Load geometries and nodes
+    */
+    var importMesh = (gltfRuntime: IGLTFRuntime, node: IGLTFNode, meshes: string[], id: string, skin?: IGLTFSkins): Mesh => {
+        var newMesh = new Mesh(node.name, gltfRuntime.scene);
         newMesh.id = id;
-        newMesh.material = null;
         newMesh.layerMask = 0x0FFFFFFF;
         newMesh.subMeshes = [];
 
@@ -457,95 +550,113 @@
         var indexStarts = [];
         var indexCounts = [];
 
-        // Positions, normals and UVs
-        for (var i = 0; i < mesh.primitives.length; i++) {
-            // Temporary vertex data
-            var tempVertexData = new VertexData();
+        for (var meshIndex = 0; meshIndex < meshes.length; meshIndex++) {
+            var meshID = meshes[meshIndex];
+            var mesh: IGLTFMesh = gltfRuntime.meshes[meshID];
+            
+            if (!mesh) {
+                continue;
+            }
 
-            var primitive = mesh.primitives[i];
-            var attributes = primitive.attributes;
+            // Positions, normals and UVs
+            for (var i = 0; i < mesh.primitives.length; i++) {
+                // Temporary vertex data
+                var tempVertexData = new VertexData();
 
-            var accessor: IGLTFAccessor = null;
-            var buffer: any = null;
+                var primitive = mesh.primitives[i];
+                if (primitive.primitive !== 4) {
+                    continue;
+                }
 
-            var verticesCount = 0;
+                var attributes = primitive.attributes;
+                var accessor: IGLTFAccessor = null;
+                var buffer: any = null;
 
-            // Set positions, normal and uvs
-            for (var semantic in attributes) {
+                // Set positions, normal and uvs
+                for (var semantic in attributes) {
 
-                // Link accessor and buffer view
-                accessor = gltfRuntime.accessors[attributes[semantic]];
-                buffer = getBufferFromAccessor(gltfRuntime, accessor);
+                    // Link accessor and buffer view
+                    accessor = gltfRuntime.accessors[attributes[semantic]];
+                    buffer = getBufferFromAccessor(gltfRuntime, accessor);
 
-                if (semantic === "NORMAL") {
-                    tempVertexData.set(buffer, VertexBuffer.NormalKind);
-                }
-                else if (semantic === "POSITION") {
-                    verticesCounts.push(buffer.length);
-                    normalizeBuffer(buffer);
-                    tempVertexData.set(buffer, VertexBuffer.PositionKind);
-                }
-                else if (semantic.indexOf("TEXCOORD_") !== -1) {
-                    var channel = Number(semantic.split("_")[1]);
-                    var uvKind = VertexBuffer.UVKind + (channel === 0 ? "" : (channel + 1));
-                    normalizeUVs(buffer);
-                    tempVertexData.set(buffer, uvKind);
-                }
-                else if (semantic === "JOINT") {
-                    tempVertexData.set(buffer, VertexBuffer.MatricesIndicesKind);
-                }
-                else if (semantic === "WEIGHT") {
-                    tempVertexData.set(buffer, VertexBuffer.MatricesWeightsKind);
+                    if (semantic === "NORMAL") {
+                        tempVertexData.set(buffer, VertexBuffer.NormalKind);
+                    }
+                    else if (semantic === "POSITION") {
+                        verticesCounts.push(buffer.length);
+                        tempVertexData.set(buffer, VertexBuffer.PositionKind);
+                    }
+                    else if (semantic.indexOf("TEXCOORD_") !== -1) {
+                        var channel = Number(semantic.split("_")[1]);
+                        var uvKind = VertexBuffer.UVKind + (channel === 0 ? "" : (channel + 1));
+                        normalizeUVs(buffer);
+                        tempVertexData.set(buffer, uvKind);
+                    }
+                    else if (semantic === "JOINT") {
+                        tempVertexData.set(buffer, VertexBuffer.MatricesIndicesKind);
+                    }
+                    else if (semantic === "WEIGHT") {
+                        tempVertexData.set(buffer, VertexBuffer.MatricesWeightsKind);
+                    }
                 }
-            }
 
-            // Indices
-            accessor = gltfRuntime.accessors[primitive.indices];
-            buffer = getBufferFromAccessor(gltfRuntime, accessor, true);
-            tempVertexData.indices = buffer;
-            indexCounts.push(buffer.length);
+                // Indices
+                accessor = gltfRuntime.accessors[primitive.indices];
+                buffer = getBufferFromAccessor(gltfRuntime, accessor);
+                tempVertexData.indices = buffer;
+                indexCounts.push(buffer.length);
 
-            vertexData.merge(tempVertexData);
-            tempVertexData = undefined;
+                vertexData.merge(tempVertexData);
+                tempVertexData = undefined;
 
-            // Sub material
-            var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-            multiMat.subMaterials.push(material === null ? gltfRuntime.scene.defaultMaterial : material);
+                // Sub material
+                var material = gltfRuntime.scene.getMaterialByID(primitive.material);
+                multiMat.subMaterials.push(material === null ? gltfRuntime.scene.defaultMaterial : material);
 
-            // Update vertices start and index start
-            verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
-            indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
+                // Update vertices start and index start
+                verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
+                indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
+            }
         }
 
         // Apply geometry
         geometry.setAllVerticesData(vertexData);
         geometry.applyToMesh(newMesh);
 
+        newMesh.flipFaces(true);
+
         // Apply submeshes
         newMesh.subMeshes = [];
-        for (var i = 0; i < mesh.primitives.length; i++) {
-            var subMesh = new SubMesh(i, verticesStarts[i], verticesCounts[i], indexStarts[i], indexCounts[i], newMesh);
+        var index = 0;
+        for (var meshIndex = 0; meshIndex < meshes.length; meshIndex++) {
+            var meshID = meshes[meshIndex];
+            var mesh: IGLTFMesh = gltfRuntime.meshes[meshID];
+
+            if (!mesh) {
+                continue;
+            }
+
+            for (var i = 0; i < mesh.primitives.length; i++) {
+                if (mesh.primitives[i].primitive !== 4) {
+                    continue;
+                }
+
+                var subMesh = SubMesh.CreateFromIndices(index, indexStarts[index], indexCounts[index], newMesh, newMesh);
+                index++;
+            }
         }
 
         // Finish
-        newMesh._updateBoundingInfo();
-
         return newMesh;
     };
 
     var configureNode = (newNode: any, position: Vector3, rotation: Quaternion, scaling: Vector3) => {
         if (newNode.position) {
             newNode.position = position;
-
-            if (newNode.rotation) {
-                newNode.position.x *= -1;
-                newNode.position.y *= -1;
-                newNode.position.z *= -1;
-            }
         }
 
-        if (newNode.rotation) {
-            newNode.rotation = rotation.toEulerAngles().addInPlace(new Vector3(0, 0, Math.PI));
+        if (newNode.rotationQuaternion || newNode.rotation) {
+            newNode.rotationQuaternion = rotation;
         }
 
         if (newNode.scaling) {
@@ -558,30 +669,40 @@
             var position = new Vector3(0, 0, 0);
             var rotation = new Quaternion();
             var scaling = new Vector3(0, 0, 0);
-            Matrix.FromArray(node.matrix).decompose(scaling, rotation, position);
+            var mat = Matrix.FromArray(node.matrix);
+            mat.decompose(scaling, rotation, position);
             configureNode(newNode, position, rotation, scaling);
+
+            if (newNode instanceof TargetCamera) {
+                (<TargetCamera>newNode).setTarget(Vector3.Zero());
+            }
         }
         else {
-            configureNode(newNode, Vector3.FromArray(node.translation), Quaternion.FromArray(node.rotation), Vector3.FromArray(node.scale));
+            configureNode(newNode, Vector3.FromArray(node.translation), Quaternion.RotationAxis(Vector3.FromArray(node.rotation).normalize(), node.rotation[3]), Vector3.FromArray(node.scale));
         }
     };
 
-    var importNode = (gltfRuntime: IGLTFRuntime, node: IGLTFNode): Node => {
+    var importNode = (gltfRuntime: IGLTFRuntime, node: IGLTFNode, id: string): Node => {
         var lastNode: Node = null;
 
         // Meshes
         if (node.instanceSkin) {
             var instanceSkin = node.instanceSkin;
 
-            for (var i = 0; i < instanceSkin.meshes.length; i++) {
-                var meshID = instanceSkin.meshes[i];
+            if (instanceSkin.meshes) {
+                var skin: IGLTFSkins = gltfRuntime.skins[instanceSkin.skin];
 
-                var newMesh = importMesh(gltfRuntime, gltfRuntime.meshes[meshID], meshID);
-                newMesh.material = null;
+                var newMesh = importMesh(gltfRuntime, node, instanceSkin.meshes, id, skin);
+                newMesh.skeleton = gltfRuntime.scene.getLastSkeletonByID(instanceSkin.skin);
 
-                //newMesh.useBones = true;
-                //newMesh.computeBonesUsingShaders = true;
-                //newMesh.skeleton = gltfRuntime.scene.getLastSkeletonByID(instanceSkin.skin);
+                if (newMesh.skeleton === null) {
+                    newMesh.skeleton = importSkeleton(gltfRuntime, gltfRuntime.skins[instanceSkin.skin]);
+                }
+
+                if (newMesh.skeleton !== null) {
+                    newMesh.useBones = true;
+                    newMesh.applySkeleton(newMesh.skeleton);
+                }
 
                 lastNode = newMesh;
             }
@@ -590,12 +711,8 @@
             /**
             * Improve meshes property
             */
-            for (var i = 0; i < node.meshes.length; i++) {
-                var meshID = node.meshes[i];
-                var newMesh = importMesh(gltfRuntime, gltfRuntime.meshes[meshID], meshID);
-
-                lastNode = newMesh;
-            }
+            var newMesh = importMesh(gltfRuntime, node, node.mesh ? [node.mesh] : node.meshes, id);
+            lastNode = newMesh;
         }
         // Lights
         else if (node.light) {
@@ -678,8 +795,13 @@
                     persCamera.name = node.name;
                     persCamera.attachControl(gltfRuntime.scene.getEngine().getRenderingCanvas());
 
-                    if (perspectiveCamera.yfov && perspectiveCamera.aspectRatio && perspectiveCamera.znear && perspectiveCamera.zfar) {
-                        persCamera.getViewMatrix().copyFrom(Matrix.PerspectiveFovLH(perspectiveCamera.yfov, perspectiveCamera.aspectRatio, perspectiveCamera.znear, perspectiveCamera.zfar));
+                    if (!perspectiveCamera.aspectRatio) {
+                        perspectiveCamera.aspectRatio = gltfRuntime.scene.getEngine().getRenderWidth() / gltfRuntime.scene.getEngine().getRenderHeight();
+                    }
+
+                    if (perspectiveCamera.znear && perspectiveCamera.zfar) {
+                        persCamera.maxZ = perspectiveCamera.zfar;
+                        persCamera.minZ = perspectiveCamera.znear;
                     }
 
                     lastNode = persCamera;
@@ -689,7 +811,8 @@
 
         // Empty node
         if (lastNode === null && !node.jointName) {
-            lastNode = new Node(node.name, gltfRuntime.scene);
+            var dummy = new Mesh(node.name, gltfRuntime.scene);
+            lastNode = dummy;
         }
 
         if (lastNode !== null) {
@@ -710,22 +833,13 @@
     * Load buffers
     */
     var onBuffersLoaded = (gltfRuntime: IGLTFRuntime) => {
-        // Skins
-        /*
-        for (var skin in gltfRuntime.skins) {
-            var skins: IGLTFSkins = gltfRuntime.skins[skin];
-            var skeleton = importSkeleton(gltfRuntime, skins);
-            skeleton.id = skin;
-        }
-        */
-
         // Nodes
         var parsedNodes = gltfRuntime.nodes;
         var currentScene: IGLTFScene = <IGLTFScene>gltfRuntime.currentScene;
 
         for (var nde in parsedNodes) {
             var node: IGLTFNode = parsedNodes[nde];
-            var newNode = importNode(gltfRuntime, node);
+            var newNode = importNode(gltfRuntime, node, nde);
 
             if (newNode !== null) {
                 newNode.id = nde;
@@ -739,13 +853,11 @@
 
             if (node.children && parent !== null) {
                 for (var i = 0; i < node.children.length; i++) {
-                    var child = gltfRuntime.scene.getNodeByID(node.children[i]);
-                    if (child === null) {
-                        child = gltfRuntime.scene.getNodeByName(node.children[i]);
-                    }
+                    var child: IGLTFNode = gltfRuntime.nodes[node.children[i]];
+                    var childNode = gltfRuntime.scene.getNodeByID(node.children[i]);
 
-                    if (child !== null) {
-                        child.parent = parent;
+                    if (childNode !== null) {
+                        childNode.parent = parent;
                     }
                     else {
                         Tools.Warn("Node named " + node.name + " as a children named " + node.children[i] + " but does not exists");
@@ -845,7 +957,7 @@
                 }
 
                 if (type === EParameterType.SAMPLER_2D) {
-                    var texture = getTextureFromName(gltfRuntime, value);
+                    var texture: Texture = gltfRuntime.textures[value].babylonTexture;
 
                     if (texture === null) {
                         continue;
@@ -894,7 +1006,11 @@
                 else {
                     newTexture = new Texture(gltfRuntime.rootUrl + source.uri, gltfRuntime.scene, true);
                 }
+
                 newTexture.name = value;
+                textureHasAlpha(newTexture);
+
+                texture.babylonTexture = newTexture;
 
                 if (texture.internalFormat && (texture.internalFormat === ETextureFormat.ALPHA || texture.internalFormat === ETextureFormat.RGBA)) {
                     newTexture.hasAlpha = true;
@@ -962,6 +1078,7 @@
             var pass: IGLTFTechniquePass = technique.passes[technique.pass];
             var instanceProgram: IGLTFTechniquePassInstanceProgram = pass.instanceProgram;
             var program: IGLTFProgram = gltfRuntime.programs[instanceProgram.program];
+            var states: IGLTFTechniquePassStates = pass.states;
 
             var vertexShader: string = Effect.ShadersStore[program.vertexShader + "VertexShader"];
             var pixelShader: string = Effect.ShadersStore[program.fragmentShader + "PixelShader"];
@@ -1059,7 +1176,8 @@
             var options = {
                 attributes: attributes,
                 uniforms: uniforms,
-                samplers: samplers
+                samplers: samplers,
+                needAlphaBlending: states.functions && states.functions.blendEquationSeparate
             };
 
             Effect.ShadersStore[program.vertexShader + "VertexShader"] = newVertexShader;
@@ -1162,6 +1280,7 @@
                 shaderscount: 0,
 
                 scene: scene,
+                dummyNodes: [],
                 loadedBuffers: 0,
                 loadedShaders: 0,
                 rootUrl: rootUrl,
@@ -1238,13 +1357,24 @@
                 gltfRuntime.currentScene = parsedData.scenes[parsedData.scene];
             }
 
-            // Load buffers
+            // Load shaders and buffers
             load(gltfRuntime);
 
+            // Test on bones
+            for (var i = 0; i < scene.meshes.length; i++) {
+                var mesh = scene.meshes[i];
+
+                if (mesh.skeleton) {
+                    scene.beginAnimation(mesh.skeleton, 0, 20, true, 0.5, () => {
+                        console.log("finished");
+                    });
+                }
+            }
+
             // Finish
             return true;
         }
     };
 
     BABYLON.SceneLoader.RegisterPlugin(new GLTFFileLoader());
-}
+}

+ 18 - 4
loaders/glTF/babylon.glTFFileLoaderInterfaces.ts

@@ -53,6 +53,8 @@
         node?: string;
         value?: number|boolean|string|Array<any>;
         source?: string;
+
+        babylonValue?: any;
     }
 
     export interface IGLTFTechniquePassCommonProfile {
@@ -69,9 +71,15 @@
         uniforms: Object;
     }
 
+    export interface IGLTFTechniquePassStatesFunctions {
+        blendColor?: number[];
+        blendEquationSeparate?: number[];
+        blendFuncSeparate?: number[];
+    }
+
     export interface IGLTFTechniquePassStates {
         enable: number[];
-        functions: Object;
+        functions: IGLTFTechniquePassStatesFunctions;
     }
 
     export interface IGLTFTechniquePassDetails {
@@ -82,7 +90,7 @@
     export interface IGLTFTechniquePass {
         details: IGLTFTechniquePassDetails;
         instanceProgram: IGLTFTechniquePassInstanceProgram;
-        states: Object;
+        states: IGLTFTechniquePassStates;
     }
 
     export interface IGLTFTechnique extends IGLTFChildRootProperty {
@@ -132,6 +140,9 @@
         internalFormat?: ETextureFormat;
         target?: number;
         type?: number;
+        
+        // Babylon.js values (optimize)
+        babylonTexture?: Texture;
     }
 
     export interface IGLTFAmbienLight {
@@ -222,6 +233,7 @@
         jointName?: string;
         light?: string;
         matrix: number[];
+        mesh?: string;
         meshes?: string[];
         rotation?: number[];
         scale?: number[];
@@ -254,13 +266,15 @@
         skins: Object;
         currentScene: Object;
 
-        buffersCount: number,
-        shaderscount: number,
+        buffersCount: number;
+        shaderscount: number;
 
         scene: Scene;
         rootUrl: string;
         loadedBuffers: number;
         loadedShaders: number;
         arrayBuffers: Object;
+
+        dummyNodes: Node[];
     }
 }