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

First support for behaviors: BouncingBehavior for arcRotateCamera

David Catuhe 8 лет назад
Родитель
Сommit
8a39b9e725

+ 13 - 3
Tools/Gulp/config.json

@@ -22,7 +22,7 @@
                 "bones", "hdr", "polygonMesh", "csg", "lensFlares", "physics", "textureFormats", "debug", "morphTargets",
                 "colorCurves", "octrees", "simd", "vr", "virtualJoystick", "optimizations", "highlights", "assetsManager",
                 "mapTexture", "dynamicFloatArray",
-                "imageProcessing", "serialization", "probes", "layer", "textureTools"
+                "imageProcessing", "serialization", "probes", "layer", "textureTools", "cameraBehaviors"
         ],
         "minimal": ["standardMaterial", "freeCamera", "hemisphericLight"],
         "minimalWithBuilder": ["meshBuilder", "standardMaterial", "freeCamera", "hemisphericLight"],
@@ -36,7 +36,7 @@
                 "debug", "textureTools", "hdr",
                 "loader",
                 "materialsLibrary/babylon.gridMaterial.js",
-                "loaders/babylon.glTFFileLoader.js"
+                "loaders/babylon.glTFFileLoader.js", "cameraBehaviors", "morphTargets"
         ],
         "distributed": ["minimalGLTFViewer"]
     },
@@ -96,7 +96,17 @@
                 "particles.vertex",
                 "particles.fragment"
             ]
-        },         
+        },       
+        "cameraBehaviors" : 
+        {
+            "files": [
+                "../../src/Behaviors/Cameras/babylon.framingBehavior.js",
+                "../../src/Behaviors/Cameras/babylon.bouncingBehavior.js"
+            ],
+            "dependUpon" : [
+                "core"
+            ]
+        },                   
         "textureTools" : 
         {
             "files": [

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


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


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


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


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


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


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


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


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


+ 1 - 0
dist/preview release/what's new.md

@@ -3,6 +3,7 @@
 ## Major updates
 - Added support for non-pow2 textures when in WebGL2 mode ([deltakosh](https://github.com/deltakosh))
 - Engine can now be initialized with an existing webgl context ([deltakosh](https://github.com/deltakosh))
+- Introducing behaviors. (Doc here)[] ([deltakosh](https://github.com/deltakosh))
 
 ## Updates
 - POW2 textures rescale is now done by shaders (It was done using canvas before) ([deltakosh](https://github.com/deltakosh))

+ 62 - 0
src/Animations/babylon.animation.ts

@@ -144,6 +144,25 @@
             return animation;
         }
 
+        /**
+		 * Sets up an animation.
+		 * @param property the property to animate
+		 * @param animationType the animation type to apply
+		 * @param easingFunction the easing function used in the animation
+		 * @returns The created animation
+		 */
+		public static CreateAnimation(property: string, animationType: number, framePerSecond: number, easingFunction: BABYLON.EasingFunction): BABYLON.Animation {
+			var animation: BABYLON.Animation = new BABYLON.Animation(property + "Animation",
+				property,
+				framePerSecond,
+				animationType,
+				BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
+
+			animation.setEasingFunction(easingFunction);
+
+			return animation;
+		}
+
         public static CreateAndStartAnimation(name: string, node: Node, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void) {
@@ -164,6 +183,49 @@
             return node.getScene().beginAnimation(node, 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
         }
 
+        /**
+		 * Transition property of the Camera to the target Value.
+		 * @param property The property to transition
+		 * @param targetValue The target Value of the property
+         * @param host The object where the property to animate belongs
+         * @param scene Scene used to run the animation
+         * @param frameRate Framerate (in frame/s) to use
+		 * @param transition The transition type we want to use
+		 * @param duration The duration of the animation, in milliseconds
+		 * @param onAnimationEnd Call back trigger at the end of the animation.
+		 */
+		public static TransitionTo(property: string, targetValue: any, host: any, scene: Scene, frameRate: number, transition: Animation, duration: number,	onAnimationEnd: () => void = null): Animatable {
+
+			if (duration <= 0) {
+				host[property] = targetValue;
+				if (onAnimationEnd) {
+                    onAnimationEnd();
+                }
+				return;
+			}
+
+			var endFrame: number = frameRate * (duration / 1000);
+
+			transition.setKeys([{
+				frame: 0,
+				value: host[property]
+			},
+			{
+				frame: endFrame,
+				value: targetValue
+			}]);
+
+			if (!host.animations) {
+				host.animations = [];
+			}
+
+			host.animations.push(transition);
+
+			var animation: BABYLON.Animatable = scene.beginAnimation(host, 0, endFrame, false);
+			animation.onAnimationEnd = onAnimationEnd;
+			return animation;
+		}
+
         constructor(public name: string, public targetProperty: string, public framePerSecond: number, public dataType: number, public loopMode?: number, public enableBlending?: boolean) {
             this.targetPropertyPath = targetProperty.split(".");
             this.dataType = dataType;

+ 117 - 0
src/Behaviors/Cameras/babylon.bouncingBehavior.ts

@@ -0,0 +1,117 @@
+module BABYLON {
+    /**
+     * Add a bouncing effect to an ArcRotateCamera when reaching a specified minimum and maximum radius
+     */
+    export class BouncingBehavior implements Behavior<ArcRotateCamera> {
+        public get name(): string {
+            return "Bouncing";
+        }        
+
+		/**
+		 * The easing function to use when the camera bounces
+		 */
+		public bounceEasingFunction = new BackEase(0.3);
+
+		/**
+		 * The easing mode to use when the camera bounces
+		 */
+        public bounceEasingMode = EasingFunction.EASINGMODE_EASEOUT;   
+        
+        /**
+         * The duration of the animation, in milliseconds
+         */
+        public transitionDuration = 450;
+
+        /**
+         * Length of the distance animated by the transition when lower radius is reached
+         */
+        public lowerRadiusTransitionRange = 2;     
+        
+        /**
+         * Length of the distance animated by the transition when upper radius is reached
+         */
+        public upperRadiusTransitionRange = -2;          
+
+        
+        // Connection
+        private _attachedCamera: ArcRotateCamera;
+        private _onAfterCheckInputsObservabler: Observer<Camera>;
+        public attach(camera: ArcRotateCamera): void {
+            this._attachedCamera = camera;
+            this._onAfterCheckInputsObservabler = camera.onAfterCheckInputsObservable.add(() => {
+				// Add the bounce animation to the lower radius limit
+				if (this._isRadiusAtLimit(this._attachedCamera.lowerRadiusLimit)) {
+					this._applyBoundRadiusAnimation(this.lowerRadiusTransitionRange);
+				}
+
+				// Add the bounce animation to the upper radius limit
+				if (this._isRadiusAtLimit(this._attachedCamera.upperRadiusLimit)) {
+					this._applyBoundRadiusAnimation(this.upperRadiusTransitionRange);
+				}
+            });
+        }
+        
+        public detach(camera: ArcRotateCamera): void {
+            camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObservabler);
+        }
+
+        // Animations
+        private _radiusIsAnimating: boolean = false;
+        private _radiusBounceTransition: Animation = null;
+        private _animatables = new Array<BABYLON.Animatable>();
+        private _cachedWheelPrecision: number;
+
+        /**
+		 * Checks if the camera radius is at the specified limit. Takes into account animation locks.
+		 * @param radiusLimit The limit to check against.
+		 * @return Bool to indicate if at limit.
+		 */
+		private _isRadiusAtLimit(radiusLimit: number): boolean {
+			if (this._attachedCamera.radius === radiusLimit && !this._radiusIsAnimating) {
+				return true;
+			}
+			return false;
+        }     
+        
+        /**
+		 * Applies an animation to the radius of the camera, extending by the radiusDelta.
+		 * @param radiusDelta The delta by which to animate to. Can be negative.
+		 */
+		private _applyBoundRadiusAnimation(radiusDelta: number): void {
+			if (!this._radiusBounceTransition) {
+				this.bounceEasingFunction.setEasingMode(this.bounceEasingMode);
+				this._radiusBounceTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, this.bounceEasingFunction);
+			}
+            // Prevent zoom until bounce has completed
+            this._cachedWheelPrecision = this._attachedCamera.wheelPrecision;
+			this._attachedCamera.wheelPrecision = Infinity;
+			this._attachedCamera.inertialRadiusOffset = 0;
+
+			// Animate to the radius limit
+			this.stopAllAnimations();
+			this._radiusIsAnimating = true;
+            this._animatables.push(Animation.TransitionTo("radius", this._attachedCamera.radius + radiusDelta, this._attachedCamera, this._attachedCamera.getScene(), 60, 
+                                    this._radiusBounceTransition, this.transitionDuration, () => this._clearAnimationLocks()));
+        }
+
+        /**
+		 * Removes all animation locks. Allows new animations to be added to any of the camera properties.
+		 */
+		protected _clearAnimationLocks(): void {
+			this._radiusIsAnimating = false;
+			this._attachedCamera.wheelPrecision = this._cachedWheelPrecision;
+		}        
+        
+		/**
+		 * Stops and removes all animations that have been applied to the camera
+		 */        
+        public stopAllAnimations(): void {
+			this._attachedCamera.animations = [];
+			while (this._animatables.length) {
+				this._animatables[0].onAnimationEnd = null;
+				this._animatables[0].stop();
+				this._animatables.shift();
+			}
+		}
+    }
+}

+ 15 - 0
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -0,0 +1,15 @@
+module BABYLON {
+    export class FramingBehavior implements Behavior<ArcRotateCamera> {
+        public get name(): string {
+            return "Framing";
+        }
+        
+        public attach(camera: ArcRotateCamera): void {
+
+        }
+        
+        public detach(camera: ArcRotateCamera): void {
+            
+        }
+    }
+}

+ 8 - 0
src/Behaviors/babylon.behavior.ts

@@ -0,0 +1,8 @@
+module BABYLON {
+    export interface Behavior<T extends Node> {
+        name: string;
+
+        attach(node: T): void;
+        detach(node: T): void;
+    }
+}

+ 20 - 0
src/Cameras/babylon.arcRotateCamera.ts

@@ -194,6 +194,26 @@ module BABYLON {
         protected _localDirection: Vector3;
         protected _transformedDirection: Vector3;
 
+        // Behaviors
+        private _bouncingBehavior: BouncingBehavior;
+        public get useBouncingBehavior(): boolean {
+            return this._bouncingBehavior != null;
+        }
+
+        public set useBouncingBehavior(value: boolean) {
+            if (value === this.useBouncingBehavior) {
+                return;
+            }
+
+            if (value) {
+                this._bouncingBehavior = new BouncingBehavior();
+                this.addBehavior(this._bouncingBehavior);
+            } else {
+                this.removeBehavior(this._bouncingBehavior);
+                this._bouncingBehavior = null;
+            }
+        }
+
         // Collisions
         public onCollide: (collidedMesh: AbstractMesh) => void;
         public checkCollisions = false;

+ 3 - 0
src/Cameras/babylon.camera.ts

@@ -126,6 +126,7 @@
         // Observables
         public onViewMatrixChangedObservable = new Observable<Camera>();
         public onProjectionMatrixChangedObservable = new Observable<Camera>();
+        public onAfterCheckInputsObservable = new Observable<Camera>();
 
         // Cache
         private _computedViewMatrix = Matrix.Identity();
@@ -296,6 +297,7 @@
         }
 
         public _checkInputs(): void {
+            this.onAfterCheckInputsObservable.notifyObservers(this);
         }
 
         public get rigCameras(): Camera[] {
@@ -548,6 +550,7 @@
             // Observables
             this.onViewMatrixChangedObservable.clear();
             this.onProjectionMatrixChangedObservable.clear();
+            this.onAfterCheckInputsObservable.clear();
 
             // Animations
             this.getScene().stopAnimation(this);

+ 48 - 0
src/babylon.node.ts

@@ -106,6 +106,49 @@ module BABYLON {
             return this._scene.getEngine();
         }
 
+        // Behaviors
+        private _behaviors = new Array<Behavior<Node>>();
+
+        public addBehavior(behavior: Behavior<Node>): Node {
+            var index = this._behaviors.indexOf(behavior);
+
+            if (index !== -1) {
+                return;
+            }
+
+            behavior.attach(this);
+            this._behaviors.push(behavior);
+
+            return this;
+        }
+
+        public removeBehavior(behavior: Behavior<Node>): Node {
+            var index = this._behaviors.indexOf(behavior);
+
+            if (index === -1) {
+                return;
+            } 
+
+            this._behaviors[index].detach(this);
+            this._behaviors.splice(index, 1);
+
+            return this;
+        }     
+        
+        public get behaviors(): Behavior<Node>[] {
+            return this._behaviors;
+        }
+
+        public getBehaviorByName(name: string): Behavior<Node> {
+            for (var behavior of this._behaviors) {
+                if (behavior.name === name) {
+                    return behavior;
+                }
+            }
+
+            return null;
+        }
+
         // override it in derived class
         public getWorldMatrix(): Matrix {
             return Matrix.Identity();
@@ -365,6 +408,11 @@ module BABYLON {
             // Callback
             this.onDisposeObservable.notifyObservers(this);
             this.onDisposeObservable.clear();
+
+            // Behaviors
+            for (var behavior of this._behaviors) {
+                behavior.detach(this);
+            }
         }
         
         public static ParseAnimationRanges(node: Node, parsedNode: any, scene: Scene): void {