Просмотр исходного кода

First iteration for AnimationGroup

David Catuhe 7 лет назад
Родитель
Сommit
b60d8ec967

Разница между файлами не показана из-за своего большого размера
+ 4154 - 4046
Playground/babylon.d.txt


Разница между файлами не показана из-за своего большого размера
+ 7854 - 7746
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 24 - 24
dist/preview release/babylon.js


+ 344 - 37
dist/preview release/babylon.max.js

@@ -21253,7 +21253,6 @@ var BABYLON;
             this.afterRender = null;
             this.afterRender = null;
             this.skeletons = [];
             this.skeletons = [];
             this.morphTargetManagers = [];
             this.morphTargetManagers = [];
-            this.animationGroups = [];
             this.importedMeshesFiles = new Array();
             this.importedMeshesFiles = new Array();
             this.stopAllAnimations();
             this.stopAllAnimations();
             this.resetCachedMaterial();
             this.resetCachedMaterial();
@@ -21324,6 +21323,10 @@ var BABYLON;
                     this.cameras[index].detachControl(canvas);
                     this.cameras[index].detachControl(canvas);
                 }
                 }
             }
             }
+            // Release animation groups
+            while (this.animationGroups.length) {
+                this.animationGroups[0].dispose();
+            }
             // Release lights
             // Release lights
             while (this.lights.length) {
             while (this.lights.length) {
                 this.lights[0].dispose();
                 this.lights[0].dispose();
@@ -41900,41 +41903,189 @@ var BABYLON;
 
 
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
+    /**
+     * This class defines the direct association between an animation and a target
+     */
+    var TargetedAnimation = /** @class */ (function () {
+        function TargetedAnimation() {
+        }
+        return TargetedAnimation;
+    }());
+    BABYLON.TargetedAnimation = TargetedAnimation;
+    /**
+     * Use this class to create coordinated animations on multiple targets
+     */
     var AnimationGroup = /** @class */ (function () {
     var AnimationGroup = /** @class */ (function () {
         function AnimationGroup(name, scene) {
         function AnimationGroup(name, scene) {
             if (scene === void 0) { scene = null; }
             if (scene === void 0) { scene = null; }
             this.name = name;
             this.name = name;
-            // private _animations = new Array<Animation>();
-            // private _targets = new Array<Object>()
-            // private _animatables: Animatable[];
-            // private _from: number;
-            // private _to: number;
+            this._targetedAnimations = new Array();
+            this._animatables = new Array();
+            this._from = Number.MAX_VALUE;
+            this._to = Number.MIN_VALUE;
             this.onAnimationEndObservable = new BABYLON.Observable();
             this.onAnimationEndObservable = new BABYLON.Observable();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
             this._scene.animationGroups.push(this);
             this._scene.animationGroups.push(this);
         }
         }
+        Object.defineProperty(AnimationGroup.prototype, "isStarted", {
+            get: function () {
+                return this._isStarted;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Add an animation with its target in the group
+         * @param target defines the target of the animation
+         * @param animation defines the animation we want to add
+         * @returns the {BABYLON.TargetedAnimation} object
+         */
+        AnimationGroup.prototype.addTargetedAnimation = function (target, animation) {
+            var targetedAnimation = {
+                animation: animation,
+                target: target
+            };
+            var keys = animation.getKeys();
+            if (this._from > keys[0].frame) {
+                this._from = keys[0].frame;
+            }
+            if (this._to < keys[keys.length - 1].frame) {
+                this._to = keys[keys.length - 1].frame;
+            }
+            this._targetedAnimations.push(targetedAnimation);
+            return targetedAnimation;
+        };
+        /**
+         * This function will normalize every animation in the group to make sure they all go from beginFrame to endFrame
+         * It can add constant keys at begin or end
+         * @param beginFrame defines the new begin frame for all animations. It can't be bigger than this._from
+         * @param endFrame defines the new end frame for all animations. It can't be smaller than this._to
+         */
         AnimationGroup.prototype.normalize = function (beginFrame, endFrame) {
         AnimationGroup.prototype.normalize = function (beginFrame, endFrame) {
+            beginFrame = Math.min(beginFrame, this._from);
+            endFrame = Math.min(endFrame, this._to);
+            for (var index = 0; index < this._targetedAnimations.length; index++) {
+                var targetedAnimation = this._targetedAnimations[index];
+                var keys = targetedAnimation.animation.getKeys();
+                var startKey = keys[0];
+                var endKey = keys[keys.length - 1];
+                if (startKey.frame > beginFrame) {
+                    var newKey = {
+                        frame: beginFrame,
+                        value: startKey.value,
+                        inTangent: startKey.inTangent,
+                        outTangent: startKey.outTangent
+                    };
+                    keys.splice(0, 0, newKey);
+                }
+                if (endKey.frame < endFrame) {
+                    var newKey = {
+                        frame: endFrame,
+                        value: endKey.value,
+                        inTangent: startKey.outTangent,
+                        outTangent: startKey.outTangent
+                    };
+                    keys.push(newKey);
+                }
+            }
             return this;
             return this;
         };
         };
+        /**
+         * Start all animations on given targets
+         * @param loop defines if animations must loop
+         * @param speedRatio defines the ratio to apply to animation speed (1 by default)
+         */
         AnimationGroup.prototype.start = function (loop, speedRatio) {
         AnimationGroup.prototype.start = function (loop, speedRatio) {
-            // for (var index = 0; index < this._animations) {
-            //     this._scene.beginDirectAnimation(this._targets[index], [this._animations[index]], this._from, this._to, loop, speedRatio, () => {
             if (loop === void 0) { loop = false; }
             if (loop === void 0) { loop = false; }
             if (speedRatio === void 0) { speedRatio = 1; }
             if (speedRatio === void 0) { speedRatio = 1; }
-            //     });
-            // }
+            if (this._isStarted || this._targetedAnimations.length === 0) {
+                return this;
+            }
+            for (var index = 0; index < this._targetedAnimations.length; index++) {
+                var targetedAnimation = this._targetedAnimations[index];
+                this._animatables.push(this._scene.beginDirectAnimation(targetedAnimation.target, [targetedAnimation.animation], this._from, this._to, loop, speedRatio, function () {
+                }));
+            }
+            this._isStarted = true;
             return this;
             return this;
         };
         };
+        /**
+         * Pause all animations
+         */
         AnimationGroup.prototype.pause = function () {
         AnimationGroup.prototype.pause = function () {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.pause();
+            }
+            return this;
+        };
+        /**
+         * Play all animations to initial state
+         * This function will start() the animations if they were not started or will restart() them if they were paused
+         */
+        AnimationGroup.prototype.play = function (loop) {
+            if (loop === void 0) { loop = false; }
+            if (this.isStarted) {
+                this.restart();
+            }
+            else {
+                this.start(loop);
+            }
+            return this;
+        };
+        /**
+         * Reset all animations to initial state
+         */
+        AnimationGroup.prototype.reset = function () {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.reset();
+            }
             return this;
             return this;
         };
         };
+        /**
+         * Restart animations from key 0
+         */
         AnimationGroup.prototype.restart = function () {
         AnimationGroup.prototype.restart = function () {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.restart();
+            }
             return this;
             return this;
         };
         };
+        /**
+         * Stop all animations
+         */
         AnimationGroup.prototype.stop = function () {
         AnimationGroup.prototype.stop = function () {
+            if (!this._isStarted) {
+                return this;
+            }
+            for (var index = 0; index < this._animatables.length; index++) {
+                var animatable = this._animatables[index];
+                animatable.stop();
+            }
+            this._isStarted = false;
             return this;
             return this;
         };
         };
+        /**
+         * Dispose all associated resources
+         */
         AnimationGroup.prototype.dispose = function () {
         AnimationGroup.prototype.dispose = function () {
+            this._targetedAnimations = [];
+            this._animatables = [];
+            var index = this._scene.animationGroups.indexOf(this);
+            if (index > -1) {
+                this._scene.animationGroups.splice(index, 1);
+            }
         };
         };
         return AnimationGroup;
         return AnimationGroup;
     }());
     }());
@@ -42381,6 +42532,10 @@ var BABYLON;
             }
             }
             this._localDelayOffset = null;
             this._localDelayOffset = null;
             this._pausedDelay = null;
             this._pausedDelay = null;
+            var oldPauseState = this._paused;
+            this._paused = false;
+            this._animate(0);
+            this._paused = oldPauseState;
         };
         };
         Animatable.prototype.enableBlending = function (blendingSpeed) {
         Animatable.prototype.enableBlending = function (blendingSpeed) {
             var runtimeAnimations = this._runtimeAnimations;
             var runtimeAnimations = this._runtimeAnimations;
@@ -68023,22 +68178,6 @@ var BABYLON;
 
 
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
-    /**
-     * The strenght of the force in correspondence to the distance of the affected object
-     */
-    var PhysicsRadialImpulseFalloff;
-    (function (PhysicsRadialImpulseFalloff) {
-        PhysicsRadialImpulseFalloff[PhysicsRadialImpulseFalloff["Constant"] = 0] = "Constant";
-        PhysicsRadialImpulseFalloff[PhysicsRadialImpulseFalloff["Linear"] = 1] = "Linear"; // impulse gets weaker if it's further from the origin
-    })(PhysicsRadialImpulseFalloff = BABYLON.PhysicsRadialImpulseFalloff || (BABYLON.PhysicsRadialImpulseFalloff = {}));
-    /**
-     * The strenght of the force in correspondence to the distance of the affected object
-     */
-    var PhysicsUpdraftMode;
-    (function (PhysicsUpdraftMode) {
-        PhysicsUpdraftMode[PhysicsUpdraftMode["Center"] = 0] = "Center";
-        PhysicsUpdraftMode[PhysicsUpdraftMode["Perpendicular"] = 1] = "Perpendicular"; // once a impostor is inside the cylinder, it will shoot out perpendicular from the ground of the cylinder
-    })(PhysicsUpdraftMode = BABYLON.PhysicsUpdraftMode || (BABYLON.PhysicsUpdraftMode = {}));
     var PhysicsHelper = /** @class */ (function () {
     var PhysicsHelper = /** @class */ (function () {
         function PhysicsHelper(scene) {
         function PhysicsHelper(scene) {
             this._scene = scene;
             this._scene = scene;
@@ -68126,8 +68265,10 @@ var BABYLON;
          * @param {number} radius the radius of the updraft
          * @param {number} radius the radius of the updraft
          * @param {number} strength the strength of the updraft
          * @param {number} strength the strength of the updraft
          * @param {number} height the height of the updraft
          * @param {number} height the height of the updraft
+         * @param {PhysicsUpdraftMode} updraftMode possible options: Center & Perpendicular. Defaults to Center
          */
          */
         PhysicsHelper.prototype.updraft = function (origin, radius, strength, height, updraftMode) {
         PhysicsHelper.prototype.updraft = function (origin, radius, strength, height, updraftMode) {
+            if (updraftMode === void 0) { updraftMode = PhysicsUpdraftMode.Center; }
             if (!this._physicsEngine) {
             if (!this._physicsEngine) {
                 BABYLON.Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
                 BABYLON.Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
                 return null;
                 return null;
@@ -68135,7 +68276,25 @@ var BABYLON;
             if (this._physicsEngine.getImpostors().length === 0) {
             if (this._physicsEngine.getImpostors().length === 0) {
                 return null;
                 return null;
             }
             }
-            var event = new PhysicsUpdraftEvent(this._physicsEngine, this._scene, origin, radius, strength, height, updraftMode);
+            var event = new PhysicsUpdraftEvent(this._scene, origin, radius, strength, height, updraftMode);
+            event.dispose(false);
+            return event;
+        };
+        /**
+         * @param {Vector3} origin the of the vortex
+         * @param {number} radius the radius of the vortex
+         * @param {number} strength the strength of the vortex
+         * @param {number} height   the height of the vortex
+         */
+        PhysicsHelper.prototype.vortex = function (origin, radius, strength, height) {
+            if (!this._physicsEngine) {
+                BABYLON.Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
+                return null;
+            }
+            if (this._physicsEngine.getImpostors().length === 0) {
+                return null;
+            }
+            var event = new PhysicsVortexEvent(this._scene, origin, radius, strength, height);
             event.dispose(false);
             event.dispose(false);
             return event;
             return event;
         };
         };
@@ -68177,6 +68336,9 @@ var BABYLON;
             if (!this._intersectsWithSphere(impostor, origin, radius)) {
             if (!this._intersectsWithSphere(impostor, origin, radius)) {
                 return null;
                 return null;
             }
             }
+            if (impostor.object.getClassName() !== 'Mesh') {
+                return null;
+            }
             var impostorObject = impostor.object;
             var impostorObject = impostor.object;
             var impostorObjectCenter = impostor.getObjectCenter();
             var impostorObjectCenter = impostor.getObjectCenter();
             var direction = impostorObjectCenter.subtract(origin);
             var direction = impostorObjectCenter.subtract(origin);
@@ -68305,19 +68467,18 @@ var BABYLON;
     BABYLON.PhysicsGravitationalFieldEvent = PhysicsGravitationalFieldEvent;
     BABYLON.PhysicsGravitationalFieldEvent = PhysicsGravitationalFieldEvent;
     /***** Updraft *****/
     /***** Updraft *****/
     var PhysicsUpdraftEvent = /** @class */ (function () {
     var PhysicsUpdraftEvent = /** @class */ (function () {
-        function PhysicsUpdraftEvent(physicsEngine, scene, origin, radius, strength, height, updraftMode) {
+        function PhysicsUpdraftEvent(_scene, _origin, _radius, _strength, _height, _updraftMode) {
+            this._scene = _scene;
+            this._origin = _origin;
+            this._radius = _radius;
+            this._strength = _strength;
+            this._height = _height;
+            this._updraftMode = _updraftMode;
             this._originTop = BABYLON.Vector3.Zero(); // the most upper part of the cylinder
             this._originTop = BABYLON.Vector3.Zero(); // the most upper part of the cylinder
             this._originDirection = BABYLON.Vector3.Zero(); // used if the updraftMode is perpendicular
             this._originDirection = BABYLON.Vector3.Zero(); // used if the updraftMode is perpendicular
             this._cylinderPosition = BABYLON.Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
             this._cylinderPosition = BABYLON.Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
             this._dataFetched = false; // check if the has been fetched the data. If not, do cleanup
             this._dataFetched = false; // check if the has been fetched the data. If not, do cleanup
-            this._physicsEngine = physicsEngine;
-            this._scene = scene;
-            this._origin = origin;
-            this._radius = radius;
-            this._strength = strength;
-            this._height = height;
-            this._updraftMode = updraftMode;
-            // TODO: for this._cylinderPosition & this._originTop, take rotation into account
+            this._physicsEngine = this._scene.getPhysicsEngine();
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
             if (this._updraftMode === PhysicsUpdraftMode.Perpendicular) {
             if (this._updraftMode === PhysicsUpdraftMode.Perpendicular) {
@@ -68327,7 +68488,7 @@ var BABYLON;
         }
         }
         /**
         /**
          * Returns the data related to the updraft event (cylinder).
          * Returns the data related to the updraft event (cylinder).
-         * @returns {PhysicsGravitationalFieldEventData}
+         * @returns {PhysicsUpdraftEventData}
          */
          */
         PhysicsUpdraftEvent.prototype.getData = function () {
         PhysicsUpdraftEvent.prototype.getData = function () {
             this._dataFetched = true;
             this._dataFetched = true;
@@ -68413,6 +68574,152 @@ var BABYLON;
         return PhysicsUpdraftEvent;
         return PhysicsUpdraftEvent;
     }());
     }());
     BABYLON.PhysicsUpdraftEvent = PhysicsUpdraftEvent;
     BABYLON.PhysicsUpdraftEvent = PhysicsUpdraftEvent;
+    /***** Vortex *****/
+    var PhysicsVortexEvent = /** @class */ (function () {
+        function PhysicsVortexEvent(_scene, _origin, _radius, _strength, _height) {
+            this._scene = _scene;
+            this._origin = _origin;
+            this._radius = _radius;
+            this._strength = _strength;
+            this._height = _height;
+            this._originTop = BABYLON.Vector3.Zero(); // the most upper part of the cylinder
+            this._centripetalForceThreshold = 0.7; // at which distance, relative to the radius the centripetal forces should kick in
+            this._updraftMultiplier = 0.02;
+            this._cylinderPosition = BABYLON.Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
+            this._dataFetched = false; // check if the has been fetched the data. If not, do cleanup
+            this._physicsEngine = this._scene.getPhysicsEngine();
+            this._origin.addToRef(new BABYLON.Vector3(0, this._height / 2, 0), this._cylinderPosition);
+            this._origin.addToRef(new BABYLON.Vector3(0, this._height, 0), this._originTop);
+            this._tickCallback = this._tick.bind(this);
+        }
+        /**
+         * Returns the data related to the vortex event (cylinder).
+         * @returns {PhysicsVortexEventData}
+         */
+        PhysicsVortexEvent.prototype.getData = function () {
+            this._dataFetched = true;
+            return {
+                cylinder: this._cylinder,
+            };
+        };
+        /**
+         * Enables the vortex.
+         */
+        PhysicsVortexEvent.prototype.enable = function () {
+            this._tickCallback.call(this);
+            this._scene.registerBeforeRender(this._tickCallback);
+        };
+        /**
+         * Disables the cortex.
+         */
+        PhysicsVortexEvent.prototype.disable = function () {
+            this._scene.unregisterBeforeRender(this._tickCallback);
+        };
+        /**
+         * Disposes the sphere.
+         * @param {bolean} force
+         */
+        PhysicsVortexEvent.prototype.dispose = function (force) {
+            var _this = this;
+            if (force === void 0) { force = true; }
+            if (force) {
+                this._cylinder.dispose();
+            }
+            else {
+                setTimeout(function () {
+                    if (!_this._dataFetched) {
+                        _this._cylinder.dispose();
+                    }
+                }, 0);
+            }
+        };
+        PhysicsVortexEvent.prototype.getImpostorForceAndContactPoint = function (impostor) {
+            if (impostor.mass === 0) {
+                return null;
+            }
+            if (!this._intersectsWithCylinder(impostor)) {
+                return null;
+            }
+            if (impostor.object.getClassName() !== 'Mesh') {
+                return null;
+            }
+            var impostorObject = impostor.object;
+            var impostorObjectCenter = impostor.getObjectCenter();
+            var originOnPlane = new BABYLON.Vector3(this._origin.x, impostorObjectCenter.y, this._origin.z); // the distance to the origin as if both objects were on a plane (Y-axis)
+            var originToImpostorDirection = impostorObjectCenter.subtract(originOnPlane);
+            var ray = new BABYLON.Ray(originOnPlane, originToImpostorDirection, this._radius);
+            var hit = ray.intersectsMesh(impostorObject);
+            var contactPoint = hit.pickedPoint;
+            if (!contactPoint) {
+                return null;
+            }
+            var absoluteDistanceFromOrigin = hit.distance / this._radius;
+            var perpendicularDirection = BABYLON.Vector3.Cross(originOnPlane, impostorObjectCenter).normalize();
+            var directionToOrigin = contactPoint.normalize();
+            if (absoluteDistanceFromOrigin > this._centripetalForceThreshold) {
+                directionToOrigin = directionToOrigin.negate();
+            }
+            // TODO: find a more physically based solution
+            if (absoluteDistanceFromOrigin > this._centripetalForceThreshold) {
+                var forceX = directionToOrigin.x * this._strength / 8;
+                var forceY = directionToOrigin.y * this._updraftMultiplier;
+                var forceZ = directionToOrigin.z * this._strength / 8;
+            }
+            else {
+                var forceX = (perpendicularDirection.x + directionToOrigin.x) / 2;
+                var forceY = this._originTop.y * this._updraftMultiplier;
+                var forceZ = (perpendicularDirection.z + directionToOrigin.z) / 2;
+            }
+            var force = new BABYLON.Vector3(forceX, forceY, forceZ);
+            force = force.multiplyByFloats(this._strength, this._strength, this._strength);
+            return { force: force, contactPoint: impostorObjectCenter };
+        };
+        PhysicsVortexEvent.prototype._tick = function () {
+            var _this = this;
+            this._physicsEngine.getImpostors().forEach(function (impostor) {
+                var impostorForceAndContactPoint = _this.getImpostorForceAndContactPoint(impostor);
+                if (!impostorForceAndContactPoint) {
+                    return;
+                }
+                impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            });
+        };
+        /*** Helpers ***/
+        PhysicsVortexEvent.prototype._prepareCylinder = function () {
+            if (!this._cylinder) {
+                this._cylinder = BABYLON.MeshBuilder.CreateCylinder("vortexEventCylinder", {
+                    height: this._height,
+                    diameter: this._radius * 2,
+                }, this._scene);
+                this._cylinder.isVisible = false;
+            }
+        };
+        PhysicsVortexEvent.prototype._intersectsWithCylinder = function (impostor) {
+            var impostorObject = impostor.object;
+            this._prepareCylinder();
+            this._cylinder.position = this._cylinderPosition;
+            return this._cylinder.intersectsMesh(impostorObject, true);
+        };
+        return PhysicsVortexEvent;
+    }());
+    BABYLON.PhysicsVortexEvent = PhysicsVortexEvent;
+    /***** Enums *****/
+    /**
+    * The strenght of the force in correspondence to the distance of the affected object
+    */
+    var PhysicsRadialImpulseFalloff;
+    (function (PhysicsRadialImpulseFalloff) {
+        PhysicsRadialImpulseFalloff[PhysicsRadialImpulseFalloff["Constant"] = 0] = "Constant";
+        PhysicsRadialImpulseFalloff[PhysicsRadialImpulseFalloff["Linear"] = 1] = "Linear"; // impulse gets weaker if it's further from the origin
+    })(PhysicsRadialImpulseFalloff = BABYLON.PhysicsRadialImpulseFalloff || (BABYLON.PhysicsRadialImpulseFalloff = {}));
+    /**
+     * The strenght of the force in correspondence to the distance of the affected object
+     */
+    var PhysicsUpdraftMode;
+    (function (PhysicsUpdraftMode) {
+        PhysicsUpdraftMode[PhysicsUpdraftMode["Center"] = 0] = "Center";
+        PhysicsUpdraftMode[PhysicsUpdraftMode["Perpendicular"] = 1] = "Perpendicular"; // once a impostor is inside the cylinder, it will shoot out perpendicular from the ground of the cylinder
+    })(PhysicsUpdraftMode = BABYLON.PhysicsUpdraftMode || (BABYLON.PhysicsUpdraftMode = {}));
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 
 //# sourceMappingURL=babylon.physicsHelper.js.map
 //# sourceMappingURL=babylon.physicsHelper.js.map

Разница между файлами не показана из-за своего большого размера
+ 24 - 24
dist/preview release/babylon.worker.js


Разница между файлами не показана из-за своего большого размера
+ 24 - 24
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 79922 - 80171
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 28 - 28
dist/preview release/viewer/babylon.viewer.js


+ 10 - 5
src/Animations/babylon.animatable.ts

@@ -67,7 +67,7 @@
             }
             }
 
 
             return null;
             return null;
-        }        
+        }
 
 
         public reset(): void {
         public reset(): void {
             var runtimeAnimations = this._runtimeAnimations;
             var runtimeAnimations = this._runtimeAnimations;
@@ -78,6 +78,11 @@
 
 
             this._localDelayOffset = null;
             this._localDelayOffset = null;
             this._pausedDelay = null;
             this._pausedDelay = null;
+
+            let oldPauseState = this._paused;
+            this._paused = false;
+            this._animate(0);
+            this._paused = oldPauseState;
         }
         }
 
 
         public enableBlending(blendingSpeed: number): void {
         public enableBlending(blendingSpeed: number): void {
@@ -128,7 +133,7 @@
         }
         }
 
 
         public stop(animationName?: string): void {
         public stop(animationName?: string): void {
-            
+
             if (animationName) {
             if (animationName) {
 
 
                 var idx = this._scene._activeAnimatables.indexOf(this);
                 var idx = this._scene._activeAnimatables.indexOf(this);
@@ -136,7 +141,7 @@
                 if (idx > -1) {
                 if (idx > -1) {
 
 
                     var runtimeAnimations = this._runtimeAnimations;
                     var runtimeAnimations = this._runtimeAnimations;
-                    
+
                     for (var index = runtimeAnimations.length - 1; index >= 0; index--) {
                     for (var index = runtimeAnimations.length - 1; index >= 0; index--) {
                         if (typeof animationName === "string" && runtimeAnimations[index].animation.name != animationName) {
                         if (typeof animationName === "string" && runtimeAnimations[index].animation.name != animationName) {
                             continue;
                             continue;
@@ -162,11 +167,11 @@
                 if (index > -1) {
                 if (index > -1) {
                     this._scene._activeAnimatables.splice(index, 1);
                     this._scene._activeAnimatables.splice(index, 1);
                     var runtimeAnimations = this._runtimeAnimations;
                     var runtimeAnimations = this._runtimeAnimations;
-                    
+
                     for (var index = 0; index < runtimeAnimations.length; index++) {
                     for (var index = 0; index < runtimeAnimations.length; index++) {
                         runtimeAnimations[index].dispose();
                         runtimeAnimations[index].dispose();
                     }
                     }
-                    
+
                     if (this.onAnimationEnd) {
                     if (this.onAnimationEnd) {
                         this.onAnimationEnd();
                         this.onAnimationEnd();
                     }
                     }

+ 176 - 9
src/Animations/babylon.animationGroup.ts

@@ -1,49 +1,216 @@
 module BABYLON {
 module BABYLON {
+    /**
+     * This class defines the direct association between an animation and a target
+     */
+    export class TargetedAnimation {
+        public animation: Animation;
+        public target: any;
+    }
+
+    /**
+     * Use this class to create coordinated animations on multiple targets
+     */
     export class AnimationGroup implements IDisposable {
     export class AnimationGroup implements IDisposable {
         private _scene: Scene;
         private _scene: Scene;
 
 
-        // private _animations = new Array<Animation>();
-        // private _targets = new Array<Object>()
-        // private _animatables: Animatable[];
-        // private _from: number;
-        // private _to: number;
+        private _targetedAnimations = new Array<TargetedAnimation>();
+        private _animatables = new Array<Animatable>();
+        private _from = Number.MAX_VALUE;
+        private _to = Number.MIN_VALUE;
+        private _isStarted: boolean;
 
 
         public onAnimationEndObservable = new Observable<Animation>();
         public onAnimationEndObservable = new Observable<Animation>();
 
 
+        public get isStarted(): boolean {
+            return this._isStarted;
+        }
+
         public constructor(public name: string, scene: Nullable<Scene> = null) {
         public constructor(public name: string, scene: Nullable<Scene> = null) {
             this._scene = scene || Engine.LastCreatedScene!;
             this._scene = scene || Engine.LastCreatedScene!;
 
 
             this._scene.animationGroups.push(this);
             this._scene.animationGroups.push(this);
         }
         }
 
 
+        /**
+         * Add an animation (with its target) in the group
+         * @param animation defines the animation we want to add
+         * @param target defines the target of the animation
+         * @returns the {BABYLON.TargetedAnimation} object
+         */
+        public addTargetedAnimation(animation: Animation, target: any): TargetedAnimation {
+            let targetedAnimation = {
+                animation: animation,
+                target: target
+            };
+
+            let keys = animation.getKeys();
+            if (this._from > keys[0].frame) {
+                this._from = keys[0].frame;
+            }
+
+            if (this._to < keys[keys.length - 1].frame) {
+                this._to = keys[keys.length - 1].frame;
+            }
+
+            this._targetedAnimations.push(targetedAnimation);
+
+            return targetedAnimation;
+        }
+
+        /**
+         * This function will normalize every animation in the group to make sure they all go from beginFrame to endFrame
+         * It can add constant keys at begin or end
+         * @param beginFrame defines the new begin frame for all animations. It can't be bigger than the smaller begin frame of all animations
+         * @param endFrame defines the new end frame for all animations. It can't be smaller than the larger end frame of all animations
+         */
         public normalize(beginFrame: number, endFrame: number): AnimationGroup {
         public normalize(beginFrame: number, endFrame: number): AnimationGroup {
+            beginFrame = Math.min(beginFrame, this._from);
+            endFrame = Math.min(endFrame, this._to);
+
+            for (var index = 0; index < this._targetedAnimations.length; index++) {
+                let targetedAnimation = this._targetedAnimations[index];
+                let keys = targetedAnimation.animation.getKeys();
+                let startKey = keys[0];
+                let endKey = keys[keys.length - 1];
+
+                if (startKey.frame > beginFrame) {
+                    let newKey = {
+                        frame: beginFrame,
+                        value: startKey.value,
+                        inTangent: startKey.inTangent,
+                        outTangent: startKey.outTangent
+                    }
+                    keys.splice(0, 0, newKey);
+                }
+
+                if (endKey.frame < endFrame) {
+                    let newKey = {
+                        frame: endFrame,
+                        value: endKey.value,
+                        inTangent: startKey.outTangent,
+                        outTangent: startKey.outTangent
+                    }
+                    keys.push(newKey);
+                }
+            }
+
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Start all animations on given targets
+         * @param loop defines if animations must loop
+         * @param speedRatio defines the ratio to apply to animation speed (1 by default)
+         */
         public start(loop = false, speedRatio = 1): AnimationGroup {
         public start(loop = false, speedRatio = 1): AnimationGroup {
-            // for (var index = 0; index < this._animations) {
-            //     this._scene.beginDirectAnimation(this._targets[index], [this._animations[index]], this._from, this._to, loop, speedRatio, () => {
+            if (this._isStarted || this._targetedAnimations.length === 0) {
+                return this;
+            }
+
+            for (var index = 0; index < this._targetedAnimations.length; index++) {
+                let targetedAnimation = this._targetedAnimations[index];
+                this._animatables.push(this._scene.beginDirectAnimation(targetedAnimation.target, [targetedAnimation.animation], this._from, this._to, loop, speedRatio, () => {
 
 
-            //     });
-            // }
+                }));
+            }
+
+            this._isStarted = true;
 
 
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Pause all animations
+         */
         public pause(): AnimationGroup {
         public pause(): AnimationGroup {
+            if (!this._isStarted) {
+                return this;
+            }
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.pause();
+            }
+
+            return this;
+        }
+
+        /**
+         * Play all animations to initial state
+         * This function will start() the animations if they were not started or will restart() them if they were paused
+         */
+        public play(loop = false): AnimationGroup {
+            if (this.isStarted) {
+                this.restart();
+            } else {
+                this.start(loop);
+            }
+
+            return this;
+        }
+
+        /**
+         * Reset all animations to initial state
+         */
+        public reset(): AnimationGroup {
+            if (!this._isStarted) {
+                return this;
+            }
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.reset();
+            }
+
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Restart animations from key 0
+         */
         public restart(): AnimationGroup {
         public restart(): AnimationGroup {
+            if (!this._isStarted) {
+                return this;
+            }
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.restart();
+            }
+
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Stop all animations
+         */
         public stop(): AnimationGroup {
         public stop(): AnimationGroup {
+            if (!this._isStarted) {
+                return this;
+            }
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.stop();
+            }
+
+            this._isStarted = false;
+
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Dispose all associated resources
+         */
         public dispose(): void {
         public dispose(): void {
+            this._targetedAnimations = [];
+            this._animatables = [];
+
+            var index = this._scene.animationGroups.indexOf(this);
 
 
+            if (index > -1) {
+                this._scene.animationGroups.splice(index, 1);
+            }
         }
         }
     }
     }
 }
 }

+ 5 - 1
src/babylon.scene.ts

@@ -3777,7 +3777,6 @@
 
 
             this.skeletons = [];
             this.skeletons = [];
             this.morphTargetManagers = [];
             this.morphTargetManagers = [];
-            this.animationGroups = [];
 
 
             this.importedMeshesFiles = new Array<string>();
             this.importedMeshesFiles = new Array<string>();
 
 
@@ -3864,6 +3863,11 @@
                 }
                 }
             }
             }
 
 
+            // Release animation groups
+            while (this.animationGroups.length) {
+                this.animationGroups[0].dispose();
+            }
+
             // Release lights
             // Release lights
             while (this.lights.length) {
             while (this.lights.length) {
                 this.lights[0].dispose();
                 this.lights[0].dispose();