Browse Source

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

# Conflicts:
#	dist/preview release/babylon.d.ts
#	dist/preview release/babylon.js
#	dist/preview release/babylon.module.d.ts
#	dist/preview release/babylon.worker.js
Gianni 8 years ago
parent
commit
a52874ec3e

+ 2 - 1
Tools/Gulp/config.json

@@ -101,7 +101,8 @@
         {
         {
             "files": [
             "files": [
                 "../../src/Behaviors/Cameras/babylon.framingBehavior.js",
                 "../../src/Behaviors/Cameras/babylon.framingBehavior.js",
-                "../../src/Behaviors/Cameras/babylon.bouncingBehavior.js"
+                "../../src/Behaviors/Cameras/babylon.bouncingBehavior.js",
+                "../../src/Behaviors/Cameras/babylon.autoRotationBehavior.js"
             ],
             ],
             "dependUpon" : [
             "dependUpon" : [
                 "core"
                 "core"

File diff suppressed because it is too large
+ 7353 - 7156
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 31 - 31
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 519 - 8
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 7353 - 7156
dist/preview release/babylon.module.d.ts


File diff suppressed because it is too large
+ 43 - 43
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 6492 - 6295
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


File diff suppressed because it is too large
+ 33 - 33
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


File diff suppressed because it is too large
+ 803 - 10
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


File diff suppressed because it is too large
+ 6492 - 6295
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 0 - 5
dist/preview release/gui/babylon.gui.d.ts

@@ -353,7 +353,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 
 
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class Line extends Control {
     class Line extends Control {
         name: string;
         name: string;
@@ -387,7 +386,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 
 
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class Slider extends Control {
     class Slider extends Control {
         name: string;
         name: string;
@@ -418,7 +416,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 
 
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class Checkbox extends Control {
     class Checkbox extends Control {
         name: string;
         name: string;
@@ -439,7 +436,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 
 
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class RadioButton extends Control {
     class RadioButton extends Control {
         name: string;
         name: string;
@@ -548,7 +544,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 
 
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class ColorPicker extends Control {
     class ColorPicker extends Control {
         name: string;
         name: string;

+ 0 - 5
dist/preview release/gui/babylon.gui.js

@@ -2104,7 +2104,6 @@ var __extends = (this && this.__extends) || (function () {
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
     };
     };
 })();
 })();
-var DOMImage = Image;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var GUI;
     var GUI;
@@ -2314,7 +2313,6 @@ var __extends = (this && this.__extends) || (function () {
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
     };
     };
 })();
 })();
-var DOMImage = Image;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var GUI;
     var GUI;
@@ -2519,7 +2517,6 @@ var __extends = (this && this.__extends) || (function () {
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
     };
     };
 })();
 })();
-var DOMImage = Image;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var GUI;
     var GUI;
@@ -2645,7 +2642,6 @@ var __extends = (this && this.__extends) || (function () {
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
     };
     };
 })();
 })();
-var DOMImage = Image;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var GUI;
     var GUI;
@@ -3355,7 +3351,6 @@ var __extends = (this && this.__extends) || (function () {
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
         d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
     };
     };
 })();
 })();
-var DOMImage = Image;
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var GUI;
     var GUI;

File diff suppressed because it is too large
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


+ 0 - 5
dist/preview release/gui/babylon.gui.module.d.ts

@@ -353,7 +353,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class Line extends Control {
     class Line extends Control {
         name: string;
         name: string;
@@ -387,7 +386,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class Slider extends Control {
     class Slider extends Control {
         name: string;
         name: string;
@@ -418,7 +416,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class Checkbox extends Control {
     class Checkbox extends Control {
         name: string;
         name: string;
@@ -439,7 +436,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class RadioButton extends Control {
     class RadioButton extends Control {
         name: string;
         name: string;
@@ -548,7 +544,6 @@ declare module BABYLON.GUI {
 }
 }
 
 
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
-declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
     class ColorPicker extends Control {
     class ColorPicker extends Control {
         name: string;
         name: string;

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

@@ -3,7 +3,11 @@
 ## Major updates
 ## Major updates
 - Added support for non-pow2 textures when in WebGL2 mode ([deltakosh](https://github.com/deltakosh))
 - 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))
 - Engine can now be initialized with an existing webgl context ([deltakosh](https://github.com/deltakosh))
-- Introducing behaviors. (Doc here)[] ([deltakosh](https://github.com/deltakosh))
+- Introduced behaviors. (Doc here)[http://doc.babylonjs.com/overviews/behaviors] ([deltakosh](https://github.com/deltakosh))
+- New behaviors for ArcRotateCamera:
+ - AutoRotation ([deltakosh](https://github.com/deltakosh))
+ - Framing ([deltakosh](https://github.com/deltakosh))
+ - Bouncing ([deltakosh](https://github.com/deltakosh))
 
 
 ## Updates
 ## Updates
 - POW2 textures rescale is now done by shaders (It was done using canvas before) ([deltakosh](https://github.com/deltakosh))
 - POW2 textures rescale is now done by shaders (It was done using canvas before) ([deltakosh](https://github.com/deltakosh))

+ 1 - 1
src/Animations/babylon.easing.ts

@@ -124,7 +124,7 @@
         }
         }
     }
     }
 
 
-    export class ExponentialEase  extends EasingFunction implements IEasingFunction {
+    export class ExponentialEase extends EasingFunction implements IEasingFunction {
         constructor(public exponent: number= 2) {
         constructor(public exponent: number= 2) {
             super();
             super();
         }
         }

+ 157 - 0
src/Behaviors/Cameras/babylon.autoRotationBehavior.ts

@@ -0,0 +1,157 @@
+module BABYLON {
+    export class AutoRotationBehavior implements Behavior<ArcRotateCamera> {
+        public get name(): string {
+            return "AutoRotation";
+        }
+
+        private _zoomStopsAnimation = false;
+		private _idleRotationSpeed = 0.05;
+		private _idleRotationWaitTime = 2000;
+        private _idleRotationSpinupTime = 2000;  
+       
+		/**
+		* Sets the flag that indicates if user zooming should stop model animation.
+		*/
+		public set zoomStopsAnimation(flag: boolean) {
+			this._zoomStopsAnimation = flag;
+		}
+
+		/**
+		* Gets the flag that indicates if user zooming should stop model animation.
+		*/
+		public get zoomStopsAnimation(): boolean {
+			return this._zoomStopsAnimation;
+        }       
+        
+		/**
+		* Sets the default speed at which the camera rotates around the model.
+		*/
+		public set idleRotationSpeed(speed: number) {
+			this._idleRotationSpeed = speed;
+		}
+
+		/**
+		* Gets the default speed at which the camera rotates around the model.
+		*/
+		public get idleRotationSpeed() {
+			return this._idleRotationSpeed;
+		}
+
+		/**
+		* Sets the time (in milliseconds) to wait after user interaction before the camera starts rotating.
+		*/
+		public set idleRotationWaitTime(time: number) {
+			this._idleRotationWaitTime = time;
+		}
+
+		/**
+		* Gets the time (milliseconds) to wait after user interaction before the camera starts rotating.
+		*/
+		public get idleRotationWaitTime() {
+			return this._idleRotationWaitTime;
+		}
+
+		/**
+		* Sets the time (milliseconds) to take to spin up to the full idle rotation speed.
+		*/
+		public set idleRotationSpinupTime(time: number) {
+			this._idleRotationSpinupTime = time;
+		}
+
+		/**
+		* Gets the time (milliseconds) to take to spin up to the full idle rotation speed.
+		*/
+		public get idleRotationSpinupTime() {
+			return this._idleRotationSpinupTime;
+		}
+        
+        // Default behavior functions
+        private _onPrePointerObservableObserver: Observer<PointerInfoPre>;
+        private _onAfterCheckInputsObserver: Observer<Camera>;
+        private _attachedCamera: ArcRotateCamera;
+        private _isPointerDown = false;
+        private _lastFrameTime: number = null;
+        private _lastInteractionTime = -Infinity;
+
+        public attach(camera: ArcRotateCamera): void {
+            this._attachedCamera = camera;
+            let scene = this._attachedCamera.getScene();
+
+            this._onPrePointerObservableObserver = scene.onPrePointerObservable.add((pointerInfoPre) => {
+                if (pointerInfoPre.type === PointerEventTypes.POINTERDOWN) {
+                    this._isPointerDown = true;
+                    return
+                }
+
+                if (pointerInfoPre.type === PointerEventTypes.POINTERUP) {
+                    this._isPointerDown = false;
+                }
+            });
+
+            this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {      
+                let now = Tools.Now;
+                let dt = 16;
+                if (this._lastFrameTime != null) {
+                    dt =  now - this._lastFrameTime;
+                }
+                this._lastFrameTime = now;
+
+                // Stop the animation if there is user interaction and the animation should stop for this interaction
+                this._applyUserInteraction();
+    
+                let timeToRotation = now - this._lastInteractionTime - this._idleRotationWaitTime;
+				let scale = Math.max(Math.min(timeToRotation / (this._idleRotationSpinupTime), 1), 0);
+                let cameraRotationSpeed = this._idleRotationSpeed * scale;
+    
+                // Step camera rotation by rotation speed
+                this._attachedCamera.alpha -= cameraRotationSpeed * (dt / 1000);
+            });
+        }
+             
+        public detach(camera: ArcRotateCamera): void {
+            let scene = this._attachedCamera.getScene();
+            
+            scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
+            camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+		}
+
+		/**
+		 * Returns true if user is scrolling. 
+		 * @return true if user is scrolling.
+		 */
+		private _userIsZooming(): boolean {
+			return this._attachedCamera.inertialRadiusOffset !== 0;
+		}   		
+		
+		private _lastFrameRadius = 0;
+		private _shouldAnimationStopForInteraction(): boolean {
+			var zoomHasHitLimit = false;
+			if (this._lastFrameRadius === this._attachedCamera.radius && this._attachedCamera.inertialRadiusOffset !== 0) {
+				zoomHasHitLimit = true;
+			}
+
+			// Update the record of previous radius - works as an approx. indicator of hitting radius limits
+			this._lastFrameRadius = this._attachedCamera.radius;
+			return this._zoomStopsAnimation ? zoomHasHitLimit : this._userIsZooming();
+		}   		
+
+		/**
+		 *  Applies any current user interaction to the camera. Takes into account maximum alpha rotation.
+		 */          
+        private _applyUserInteraction(): void {
+			if (this._userIsMoving() && !this._shouldAnimationStopForInteraction()) {
+                this._lastInteractionTime = Tools.Now;
+			}
+        }                
+   
+        // Tools
+        private _userIsMoving(): boolean {
+			return this._attachedCamera.inertialAlphaOffset !== 0 ||
+				this._attachedCamera.inertialBetaOffset !== 0 ||
+				this._attachedCamera.inertialRadiusOffset !== 0 ||
+				this._attachedCamera.inertialPanningX !== 0 ||
+				this._attachedCamera.inertialPanningY !== 0 ||
+				this._isPointerDown;
+		}
+    }
+}

+ 3 - 3
src/Behaviors/Cameras/babylon.bouncingBehavior.ts

@@ -35,10 +35,10 @@ module BABYLON {
         
         
         // Connection
         // Connection
         private _attachedCamera: ArcRotateCamera;
         private _attachedCamera: ArcRotateCamera;
-        private _onAfterCheckInputsObservabler: Observer<Camera>;
+        private _onAfterCheckInputsObserver: Observer<Camera>;
         public attach(camera: ArcRotateCamera): void {
         public attach(camera: ArcRotateCamera): void {
             this._attachedCamera = camera;
             this._attachedCamera = camera;
-            this._onAfterCheckInputsObservabler = camera.onAfterCheckInputsObservable.add(() => {
+            this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
 				// Add the bounce animation to the lower radius limit
 				// Add the bounce animation to the lower radius limit
 				if (this._isRadiusAtLimit(this._attachedCamera.lowerRadiusLimit)) {
 				if (this._isRadiusAtLimit(this._attachedCamera.lowerRadiusLimit)) {
 					this._applyBoundRadiusAnimation(this.lowerRadiusTransitionRange);
 					this._applyBoundRadiusAnimation(this.lowerRadiusTransitionRange);
@@ -52,7 +52,7 @@ module BABYLON {
         }
         }
         
         
         public detach(camera: ArcRotateCamera): void {
         public detach(camera: ArcRotateCamera): void {
-            camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObservabler);
+            camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
         }
         }
 
 
         // Animations
         // Animations

+ 306 - 1
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -3,13 +3,318 @@ module BABYLON {
         public get name(): string {
         public get name(): string {
             return "Framing";
             return "Framing";
         }
         }
+
+        private _mode = FramingBehavior.IgnoreBoundsSizeMode;
+        private _radius = 0;
+        private _elevation = 0;
+        private _positionY = 0;
+        private _defaultElevation = 0;
+        private _elevationReturnTime = 2;
+        private _elevationReturnWaitTime = 0;
+        private _zoomStopsAnimation = false;
+		private _framingTime = 1.0;
+        private _easingFunction: EasingFunction = new ExponentialEase();
+        private _easingMode = EasingFunction.EASINGMODE_EASEINOUT;
         
         
-        public attach(camera: ArcRotateCamera): void {
+		/**
+		 * Gets the easing function to use for transitions
+		 */
+		public get easingFunction(): EasingFunction {
+            return this._easingFunction;
+        }
+        
+        /**
+		 * Sets the easing function to use for transitions
+		 */
+		public set easingFunction(value: EasingFunction) {
+            this._easingFunction = value;
+        }    
 
 
+		/**
+		 * Gets the easing function to use for transitions
+		 */
+		public get easingMode(): number {
+            return this._easingMode;
         }
         }
         
         
+        /**
+		 * Sets the easing function to use for transitions
+		 */
+		public set easingMode(value: number) {
+            this._easingMode = value;
+        }          
+
+        /**
+		 * Sets the current mode used by the behavior
+		 */
+		public set mode(mode: number) {
+			this._mode = mode;
+		}
+
+		/**
+		 * Gets current mode used by the behavior.
+		 */
+		public get mode(): number {
+			return this._mode;
+        }
+        
+	    /**
+		 * Sets the radius of the camera relative to the framed model's bounding box.
+		 */
+		public set radius(radius: number) {
+			this._radius = radius;
+		}
+
+		/**
+		 * Gets the radius of the camera relative to the framed model's bounding box.
+		 */
+		public get radius(): number {
+			return this._radius;
+		}
+
+		/**
+		 * Sets the elevation of the camera from the framed model, in radians.
+		 */
+		public set elevation(elevation: number) {
+			this._elevation = elevation;
+		}
+
+		/**
+		 * Gets the elevation of the camera from the framed model, in radians.
+		 */
+		public get elevation(): number {
+			return this._elevation;
+		}
+
+		/**
+		 * Sets the Y offset of the primary model from the camera's focus.
+		 */
+		public set positionY(positionY: number) {
+			this._positionY = positionY;
+		}
+
+		/**
+		 * Gets the Y offset of the primary model from the camera's focus.
+		 */
+		public get positionY(): number {
+			return this._positionY;
+		}
+
+		/**
+		* Sets the angle above/below the horizontal plane to return to when the return to default elevation idle
+		* behaviour is triggered, in radians.
+		*/
+		public set defaultElevation(elevation: number) {
+			this._defaultElevation = elevation;
+		}
+
+		/**
+		* Gets the angle above/below the horizontal plane to return to when the return to default elevation idle
+		* behaviour is triggered, in radians.
+		*/
+		public get defaultElevation() {
+			return this._defaultElevation;
+		}
+
+		/**
+		 * Sets the time (in milliseconds) taken to return to the default beta position.
+		 * Negative value indicates camera should not return to default.
+		 */
+		public set elevationReturnTime(speed: number) {
+			this._elevationReturnTime = speed;
+		}
+
+		/**
+		 * Gets the time (in milliseconds) taken to return to the default beta position.
+		 * Negative value indicates camera should not return to default.
+		 */
+		public get elevationReturnTime(): number {
+			return this._elevationReturnTime;
+		}
+
+		/**
+		 * Sets the delay (in milliseconds) taken before the camera returns to the default beta position.
+		 */
+		public set elevationReturnWaitTime(time: number) {
+			this._elevationReturnWaitTime = time;
+		}
+
+		/**
+		 * Gets the delay (in milliseconds) taken before the camera returns to the default beta position.
+		 */
+		public get elevationReturnWaitTime(): number {
+			return this._elevationReturnWaitTime;
+		}
+
+		/**
+		* Sets the flag that indicates if user zooming should stop model animation.
+		*/
+		public set zoomStopsAnimation(flag: boolean) {
+			this._zoomStopsAnimation = flag;
+		}
+
+		/**
+		* Gets the flag that indicates if user zooming should stop model animation.
+		*/
+		public get zoomStopsAnimation(): boolean {
+			return this._zoomStopsAnimation;
+        }       
+        	
+		/**
+		 * Sets the transition time when framing the model, in milliseconds
+		*/
+		public set framingTime(time: number) {
+			this._framingTime = time;
+		}
+
+        /**
+         * Gets the transition time when framing the model, in milliseconds
+        */
+        public get framingTime() {
+            return this._framingTime;
+		}        
+        
+        // Default behavior functions
+        private _onPrePointerObservableObserver: Observer<PointerInfoPre>;
+        private _onAfterCheckInputsObserver: Observer<Camera>;
+        private _attachedCamera: ArcRotateCamera;
+        private _isPointerDown = false;
+        private _lastFrameTime: number = null;
+        private _lastInteractionTime = -Infinity;
+
+        public attach(camera: ArcRotateCamera): void {
+            this._attachedCamera = camera;
+            let scene = this._attachedCamera.getScene();
+
+            this._onPrePointerObservableObserver = scene.onPrePointerObservable.add((pointerInfoPre) => {
+                if (pointerInfoPre.type === PointerEventTypes.POINTERDOWN) {
+                    this._isPointerDown = true;
+                    return
+                }
+
+                if (pointerInfoPre.type === PointerEventTypes.POINTERUP) {
+                    this._isPointerDown = false;
+                }
+            });
+
+            this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {      
+                // Stop the animation if there is user interaction and the animation should stop for this interaction
+                this._applyUserInteraction();
+       
+                // Maintain the camera above the ground. If the user pulls the camera beneath the ground plane, lift it
+                // back to the default position after a given timeout
+                this._maintainCameraAboveGround();                
+            });
+        }
+             
         public detach(camera: ArcRotateCamera): void {
         public detach(camera: ArcRotateCamera): void {
+            let scene = this._attachedCamera.getScene();
             
             
+            scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
+            camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
         }
         }
+
+        // Framing control
+        private _animatables = new Array<BABYLON.Animatable>();
+        private _betaIsAnimating = false;
+        private _betaTransition: BABYLON.Animation;
+        private _lastFrameRadius = 0;
+
+		/**
+		 * Keeps the camera above the ground plane. If the user pulls the camera below the ground plane, the camera
+		 * is automatically returned to its default position (expected to be above ground plane). 
+		 */
+		private _maintainCameraAboveGround(): void {
+			let timeSinceInteraction = Tools.Now - this._lastInteractionTime;
+			let defaultBeta = Math.PI * 0.5 - this._defaultElevation;
+			let limitBeta = Math.PI * 0.5;
+            
+            // Bring the camera back up if below the ground plane
+			if (!this._betaIsAnimating && this._attachedCamera.beta > limitBeta && timeSinceInteraction >= this._elevationReturnWaitTime) {
+                this._betaIsAnimating = true;
+                
+				//Transition to new position
+                this.stopAllAnimations();
+                
+                if (!this._betaTransition) {
+                    this.easingFunction.setEasingMode(this.easingMode);
+                    this._betaTransition = Animation.CreateAnimation("beta", Animation.ANIMATIONTYPE_FLOAT, 60, this.easingFunction);
+                }
+
+				Animation.TransitionTo("beta", defaultBeta, this._attachedCamera, this._attachedCamera.getScene(), 60,
+                    this._betaTransition, this._elevationReturnTime, 
+                    () => {
+						this.stopAllAnimations();
+					});
+			}
+		}        
+
+		/**
+		 * Returns true if user is scrolling. 
+		 * @return true if user is scrolling.
+		 */
+		protected _userIsZooming(): boolean {
+			return this._attachedCamera.inertialRadiusOffset !== 0;
+		}        
+
+		/**
+		 * Indicates if default model animation (rotation) should stop for the current user interaction.
+		 * 
+		 * @return True if the model animation (rotation) should stop when given the current user interaction.
+		 */
+		protected _shouldAnimationStopForInteraction(): boolean {
+			var zoomHasHitLimit = false;
+			if (this._lastFrameRadius === this._attachedCamera.radius && this._attachedCamera.inertialRadiusOffset !== 0) {
+				zoomHasHitLimit = true;
+			}
+
+			// Update the record of previous radius - works as an approx. indicator of hitting radius limits
+			this._lastFrameRadius = this._attachedCamera.radius;
+			return this._zoomStopsAnimation ? zoomHasHitLimit : this._userIsZooming();
+		}        
+
+		/**
+		 *  Applies any current user interaction to the camera. Takes into account maximum alpha rotation.
+		 */          
+        private _applyUserInteraction(): void {
+			if (this._userIsMoving() && !this._shouldAnimationStopForInteraction()) {
+                this._lastInteractionTime = Tools.Now;
+				this.stopAllAnimations();
+			}
+        }                
+        
+		/**
+		 * 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();
+			}
+		}        
+
+        // Tools
+        private _userIsMoving(): boolean {
+			return this._attachedCamera.inertialAlphaOffset !== 0 ||
+				this._attachedCamera.inertialBetaOffset !== 0 ||
+				this._attachedCamera.inertialRadiusOffset !== 0 ||
+				this._attachedCamera.inertialPanningX !== 0 ||
+				this._attachedCamera.inertialPanningY !== 0 ||
+				this._isPointerDown;
+		}
+
+        // Statics
+
+        /**
+         * The camera can move all the way towards the model.
+         */
+        public static IgnoreBoundsSizeMode = 0;
+
+        /**
+         * The camera is not allowed to zoom closer to the model than the point at which the adjusted bounding sphere touches the frustum sides
+         */
+        public static FitFrustumSidesMode = 0;
     }
     }
 }
 }

+ 0 - 2
src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts

@@ -229,8 +229,6 @@ module BABYLON {
                 this._onLostFocus = null;
                 this._onLostFocus = null;
                 this._onContextMenu = null;
                 this._onContextMenu = null;
             }
             }
-
-            this.camera = null;
         }
         }
 
 
         getClassName(): string {
         getClassName(): string {

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

@@ -214,6 +214,45 @@ module BABYLON {
             }
             }
         }
         }
 
 
+        private _framingBehavior: FramingBehavior;
+        public get useFramingBehavior(): boolean {
+            return this._framingBehavior != null;
+        }
+
+        public set useFramingBehavior(value: boolean) {
+            if (value === this.useFramingBehavior) {
+                return;
+            }
+
+            if (value) {
+                this._framingBehavior = new FramingBehavior();
+                this.addBehavior(this._framingBehavior);
+            } else {
+                this.removeBehavior(this._framingBehavior);
+                this._framingBehavior = null;
+            }
+        }        
+
+        private _autoRotationBehavior: AutoRotationBehavior;
+        public get useAutoRotationBehavior(): boolean {
+            return this._autoRotationBehavior != null;
+        }
+
+        public set useAutoRotationBehavior(value: boolean) {
+            if (value === this.useAutoRotationBehavior) {
+                return;
+            }
+
+            if (value) {
+                this._autoRotationBehavior = new AutoRotationBehavior();
+                this.addBehavior(this._autoRotationBehavior);
+            } else {
+                this.removeBehavior(this._autoRotationBehavior);
+                this._autoRotationBehavior = null;
+            }
+        }        
+        
+
         // Collisions
         // Collisions
         public onCollide: (collidedMesh: AbstractMesh) => void;
         public onCollide: (collidedMesh: AbstractMesh) => void;
         public checkCollisions = false;
         public checkCollisions = false;
@@ -427,6 +466,10 @@ module BABYLON {
             var radiusv3 = this.position.subtract(this._getTargetPosition());
             var radiusv3 = this.position.subtract(this._getTargetPosition());
             this.radius = radiusv3.length();
             this.radius = radiusv3.length();
 
 
+            if (this.radius === 0) {
+                this.radius = 0.0001; // Just to avoid division by zero
+            }
+
             // Alpha
             // Alpha
             this.alpha = Math.acos(radiusv3.x / Math.sqrt(Math.pow(radiusv3.x, 2) + Math.pow(radiusv3.z, 2)));
             this.alpha = Math.acos(radiusv3.x / Math.sqrt(Math.pow(radiusv3.x, 2) + Math.pow(radiusv3.z, 2)));
 
 

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

@@ -552,6 +552,9 @@
             this.onProjectionMatrixChangedObservable.clear();
             this.onProjectionMatrixChangedObservable.clear();
             this.onAfterCheckInputsObservable.clear();
             this.onAfterCheckInputsObservable.clear();
 
 
+            // Inputs
+            this.inputs.clear();
+
             // Animations
             // Animations
             this.getScene().stopAnimation(this);
             this.getScene().stopAnimation(this);
 
 

+ 8 - 2
src/Cameras/babylon.cameraInputsManager.ts

@@ -55,6 +55,7 @@ module BABYLON {
                 var input = this.attached[cam];
                 var input = this.attached[cam];
                 if (input === inputToRemove) {
                 if (input === inputToRemove) {
                     input.detachControl(this.attachedElement);
                     input.detachControl(this.attachedElement);
+                    input.camera = null;
                     delete this.attached[cam];
                     delete this.attached[cam];
                     this.rebuildInputCheck();
                     this.rebuildInputCheck();
                 }
                 }
@@ -66,6 +67,7 @@ module BABYLON {
                 var input = this.attached[cam];
                 var input = this.attached[cam];
                 if (input.getClassName() === inputType) {
                 if (input.getClassName() === inputType) {
                     input.detachControl(this.attachedElement);
                     input.detachControl(this.attachedElement);
+                    input.camera = null;
                     delete this.attached[cam];
                     delete this.attached[cam];
                     this.rebuildInputCheck();
                     this.rebuildInputCheck();
                 }
                 }
@@ -99,7 +101,7 @@ module BABYLON {
             }
             }
         }
         }
 
 
-        public detachElement(element: HTMLElement) {
+        public detachElement(element: HTMLElement, disconnect = false) {
             if (this.attachedElement !== element) {
             if (this.attachedElement !== element) {
                 return;
                 return;
             }
             }
@@ -107,6 +109,10 @@ module BABYLON {
             for (var cam in this.attached) {
             for (var cam in this.attached) {
                 var input = this.attached[cam];
                 var input = this.attached[cam];
                 this.attached[cam].detachControl(element);
                 this.attached[cam].detachControl(element);
+
+                if (disconnect) {
+                    this.attached[cam].camera = null;
+                }
             }
             }
 
 
             this.attachedElement = null;
             this.attachedElement = null;
@@ -125,7 +131,7 @@ module BABYLON {
 
 
         public clear() {
         public clear() {
             if (this.attachedElement) {
             if (this.attachedElement) {
-                this.detachElement(this.attachedElement);
+                this.detachElement(this.attachedElement, true);
             }
             }
             this.attached = {};
             this.attached = {};
             this.attachedElement = null;
             this.attachedElement = null;

+ 1 - 0
src/Materials/babylon.material.ts

@@ -470,6 +470,7 @@
 
 
         protected _afterBind(mesh: Mesh): void {
         protected _afterBind(mesh: Mesh): void {
             this._scene._cachedMaterial = this;
             this._scene._cachedMaterial = this;
+            this._scene._cachedVisibility = mesh.visibility;
 
 
             this.onBindObservable.notifyObservers(mesh);
             this.onBindObservable.notifyObservers(mesh);
 
 

+ 1 - 1
src/Materials/babylon.pushMaterial.ts

@@ -42,7 +42,7 @@
         }
         }
 
 
         protected _mustRebind(scene: Scene, effect: Effect, visibility: number = 0) {
         protected _mustRebind(scene: Scene, effect: Effect, visibility: number = 0) {
-            return scene.isCachedMaterialValid(this, effect, visibility);
+            return scene.isCachedMaterialInvalid(this, effect, visibility);
         }
         }
     }
     }
 } 
 } 

+ 1 - 1
src/babylon.scene.ts

@@ -932,7 +932,7 @@
             return this._cachedVisibility;
             return this._cachedVisibility;
         }
         }
 
 
-        public isCachedMaterialValid(material: Material, effect: Effect, visibility: number = 0) {
+        public isCachedMaterialInvalid(material: Material, effect: Effect, visibility: number = 1) {
             return this._cachedEffect !== effect || this._cachedMaterial !== material || this._cachedVisibility !== visibility;
             return this._cachedEffect !== effect || this._cachedMaterial !== material || this._cachedVisibility !== visibility;
         }
         }