Pārlūkot izejas kodu

Merge pull request #959 from julien-moreau/master

Scene and Particle Systems: support of animations (serialize and parse)
David Catuhe 9 gadi atpakaļ
vecāks
revīzija
418895b551

+ 57 - 9
loaders/glTF/babylon.glTFFileLoader.js

@@ -335,6 +335,7 @@ var BABYLON;
     var loadAnimations = function (gltfRuntime) {
         for (var anim in gltfRuntime.animations) {
             var animation = gltfRuntime.animations[anim];
+            var lastAnimation = null;
             for (var i = 0; i < animation.channels.length; i++) {
                 // Get parameters and load buffers
                 var channel = animation.channels[i];
@@ -371,9 +372,17 @@ var BABYLON;
                     }
                 }
                 // Create animation and key frames
-                var babylonAnimation = new BABYLON.Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
+                var babylonAnimation = null;
                 var keys = [];
                 var arrayOffset = 0;
+                var modifyKey = false;
+                if (isBone && lastAnimation && lastAnimation.getKeys().length === bufferInput.length) {
+                    babylonAnimation = lastAnimation;
+                    modifyKey = true;
+                }
+                if (!modifyKey) {
+                    babylonAnimation = new BABYLON.Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
+                }
                 // For each frame
                 for (var j = 0; j < bufferInput.length; j++) {
                     var value = null;
@@ -392,26 +401,42 @@ var BABYLON;
                         var scaling = BABYLON.Vector3.Zero();
                         // Warning on decompose
                         var mat = bone.getBaseMatrix();
+                        if (modifyKey) {
+                            mat = lastAnimation.getKeys()[j].value;
+                        }
                         mat.decompose(scaling, rotationQuaternion, translation);
                         if (targetPath === "position") {
                             translation = value;
                         }
                         else if (targetPath === "rotationQuaternion") {
                             rotationQuaternion = value;
+                            // Y is Up
+                            if (GLTFFileLoader.MakeYUP) {
+                                rotationQuaternion = rotationQuaternion.multiply(new BABYLON.Quaternion(-0.707107, 0, 0, 0.707107));
+                            }
                         }
                         else {
                             scaling = value;
                         }
                         value = BABYLON.Matrix.Compose(scaling, rotationQuaternion, translation);
                     }
-                    keys.push({
-                        frame: bufferInput[j],
-                        value: value
-                    });
+                    if (!modifyKey) {
+                        keys.push({
+                            frame: bufferInput[j],
+                            value: value
+                        });
+                    }
+                    else {
+                        lastAnimation.getKeys()[j].value = value;
+                    }
                 }
                 // Finish
-                babylonAnimation.setKeys(keys);
-                targetNode.animations.push(babylonAnimation);
+                if (!modifyKey) {
+                    babylonAnimation.setKeys(keys);
+                    targetNode.animations.push(babylonAnimation);
+                }
+                lastAnimation = babylonAnimation;
+                gltfRuntime.scene.stopAnimation(targetNode);
                 gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true, 1.0);
             }
         }
@@ -425,6 +450,10 @@ var BABYLON;
             var scale = BABYLON.Vector3.FromArray(node.scale);
             var rotation = BABYLON.Quaternion.FromArray(node.rotation);
             var position = BABYLON.Vector3.FromArray(node.translation);
+            // Y is Up
+            if (GLTFFileLoader.MakeYUP) {
+                rotation = rotation.multiply(new BABYLON.Quaternion(-0.707107, 0, 0, 0.707107));
+            }
             mat = BABYLON.Matrix.Compose(scale, rotation, position);
         }
         else {
@@ -550,6 +579,12 @@ var BABYLON;
             }
         }
     };
+    var printMat = function (m) {
+        console.log(m[0] + "\t" + m[1] + "\t" + m[2] + "\t" + m[3] + "\n" +
+            m[4] + "\t" + m[5] + "\t" + m[6] + "\t" + m[7] + "\n" +
+            m[8] + "\t" + m[9] + "\t" + m[10] + "\t" + m[11] + "\n" +
+            m[12] + "\t" + m[13] + "\t" + m[14] + "\t" + m[15] + "\n");
+    };
     /**
     * Imports a skeleton
     */
@@ -564,12 +599,14 @@ var BABYLON;
         var accessor = gltfRuntime.accessors[skins.inverseBindMatrices];
         var buffer = getBufferFromAccessor(gltfRuntime, accessor);
         var bindShapeMatrix = BABYLON.Matrix.FromArray(skins.bindShapeMatrix);
-        newSkeleton._identity = bindShapeMatrix;
         // Find the root bones
         var nodesToRoot = [];
         var nodesToRootToAdd = [];
         getNodesToRoot(gltfRuntime, newSkeleton, skins, nodesToRoot);
         newSkeleton.bones = [];
+        if (nodesToRoot.length === 0) {
+            newSkeleton.needInitialSkinMatrix = true;
+        }
         // Joints
         for (var i = 0; i < skins.jointNames.length; i++) {
             var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
@@ -579,6 +616,12 @@ var BABYLON;
                 continue;
             }
             var id = jointNode.id;
+            // Optimize, if the bone already exists...
+            var existingBone = gltfRuntime.scene.getBoneByID(id);
+            if (existingBone) {
+                newSkeleton.bones.push(existingBone);
+                continue;
+            }
             // Check if node already exists
             var foundBone = false;
             for (var j = 0; j < newSkeleton.bones.length; j++) {
@@ -613,7 +656,7 @@ var BABYLON;
             }
             // Create bone
             var mat = configureBoneTransformation(node);
-            if (!parentBone) {
+            if (!parentBone && nodesToRoot.length > 0) {
                 parentBone = getNodeToRoot(nodesToRoot, id);
                 if (parentBone) {
                     if (nodesToRootToAdd.indexOf(parentBone) === -1) {
@@ -621,6 +664,11 @@ var BABYLON;
                     }
                 }
             }
+            if (!parentBone && nodesToRoot.length === 0) {
+                var inverseBindMatrix = BABYLON.Matrix.FromArray(buffer, i * 16);
+                var invertMesh = BABYLON.Matrix.Invert(mesh.getWorldMatrix());
+                mat = mat.multiply(mesh.getWorldMatrix());
+            }
             var bone = new BABYLON.Bone(node.name, newSkeleton, parentBone, mat);
             bone.id = id;
         }

+ 69 - 9
loaders/glTF/babylon.glTFFileLoader.ts

@@ -355,6 +355,7 @@
     var loadAnimations = (gltfRuntime: IGLTFRuntime) => {
         for (var anim in gltfRuntime.animations) {
             var animation: IGLTFAnimation = gltfRuntime.animations[anim];
+            var lastAnimation: Animation = null;
 
             for (var i = 0; i < animation.channels.length; i++) {
                 // Get parameters and load buffers
@@ -403,9 +404,19 @@
                 }
 
                 // Create animation and key frames
-                var babylonAnimation = new Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, Animation.ANIMATIONLOOPMODE_CYCLE);
+                var babylonAnimation: Animation = null;
                 var keys = [];
                 var arrayOffset = 0;
+                var modifyKey = false;
+
+                if (isBone && lastAnimation && lastAnimation.getKeys().length === bufferInput.length) {
+                    babylonAnimation = lastAnimation;
+                    modifyKey = true;
+                }
+                
+                if (!modifyKey) {
+                    babylonAnimation = new Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, Animation.ANIMATIONLOOPMODE_CYCLE);
+                }
 
                 // For each frame
                 for (var j = 0; j < bufferInput.length; j++) {
@@ -428,6 +439,11 @@
 
                         // Warning on decompose
                         var mat = bone.getBaseMatrix();
+
+                        if (modifyKey) {
+                            mat = lastAnimation.getKeys()[j].value;
+                        }
+
                         mat.decompose(scaling, rotationQuaternion, translation);
 
                         if (targetPath === "position") {
@@ -435,6 +451,10 @@
                         }
                         else if (targetPath === "rotationQuaternion") {
                             rotationQuaternion = value;
+                            // Y is Up
+                            if (GLTFFileLoader.MakeYUP) {
+                                rotationQuaternion = rotationQuaternion.multiply(new Quaternion(-0.707107, 0, 0, 0.707107));
+                            }
                         }
                         else {
                             scaling = value;
@@ -443,16 +463,26 @@
                         value = Matrix.Compose(scaling, rotationQuaternion, translation);
                     }
 
-                    keys.push({
-                        frame: bufferInput[j],
-                        value: value
-                    });
+                    if (!modifyKey) {
+                        keys.push({
+                            frame: bufferInput[j],
+                            value: value
+                        });
+                    }
+                    else {
+                        lastAnimation.getKeys()[j].value = value;
+                    }
                 }
                 
                 // Finish
-                babylonAnimation.setKeys(keys);
-                targetNode.animations.push(babylonAnimation);
+                if (!modifyKey) {
+                    babylonAnimation.setKeys(keys);
+                    targetNode.animations.push(babylonAnimation);
+                }
+
+                lastAnimation = babylonAnimation;
 
+                gltfRuntime.scene.stopAnimation(targetNode);
                 gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true, 1.0);
             }
         }
@@ -468,6 +498,11 @@
             var rotation = Quaternion.FromArray(node.rotation);
             var position = Vector3.FromArray(node.translation);
 
+            // Y is Up
+            if (GLTFFileLoader.MakeYUP) {
+                rotation = rotation.multiply(new Quaternion(-0.707107, 0, 0, 0.707107));
+            }
+
             mat = Matrix.Compose(scale, rotation, position);
         }
         else {
@@ -616,6 +651,15 @@
         }
     };
 
+    var printMat = (m: Float32Array) => {
+        console.log(
+            m[0] + "\t" + m[1] + "\t" + m[2] + "\t" + m[3] + "\n" +
+            m[4] + "\t" + m[5] + "\t" + m[6] + "\t" + m[7] + "\n" +
+            m[8] + "\t" + m[9] + "\t" + m[10] + "\t" + m[11] + "\n" +
+            m[12] + "\t" + m[13] + "\t" + m[14] + "\t" + m[15] + "\n"
+        );
+    }
+
     /**
     * Imports a skeleton
     */
@@ -634,7 +678,6 @@
         var buffer = getBufferFromAccessor(gltfRuntime, accessor);
 
         var bindShapeMatrix = Matrix.FromArray(skins.bindShapeMatrix);
-        (<any>newSkeleton)._identity = bindShapeMatrix;
 
         // Find the root bones
         var nodesToRoot: INodeToRoot[] = [];
@@ -643,6 +686,10 @@
         getNodesToRoot(gltfRuntime, newSkeleton, skins, nodesToRoot);
         newSkeleton.bones = [];
 
+        if (nodesToRoot.length === 0) {
+            newSkeleton.needInitialSkinMatrix = true;
+        }
+
         // Joints
         for (var i = 0; i < skins.jointNames.length; i++) {
             var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
@@ -655,6 +702,13 @@
             
             var id = jointNode.id;
 
+            // Optimize, if the bone already exists...
+            var existingBone = gltfRuntime.scene.getBoneByID(id);
+            if (existingBone) {
+                newSkeleton.bones.push(existingBone);
+                continue;
+            }
+
             // Check if node already exists
             var foundBone = false;
             for (var j = 0; j < newSkeleton.bones.length; j++) {
@@ -697,7 +751,7 @@
             // Create bone
             var mat = configureBoneTransformation(node);
 
-            if (!parentBone) {
+            if (!parentBone && nodesToRoot.length > 0) {
                 parentBone = getNodeToRoot(nodesToRoot, id);
 
                 if (parentBone) {
@@ -707,6 +761,12 @@
                 }
             }
 
+            if (!parentBone && nodesToRoot.length === 0) {
+                var inverseBindMatrix = Matrix.FromArray(buffer, i * 16);
+                var invertMesh = Matrix.Invert(mesh.getWorldMatrix());
+                mat = mat.multiply(mesh.getWorldMatrix());
+            }
+
             var bone = new Bone(node.name, newSkeleton, parentBone, mat);
             bone.id = id;
         }

+ 7 - 0
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -207,6 +207,13 @@ var BABYLON;
                     var parsedLight = parsedData.lights[index];
                     BABYLON.Light.Parse(parsedLight, scene);
                 }
+                // Animations
+                if (parsedData.animations) {
+                    for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
+                        var parsedAnimation = parsedData.animations[index];
+                        scene.animations.push(BABYLON.Animation.Parse(parsedAnimation));
+                    }
+                }
                 // Materials
                 if (parsedData.materials) {
                     for (index = 0, cache = parsedData.materials.length; index < cache; index++) {

+ 8 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -222,6 +222,14 @@
                 Light.Parse(parsedLight, scene);
             }
 
+            // Animations
+            if (parsedData.animations) {
+                for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
+                    var parsedAnimation = parsedData.animations[index];
+                    scene.animations.push(Animation.Parse(parsedAnimation));
+                }
+            }
+
             // Materials
             if (parsedData.materials) {
                 for (index = 0, cache = parsedData.materials.length; index < cache; index++) {

+ 21 - 0
src/Particles/babylon.particleSystem.js

@@ -11,6 +11,8 @@ var BABYLON;
         function ParticleSystem(name, capacity, scene, customEffect) {
             var _this = this;
             this.name = name;
+            // Members
+            this.animations = [];
             this.renderingGroupId = 0;
             this.emitter = null;
             this.emitRate = 10;
@@ -332,6 +334,7 @@ var BABYLON;
         ParticleSystem.prototype.serialize = function () {
             var serializationObject = {};
             serializationObject.name = this.name;
+            // Emitter
             if (this.emitter.position) {
                 serializationObject.emitterId = this.emitter.id;
             }
@@ -343,6 +346,14 @@ var BABYLON;
             if (this.particleTexture) {
                 serializationObject.textureName = this.particleTexture.name;
             }
+            // Animations
+            serializationObject.animations = [];
+            var animation;
+            for (var index = 0; index < this.animations.length; index++) {
+                animation = this.animations[index];
+                serializationObject.animations.push(animation.serialize());
+            }
+            // Particle system
             serializationObject.minAngularSpeed = this.minAngularSpeed;
             serializationObject.maxAngularSpeed = this.maxAngularSpeed;
             serializationObject.minSize = this.minSize;
@@ -369,16 +380,26 @@ var BABYLON;
         ParticleSystem.Parse = function (parsedParticleSystem, scene, rootUrl) {
             var name = parsedParticleSystem.name;
             var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, scene);
+            // Texture
             if (parsedParticleSystem.textureName) {
                 particleSystem.particleTexture = new BABYLON.Texture(rootUrl + parsedParticleSystem.textureName, scene);
                 particleSystem.particleTexture.name = parsedParticleSystem.textureName;
             }
+            // Emitter
             if (parsedParticleSystem.emitterId) {
                 particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
             }
             else {
                 particleSystem.emitter = BABYLON.Vector3.FromArray(parsedParticleSystem.emitter);
             }
+            // Animations
+            if (parsedParticleSystem.animations) {
+                for (var animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
+                    var parsedAnimation = parsedParticleSystem.animations[animationIndex];
+                    particleSystem.animations.push(BABYLON.Animation.Parse(parsedAnimation));
+                }
+            }
+            // Particle system
             particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
             particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
             particleSystem.minSize = parsedParticleSystem.minSize;

+ 29 - 1
src/Particles/babylon.particleSystem.ts

@@ -9,12 +9,14 @@
         return ((random * (max - min)) + min);
     }
 
-    export class ParticleSystem implements IDisposable {
+    export class ParticleSystem implements IDisposable, IAnimatable {
         // Statics
         public static BLENDMODE_ONEONE = 0;
         public static BLENDMODE_STANDARD = 1;
 
         // Members
+        public animations: Animation[] = [];
+
         public id: string;
         public renderingGroupId = 0;
         public emitter = null;
@@ -449,17 +451,29 @@
             var serializationObject: any = {};
 
             serializationObject.name = this.name;
+
+            // Emitter
             if (this.emitter.position) {
                 serializationObject.emitterId = this.emitter.id;
             } else {
                 serializationObject.emitter = this.emitter.asArray();;
             }
+
             serializationObject.capacity = this.getCapacity();
 
             if (this.particleTexture) {
                 serializationObject.textureName = this.particleTexture.name;
             }
+            
+            // Animations
+            serializationObject.animations = [];
+            var animation: Animation;
+            for (var index = 0; index < this.animations.length; index++) {
+                animation = this.animations[index];
+                serializationObject.animations.push(animation.serialize());
+            }
 
+            // Particle system
             serializationObject.minAngularSpeed = this.minAngularSpeed;
             serializationObject.maxAngularSpeed = this.maxAngularSpeed;
             serializationObject.minSize = this.minSize;
@@ -488,15 +502,29 @@
         public static Parse(parsedParticleSystem: any, scene: Scene, rootUrl: string): ParticleSystem {
             var name = parsedParticleSystem.name;
             var particleSystem = new ParticleSystem(name, parsedParticleSystem.capacity, scene);
+
+            // Texture
             if (parsedParticleSystem.textureName) {
                 particleSystem.particleTexture = new Texture(rootUrl + parsedParticleSystem.textureName, scene);
                 particleSystem.particleTexture.name = parsedParticleSystem.textureName;
             }
+
+            // Emitter
             if (parsedParticleSystem.emitterId) {
                 particleSystem.emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
             } else {
                 particleSystem.emitter = Vector3.FromArray(parsedParticleSystem.emitter);
             }
+
+            // Animations
+            if (parsedParticleSystem.animations) {
+                for (var animationIndex = 0; animationIndex < parsedParticleSystem.animations.length; animationIndex++) {
+                    var parsedAnimation = parsedParticleSystem.animations[animationIndex];
+                    particleSystem.animations.push(Animation.Parse(parsedAnimation));
+                }
+            }
+
+            // Particle system
             particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
             particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
             particleSystem.minSize = parsedParticleSystem.minSize;

+ 10 - 0
src/Tools/babylon.sceneSerializer.js

@@ -183,6 +183,9 @@ var BABYLON;
     var SceneSerializer = (function () {
         function SceneSerializer() {
         }
+        SceneSerializer.ClearCache = function () {
+            serializedGeometries = [];
+        };
         SceneSerializer.Serialize = function (scene) {
             var serializationObject = {};
             // Scene
@@ -224,6 +227,13 @@ var BABYLON;
             if (scene.activeCamera) {
                 serializationObject.activeCameraID = scene.activeCamera.id;
             }
+            // Animations
+            serializationObject.animations = [];
+            var animation;
+            for (index = 0; index < scene.animations.length; index++) {
+                animation = scene.animations[index];
+                serializationObject.animations.push(animation.serialize());
+            }
             // Materials
             serializationObject.materials = [];
             serializationObject.multiMaterials = [];

+ 12 - 0
src/Tools/babylon.sceneSerializer.ts

@@ -207,6 +207,10 @@
     }
 
     export class SceneSerializer {
+        public static ClearCache(): void {
+            serializedGeometries = [];
+        }
+
         public static Serialize(scene: Scene): any {
             var serializationObject: any = {};
 
@@ -255,6 +259,14 @@
                 serializationObject.activeCameraID = scene.activeCamera.id;
             }
 
+            // Animations
+            serializationObject.animations = [];
+            var animation: Animation;
+            for (index = 0; index < scene.animations.length; index++) {
+                animation = scene.animations[index];
+                serializationObject.animations.push(animation.serialize());
+            }
+
             // Materials
             serializationObject.materials = [];
             serializationObject.multiMaterials = [];

+ 2 - 0
src/babylon.scene.js

@@ -19,6 +19,8 @@ var BABYLON;
             this.forceShowBoundingBoxes = false;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
+            // Animations
+            this.animations = [];
             this.cameraToUseForPointers = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
             this._startingPointerPosition = new BABYLON.Vector2(0, 0);
             this._startingPointerTime = 0;

+ 4 - 1
src/babylon.scene.ts

@@ -7,7 +7,7 @@
      * Represents a scene to be rendered by the engine.
      * @see http://doc.babylonjs.com/page.php?p=21911
      */
-    export class Scene {
+    export class Scene implements IAnimatable {
         // Statics
         private static _FOGMODE_NONE = 0;
         private static _FOGMODE_EXP = 1;
@@ -61,6 +61,9 @@
         public animationsEnabled = true;
         public constantlyUpdateMeshUnderPointer = false;
 
+        // Animations
+        public animations: Animation[] = [];
+
         // Pointers
         private _onPointerMove: (evt: PointerEvent) => void;
         private _onPointerDown: (evt: PointerEvent) => void;