浏览代码

Fixed #2988
Associated with #2982

David Catuhe 7 年之前
父节点
当前提交
f0a0a90146

文件差异内容过多而无法显示
+ 4420 - 4420
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 4420 - 4420
dist/preview release/babylon.module.d.ts


+ 9 - 7
src/Audio/babylon.analyser.ts

@@ -10,16 +10,16 @@ module BABYLON {
         private _byteTime: Uint8Array;
         private _floatFreqs: Float32Array;
         private _webAudioAnalyser: AnalyserNode;
-        private _debugCanvas: HTMLCanvasElement;
-        private _debugCanvasContext: CanvasRenderingContext2D;
+        private _debugCanvas: Nullable< HTMLCanvasElement>;
+        private _debugCanvasContext: Nullable<CanvasRenderingContext2D>;
         private _scene: Scene;
-        private _registerFunc: () => void
+        private _registerFunc: Nullable<() => void>;
         private _audioEngine: AudioEngine;
 
         constructor(scene: Scene) {
             this._scene = scene;
             this._audioEngine = Engine.audioEngine;
-            if (this._audioEngine.canUseWebAudio) {
+            if (this._audioEngine.canUseWebAudio && this._audioEngine.audioContext) {
                 this._webAudioAnalyser = this._audioEngine.audioContext.createAnalyser();
                 this._webAudioAnalyser.minDecibels = -140;
                 this._webAudioAnalyser.maxDecibels = 0;
@@ -81,7 +81,7 @@ module BABYLON {
                     };
                     this._scene.registerBeforeRender(this._registerFunc);
                 }
-                if (this._registerFunc) {
+                if (this._registerFunc && this._debugCanvasContext) {
                     var workingArray = this.getByteFrequencyData();
 
                     this._debugCanvasContext.fillStyle = 'rgb(0, 0, 0)';
@@ -104,8 +104,10 @@ module BABYLON {
 
         public stopDebugCanvas() {
             if (this._debugCanvas) {
-                this._scene.unregisterBeforeRender(this._registerFunc);
-                this._registerFunc = null;
+                if (this._registerFunc) {
+                    this._scene.unregisterBeforeRender(this._registerFunc);
+                    this._registerFunc = null;
+                }
                 document.body.removeChild(this._debugCanvas);
                 this._debugCanvas = null;
                 this._debugCanvasContext = null;

+ 8 - 5
src/Audio/babylon.audioEngine.ts

@@ -1,11 +1,11 @@
 module BABYLON {
     export class AudioEngine {
-        private _audioContext: AudioContext = null;
+        private _audioContext: Nullable<AudioContext> = null;
         private _audioContextInitialized = false;
         public canUseWebAudio: boolean = false;
         public masterGain: GainNode;
 
-        private _connectedAnalyser: Analyser;
+        private _connectedAnalyser: Nullable<Analyser>;
         public WarnedWebAudioUnsupported: boolean = false;
         public unlocked: boolean = false;
         public onAudioUnlocked: () => any;
@@ -13,7 +13,7 @@
         public isMP3supported: boolean = false;
         public isOGGsupported: boolean = false;
 
-        public get audioContext(): AudioContext {
+        public get audioContext(): Nullable<AudioContext> {
             if (!this._audioContextInitialized) {
                 this._initializeAudioContext();
             }
@@ -56,6 +56,9 @@
 
         private _unlockiOSaudio() {
             var unlockaudio = () => {
+                if (!this.audioContext) {
+                    return;
+                }
                 var buffer = this.audioContext.createBuffer(1, 1, 22050);
                 var source = this.audioContext.createBufferSource();
                 source.buffer = buffer;
@@ -95,7 +98,7 @@
 
         public dispose() {
             if (this.canUseWebAudio && this._audioContextInitialized) {
-                if (this._connectedAnalyser) {
+                if (this._connectedAnalyser && this._audioContext) {
                     this._connectedAnalyser.stopDebugCanvas();
                     this._connectedAnalyser.dispose();
                     this.masterGain.disconnect();
@@ -126,7 +129,7 @@
             if (this._connectedAnalyser) {
                 this._connectedAnalyser.stopDebugCanvas();
             }
-            if (this.canUseWebAudio && this._audioContextInitialized) {
+            if (this.canUseWebAudio && this._audioContextInitialized && this._audioContext) {
                 this._connectedAnalyser = analyser;
                 this.masterGain.disconnect();
                 this._connectedAnalyser.connectAudioNodes(this.masterGain, this._audioContext.destination);

+ 46 - 31
src/Audio/babylon.sound.ts

@@ -24,12 +24,12 @@ module BABYLON {
         public isPlaying: boolean = false;
         public isPaused: boolean = false;
         private _isDirectional: boolean = false;
-        private _readyToPlayCallback: () => any;
-        private _audioBuffer: AudioBuffer;
-        private _soundSource: AudioBufferSourceNode;
+        private _readyToPlayCallback: Nullable<() => any>;
+        private _audioBuffer: Nullable<AudioBuffer>;
+        private _soundSource: Nullable<AudioBufferSourceNode>;
         private _streamingSource: MediaElementAudioSourceNode
-        private _soundPanner: PannerNode;
-        private _soundGain: GainNode;
+        private _soundPanner: Nullable<PannerNode>;
+        private _soundGain: Nullable<GainNode>;
         private _inputAudioNode: AudioNode;
         private _ouputAudioNode: AudioNode;
         // Used if you'd like to create a directional sound.
@@ -38,9 +38,9 @@ module BABYLON {
         private _coneOuterAngle: number = 360;
         private _coneOuterGain: number = 0;
         private _scene: Scene;
-        private _connectedMesh: AbstractMesh;
+        private _connectedMesh: Nullable<AbstractMesh>;
         private _customAttenuationFunction: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number;
-        private _registerFunc: (connectedMesh: AbstractMesh) => any;
+        private _registerFunc: Nullable<(connectedMesh: AbstractMesh) => any>;
         private _isOutputConnected = false;
         private _htmlAudioElement: HTMLAudioElement;
         private _urlType: string = "Unknown";
@@ -52,7 +52,7 @@ module BABYLON {
         * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
         * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
         */
-        constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: () => void, options?: any) {
+        constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback: Nullable<() => void> = null, options?: any) {
             this.name = name;
             this._scene = scene;
             this._readyToPlayCallback = readyToPlayCallback;
@@ -82,7 +82,7 @@ module BABYLON {
                 this._streaming = options.streaming || false;
             }
 
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine.canUseWebAudio && Engine.audioEngine.audioContext) {
                 this._soundGain = Engine.audioEngine.audioContext.createGain();
                 this._soundGain.gain.value = this._volume;
                 this._inputAudioNode = this._soundGain;
@@ -130,7 +130,7 @@ module BABYLON {
                                 if (codecSupportedFound) {
                                     // Loading sound using XHR2
                                     if (!this._streaming) {
-                                        Tools.LoadFile(url, (data) => { this._soundLoaded(data); }, null, this._scene.database, true);
+                                        Tools.LoadFile(url, (data) => { this._soundLoaded(data); }, undefined, this._scene.database, true);
                                     }
                                     // Streaming sound using HTML5 Audio tag
                                     else {
@@ -168,7 +168,9 @@ module BABYLON {
                             // Simulating a ready to play event to avoid breaking code path
                             if (this._readyToPlayCallback) {
                                 window.setTimeout(() => {
-                                    this._readyToPlayCallback();
+                                    if (this._readyToPlayCallback) {
+                                        this._readyToPlayCallback();
+                                    }
                                 }, 1000);
                             }
                         }
@@ -185,7 +187,9 @@ module BABYLON {
                 // Simulating a ready to play event to avoid breaking code for non web audio browsers
                 if (this._readyToPlayCallback) {
                     window.setTimeout(() => {
-                        this._readyToPlayCallback();
+                        if (this._readyToPlayCallback) {
+                            this._readyToPlayCallback();
+                        }
                     }, 1000);
                 }
             }
@@ -223,7 +227,7 @@ module BABYLON {
                     document.body.removeChild(this._htmlAudioElement);
                 }
 
-                if (this._connectedMesh) {
+                if (this._connectedMesh && this._registerFunc) {
                     this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                     this._connectedMesh = null;
                 }
@@ -236,6 +240,9 @@ module BABYLON {
 
         private _soundLoaded(audioData: ArrayBuffer) {
             this._isLoaded = true;
+            if (!Engine.audioEngine.audioContext) {
+                return;
+            }
             Engine.audioEngine.audioContext.decodeAudioData(audioData, (buffer) => {
                 this._audioBuffer = buffer;
                 this._isReadyToPlay = true;
@@ -266,14 +273,16 @@ module BABYLON {
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                     }
                     else {
-                        this._soundSource.playbackRate.value = this._playbackRate;
+                        if (this._soundSource) {
+                            this._soundSource.playbackRate.value = this._playbackRate;
+                        }
                     }
                 }
             }
         }
 
         private _createSpatialParameters() {
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine.canUseWebAudio && Engine.audioEngine.audioContext) {
                 if (this._scene.headphone) {
                     this._panningModel = "HRTF";
                 }
@@ -285,7 +294,7 @@ module BABYLON {
         }
 
         private _updateSpatialParameters() {
-            if (this.spatialSound) {
+            if (this.spatialSound && this._soundPanner) {
                 if (this.useCustomAttenuation) {
                     // Tricks to disable in a way embedded Web Audio attenuation 
                     this._soundPanner.distanceModel = "linear";
@@ -315,7 +324,7 @@ module BABYLON {
         }
 
         private _switchPanningModel() {
-            if (Engine.audioEngine.canUseWebAudio && this.spatialSound) {
+            if (Engine.audioEngine.canUseWebAudio && this.spatialSound && this._soundPanner) {
                 this._soundPanner.panningModel = this._panningModel as any;
             }
         }
@@ -355,7 +364,7 @@ module BABYLON {
         public setPosition(newPosition: Vector3) {
             this._position = newPosition;
 
-            if (Engine.audioEngine.canUseWebAudio && this.spatialSound) {
+            if (Engine.audioEngine.canUseWebAudio && this.spatialSound && this._soundPanner) {
                 this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
             }
         }
@@ -369,6 +378,10 @@ module BABYLON {
         }
 
         private _updateDirection() {
+            if (!this._connectedMesh || !this._soundPanner) {
+                return;
+            }
+
             var mat = this._connectedMesh.getWorldMatrix();
             var direction = Vector3.TransformNormal(this._localDirection, mat);
             direction.normalize();
@@ -376,7 +389,7 @@ module BABYLON {
         }
 
         public updateDistanceFromListener() {
-            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.useCustomAttenuation) {
+            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.useCustomAttenuation && this._soundGain && this._scene.activeCamera) {
                 var distance = this._connectedMesh.getDistanceToCamera(this._scene.activeCamera);
                 this._soundGain.gain.value = this._customAttenuationFunction(this._volume, distance, this.maxDistance, this.refDistance, this.rolloffFactor);
             }
@@ -392,7 +405,7 @@ module BABYLON {
         * @param offset (optional) Start the sound setting it at a specific time
         */
         public play(time?: number, offset?: number) {
-            if (this._isReadyToPlay && this._scene.audioEnabled) {
+            if (this._isReadyToPlay && this._scene.audioEnabled && Engine.audioEngine.audioContext) {
                 try {
                     if (this._startOffset < 0) {
                         time = -this._startOffset;
@@ -400,7 +413,7 @@ module BABYLON {
                     }
                     var startTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                     if (!this._soundSource || !this._streamingSource) {
-                        if (this.spatialSound) {
+                        if (this.spatialSound && this._soundPanner) {
                             this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
                             if (this._isDirectional) {
                                 this._soundPanner.coneInnerAngle = this._coneInnerAngle;
@@ -432,7 +445,9 @@ module BABYLON {
                         this._soundSource.loop = this.loop;
                         this._soundSource.playbackRate.value = this._playbackRate;
                         this._soundSource.onended = () => { this._onended(); };
-                        this._soundSource.start(startTime, this.isPaused ? this._startOffset % this._soundSource.buffer.duration : offset ? offset : 0);
+                        if (this._soundSource.buffer) {
+                            this._soundSource.start(startTime, this.isPaused ? this._startOffset % this._soundSource.buffer.duration : offset ? offset : 0);
+                        }
                     }
                     this._startTime = startTime;
                     this.isPlaying = true;
@@ -464,10 +479,10 @@ module BABYLON {
                         this._htmlAudioElement.currentTime = 0;
                     }
                 }
-                else {
+                else if (Engine.audioEngine.audioContext && this._soundSource) {
                     var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                     this._soundSource.stop(stopTime);
-                    this._soundSource.onended = null;
+                    this._soundSource.onended = () => {};
                     if (!this.isPaused) {
                         this._startOffset = 0;
                     }
@@ -482,7 +497,7 @@ module BABYLON {
                 if (this._streaming) {
                     this._htmlAudioElement.pause();
                 }
-                else {
+                else if (Engine.audioEngine.audioContext) {
                     this.stop(0);
                     this._startOffset += Engine.audioEngine.audioContext.currentTime - this._startTime;
                 }
@@ -490,8 +505,8 @@ module BABYLON {
         }
 
         public setVolume(newVolume: number, time?: number) {
-            if (Engine.audioEngine.canUseWebAudio) {
-                if (time) {
+            if (Engine.audioEngine.canUseWebAudio && this._soundGain) {
+                if (time && Engine.audioEngine.audioContext) {
                     this._soundGain.gain.cancelScheduledValues(Engine.audioEngine.audioContext.currentTime);
                     this._soundGain.gain.setValueAtTime(this._soundGain.gain.value, Engine.audioEngine.audioContext.currentTime);
                     this._soundGain.gain.linearRampToValueAtTime(newVolume, Engine.audioEngine.audioContext.currentTime + time);
@@ -509,7 +524,7 @@ module BABYLON {
                 if (this._streaming) {
                     this._htmlAudioElement.playbackRate = this._playbackRate;
                 }
-                else {
+                else if (this._soundSource) {
                     this._soundSource.playbackRate.value = this._playbackRate;
                 }
             }
@@ -520,7 +535,7 @@ module BABYLON {
         }
 
         public attachToMesh(meshToConnectTo: AbstractMesh) {
-            if (this._connectedMesh) {
+            if (this._connectedMesh && this._registerFunc) {
                 this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                 this._registerFunc = null;
             }
@@ -539,7 +554,7 @@ module BABYLON {
         }
 
         public detachFromMesh() {
-            if (this._connectedMesh) {
+            if (this._connectedMesh && this._registerFunc) {
                 this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                 this._registerFunc = null;
                 this._connectedMesh = null;
@@ -553,7 +568,7 @@ module BABYLON {
             }
         }
 
-        public clone(): Sound {
+        public clone(): Nullable<Sound> {
             if (!this._streaming) {
                 var setBufferAndRun = () => {
                     if (this._isReadyToPlay) {

+ 5 - 5
src/Audio/babylon.soundtrack.ts

@@ -1,6 +1,6 @@
 module BABYLON {
     export class SoundTrack {
-        private _outputAudioNode: GainNode;
+        private _outputAudioNode: Nullable<GainNode>;
         private _scene: Scene;
         public id: number = -1;
         public soundCollection: Array<Sound>;
@@ -21,7 +21,7 @@
         }
 
         private _initializeSoundTrackAudioGraph() {
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine.canUseWebAudio && Engine.audioEngine.audioContext) {
                 this._outputAudioNode = Engine.audioEngine.audioContext.createGain();
                 this._outputAudioNode.connect(Engine.audioEngine.masterGain);
 
@@ -53,7 +53,7 @@
             if (!this._isInitialized) {
                 this._initializeSoundTrackAudioGraph();
             }
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
                 sound.connectToSoundTrackAudioNode(this._outputAudioNode);
             }
             if (sound.soundTrackId) {
@@ -77,7 +77,7 @@
         }
 
         public setVolume(newVolume: number) {
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
                 this._outputAudioNode.gain.value = newVolume;
             }
         }
@@ -103,7 +103,7 @@
                 this._connectedAnalyser.stopDebugCanvas();
             }
             this._connectedAnalyser = analyser;
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
                 this._outputAudioNode.disconnect();
                 this._connectedAnalyser.connectAudioNodes(this._outputAudioNode, Engine.audioEngine.masterGain);
             }

+ 20 - 4
src/Behaviors/Cameras/babylon.autoRotationBehavior.ts

@@ -75,9 +75,9 @@ module BABYLON {
         // Default behavior functions
         private _onPrePointerObservableObserver: Observer<PointerInfoPre>;
         private _onAfterCheckInputsObserver: Observer<Camera>;
-        private _attachedCamera: ArcRotateCamera;
+        private _attachedCamera: Nullable<ArcRotateCamera>;
         private _isPointerDown = false;
-        private _lastFrameTime: number = null;
+        private _lastFrameTime: Nullable<number> = null;
 		private _lastInteractionTime = -Infinity;
 		private _cameraRotationSpeed: number = 0;
 
@@ -111,12 +111,17 @@ module BABYLON {
 				let scale = Math.max(Math.min(timeToRotation / (this._idleRotationSpinupTime), 1), 0);
                 this._cameraRotationSpeed = this._idleRotationSpeed * scale;
     
-                // Step camera rotation by rotation speed
-                this._attachedCamera.alpha -= this._cameraRotationSpeed * (dt / 1000);
+				// Step camera rotation by rotation speed
+				if (this._attachedCamera) {
+					this._attachedCamera.alpha -= this._cameraRotationSpeed * (dt / 1000);
+				}
             });
         }
              
         public detach(): void {
+			if (!this._attachedCamera) {
+				return;
+			}
             let scene = this._attachedCamera.getScene();
             
             scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
@@ -129,11 +134,18 @@ module BABYLON {
 		 * @return true if user is scrolling.
 		 */
 		private _userIsZooming(): boolean {
+			if (!this._attachedCamera) {
+				return false;
+			}			
 			return this._attachedCamera.inertialRadiusOffset !== 0;
 		}   		
 		
 		private _lastFrameRadius = 0;
 		private _shouldAnimationStopForInteraction(): boolean {
+			if (!this._attachedCamera) {
+				return false;
+			}	
+
 			var zoomHasHitLimit = false;
 			if (this._lastFrameRadius === this._attachedCamera.radius && this._attachedCamera.inertialRadiusOffset !== 0) {
 				zoomHasHitLimit = true;
@@ -155,6 +167,10 @@ module BABYLON {
    
         // Tools
         private _userIsMoving(): boolean {
+			if (!this._attachedCamera) {
+				return false;
+			}	
+			
 			return this._attachedCamera.inertialAlphaOffset !== 0 ||
 				this._attachedCamera.inertialBetaOffset !== 0 ||
 				this._attachedCamera.inertialRadiusOffset !== 0 ||

+ 33 - 6
src/Behaviors/Cameras/babylon.bouncingBehavior.ts

@@ -53,6 +53,9 @@ module BABYLON {
 			this._autoTransitionRange = value;
 
 			let camera = this._attachedCamera;
+			if (!camera) {
+				return;
+			}
 
 			if (value) {
 				this._onMeshTargetChangedObserver = camera.onMeshTargetChangedObservable.add((mesh) => {
@@ -72,12 +75,16 @@ module BABYLON {
 		}
         
         // Connection
-        private _attachedCamera: ArcRotateCamera;
+        private _attachedCamera: Nullable<ArcRotateCamera>;
 		private _onAfterCheckInputsObserver: Observer<Camera>;	
 		private _onMeshTargetChangedObserver: Observer<AbstractMesh>;
         public attach(camera: ArcRotateCamera): void {
             this._attachedCamera = camera;
             this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
+				if (!this._attachedCamera) {
+					return;
+				}
+
 				// Add the bounce animation to the lower radius limit
 				if (this._isRadiusAtLimit(this._attachedCamera.lowerRadiusLimit)) {
 					this._applyBoundRadiusAnimation(this.lowerRadiusTransitionRange);
@@ -91,6 +98,9 @@ module BABYLON {
         }
         
         public detach(): void {
+			if (!this._attachedCamera) {
+				return;
+			}			
 			this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
 			if (this._onMeshTargetChangedObserver) {
 				this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
@@ -100,7 +110,7 @@ module BABYLON {
 
         // Animations
         private _radiusIsAnimating: boolean = false;
-        private _radiusBounceTransition: Animation = null;
+        private _radiusBounceTransition: Nullable<Animation> = null;
         private _animatables = new Array<BABYLON.Animatable>();
         private _cachedWheelPrecision: number;
 
@@ -110,6 +120,10 @@ module BABYLON {
 		 * @return Bool to indicate if at limit.
 		 */
 		private _isRadiusAtLimit(radiusLimit: number): boolean {
+			if (!this._attachedCamera) {
+				return false;
+			}
+
 			if (this._attachedCamera.radius === radiusLimit && !this._radiusIsAnimating) {
 				return true;
 			}
@@ -121,6 +135,10 @@ module BABYLON {
 		 * @param radiusDelta The delta by which to animate to. Can be negative.
 		 */
 		private _applyBoundRadiusAnimation(radiusDelta: number): void {
+			if (!this._attachedCamera) {
+				return;
+			}
+
 			if (!this._radiusBounceTransition) {
 				BouncingBehavior.EasingFunction.setEasingMode(BouncingBehavior.EasingMode);
 				this._radiusBounceTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, BouncingBehavior.EasingFunction);
@@ -133,8 +151,12 @@ module BABYLON {
 			// 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()));
+			let animatable = Animation.TransitionTo("radius", this._attachedCamera.radius + radiusDelta, this._attachedCamera, this._attachedCamera.getScene(), 60, 
+			this._radiusBounceTransition, this.transitionDuration, () => this._clearAnimationLocks());
+
+			if (animatable) {
+				this._animatables.push(animatable);
+			}
         }
 
         /**
@@ -142,14 +164,19 @@ module BABYLON {
 		 */
 		protected _clearAnimationLocks(): void {
 			this._radiusIsAnimating = false;
-			this._attachedCamera.wheelPrecision = this._cachedWheelPrecision;
+
+			if (this._attachedCamera) {
+				this._attachedCamera.wheelPrecision = this._cachedWheelPrecision;
+			}			
 		}        
         
 		/**
 		 * Stops and removes all animations that have been applied to the camera
 		 */        
         public stopAllAnimations(): void {
-			this._attachedCamera.animations = [];
+			if (this._attachedCamera) {
+				this._attachedCamera.animations = [];
+			}
 			while (this._animatables.length) {
 				this._animatables[0].onAnimationEnd = null;
 				this._animatables[0].stop();

+ 57 - 21
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -143,7 +143,7 @@ module BABYLON {
         private _onPrePointerObservableObserver: Observer<PointerInfoPre>;
 		private _onAfterCheckInputsObserver: Observer<Camera>;
 		private _onMeshTargetChangedObserver: Observer<AbstractMesh>;
-        private _attachedCamera: ArcRotateCamera;
+        private _attachedCamera: Nullable<ArcRotateCamera>;
         private _isPointerDown = false;
         private _lastInteractionTime = -Infinity;
 
@@ -181,6 +181,10 @@ module BABYLON {
         }
              
         public detach(): void {
+			if (!this._attachedCamera) {
+				return;
+			}
+
             let scene = this._attachedCamera.getScene();
             
             scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
@@ -205,7 +209,7 @@ module BABYLON {
 		 * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
 		 * @param onAnimationEnd Callback triggered at the end of the framing animation
 		 */
-		public zoomOnMesh(mesh: AbstractMesh, focusOnOriginXZ: boolean = false, onAnimationEnd: () => void = null): void {
+		public zoomOnMesh(mesh: AbstractMesh, focusOnOriginXZ: boolean = false, onAnimationEnd: Nullable<() => void> = null): void {
 			mesh.computeWorldMatrix(true);
 
 			let boundingBox = mesh.getBoundingInfo().boundingBox;
@@ -220,9 +224,13 @@ module BABYLON {
 		 * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
 		 * @param onAnimationEnd Callback triggered at the end of the framing animation
 		 */
-		public zoomOnBoundingInfo(minimumWorld: Vector3, maximumWorld: Vector3, focusOnOriginXZ: boolean = false, onAnimationEnd: () => void = null): void {
+		public zoomOnBoundingInfo(minimumWorld: Vector3, maximumWorld: Vector3, focusOnOriginXZ: boolean = false, onAnimationEnd: Nullable<() => void> = null): void {
 			let zoomTarget: BABYLON.Vector3;
 
+			if (!this._attachedCamera) {
+				return;
+			}
+
 			// Find target by interpolating from bottom of bounding box in world-space to top via framingPositionY
 			let bottom = minimumWorld.y;
 			let top = maximumWorld.y;
@@ -241,8 +249,10 @@ module BABYLON {
 			}			
 
 			this._betaIsAnimating = true;
-			this._animatables.push(Animation.TransitionTo("target", zoomTarget, this._attachedCamera, this._attachedCamera.getScene(), 
-									60, this._vectorTransition, this._framingTime));
+			let animatable = Animation.TransitionTo("target", zoomTarget, this._attachedCamera, this._attachedCamera.getScene(), 60, this._vectorTransition, this._framingTime);
+			if (animatable) {
+				this._animatables.push(animatable);
+			}
 
 			// sets the radius and lower radius bounds
 			// Small delta ensures camera is not always at lower zoom limit.
@@ -268,14 +278,20 @@ module BABYLON {
 				this._radiusTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);
 			}
 
-			this._animatables.push(Animation.TransitionTo("radius", radius, this._attachedCamera, this._attachedCamera.getScene(), 
-				60, this._radiusTransition, this._framingTime, () => {
-					if (onAnimationEnd) {
-						onAnimationEnd();
-					}
+			animatable = Animation.TransitionTo("radius", radius, this._attachedCamera, this._attachedCamera.getScene(), 
+			60, this._radiusTransition, this._framingTime, () => {
+				if (onAnimationEnd) {
+					onAnimationEnd();
+				}
 
+				if (this._attachedCamera) {
 					this._attachedCamera.storeState();
-				}));
+				}
+			});
+
+			if (animatable) {
+				this._animatables.push(animatable);
+			}
 		}
 		
 		/**
@@ -301,6 +317,10 @@ module BABYLON {
 			let distance = Math.max(distanceForHorizontalFrustum, distanceForVerticalFrustum);
 			let camera = this._attachedCamera;
 
+			if (!camera) {
+				return 0;
+			}
+
 			if (camera.lowerRadiusLimit && this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
 				// Don't exceed the requested limit
 				distance = distance < camera.lowerRadiusLimit ? camera.lowerRadiusLimit : distance;
@@ -328,7 +348,7 @@ module BABYLON {
 			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) {
+			if (this._attachedCamera && !this._betaIsAnimating && this._attachedCamera.beta > limitBeta && timeSinceInteraction >= this._elevationReturnWaitTime) {
                 this._betaIsAnimating = true;
                 
 				//Transition to new position
@@ -336,14 +356,18 @@ module BABYLON {
                 
                 if (!this._betaTransition) {
                     this._betaTransition = Animation.CreateAnimation("beta", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);
-                }
-
-				this._animatables.push(Animation.TransitionTo("beta", defaultBeta, this._attachedCamera, this._attachedCamera.getScene(), 60,
-                    this._betaTransition, this._elevationReturnTime, 
-                    () => {
-						this._clearAnimationLocks();
-						this.stopAllAnimations();
-					}));
+				}
+				
+				let animatabe = Animation.TransitionTo("beta", defaultBeta, this._attachedCamera, this._attachedCamera.getScene(), 60,
+				this._betaTransition, this._elevationReturnTime, 
+				() => {
+					this._clearAnimationLocks();
+					this.stopAllAnimations();
+				});
+
+				if (animatabe) {
+					this._animatables.push(animatabe);
+				}
 			}
 		}        
 
@@ -355,6 +379,11 @@ module BABYLON {
 			// Calculate the viewport ratio
 			// Aspect Ratio is Height/Width.
 			let camera = this._attachedCamera;
+
+			if (!camera) {
+				return Vector2.Zero();
+			}
+
 			let engine = camera.getScene().getEngine();
 			var aspectRatio = engine.getAspectRatio(camera);
 
@@ -392,7 +421,10 @@ module BABYLON {
 		 * Stops and removes all animations that have been applied to the camera
 		 */        
         public stopAllAnimations(): void {
-			this._attachedCamera.animations = [];
+			if (this._attachedCamera) {
+				this._attachedCamera.animations = [];
+			}
+
 			while (this._animatables.length) {
 				if (this._animatables[0]) {
 					this._animatables[0].onAnimationEnd = null;
@@ -406,6 +438,10 @@ module BABYLON {
 		 * Gets a value indicating if the user is moving the camera
 		 */
         public get isUserIsMoving(): boolean {
+			if (!this._attachedCamera) {
+				return false;
+			}
+			
 			return this._attachedCamera.inertialAlphaOffset !== 0 ||
 				this._attachedCamera.inertialBetaOffset !== 0 ||
 				this._attachedCamera.inertialRadiusOffset !== 0 ||

+ 53 - 62
src/Bones/babylon.bone.ts

@@ -13,7 +13,7 @@ module BABYLON {
 
         // Set this value to map this bone to a different index in the transform matrices.
         // Set this value to -1 to exclude the bone from the transform matrices.
-        public _index: number;
+        public _index: Nullable<number> = null;
 
         private _skeleton: Skeleton;
         private _localMatrix: Matrix;
@@ -22,7 +22,7 @@ module BABYLON {
         private _worldTransform = new Matrix();
         private _absoluteTransform = new Matrix();
         private _invertedAbsoluteTransform = new Matrix();
-        private _parent: Bone;
+        private _parent: Nullable<Bone>;
 
         private _scaleMatrix = Matrix.Identity();
         private _scaleVector = Vector3.One();
@@ -41,7 +41,8 @@ module BABYLON {
             }
         }
 
-        constructor(public name: string, skeleton: Skeleton, parentBone: Bone = null, localMatrix?: Matrix, restPose?: Matrix, baseMatrix?: Matrix, index?: number) {
+        constructor(public name: string, skeleton: Skeleton, parentBone: Nullable<Bone> = null, localMatrix: Nullable<Matrix> = null, 
+                    restPose: Nullable<Matrix> = null, baseMatrix: Nullable<Matrix> = null, index: Nullable<number> = null) {
             super(name, skeleton.getScene());
             this._skeleton = skeleton;
             this._localMatrix = localMatrix ? localMatrix : Matrix.Identity();
@@ -61,11 +62,11 @@ module BABYLON {
             return this._skeleton;
         }
 
-        public getParent(): Bone {
+        public getParent(): Nullable<Bone> {
             return this._parent;
         }
 
-        public setParent(parent: Bone, updateDifferenceMatrix: boolean = true): void {
+        public setParent(parent: Nullable<Bone>, updateDifferenceMatrix: boolean = true): void {
             if (this._parent === parent) {
                 return;
             }
@@ -186,7 +187,7 @@ module BABYLON {
             this._skeleton._markAsDirty();
         }
 
-        public copyAnimationRange(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired = false, skelDimensionsRatio : Vector3 = null): boolean {
+        public copyAnimationRange(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired = false, skelDimensionsRatio: Nullable<Vector3> = null): boolean {
             // all animation may be coming from a library skeleton, so may need to create animation
             if (this.animations.length === 0) {
                 this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
@@ -207,7 +208,7 @@ module BABYLON {
             var sourceParent = source.getParent();
             var parent = this.getParent();
             var parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
-            var parentRatio = parentScalingReqd ? parent.length / sourceParent.length : null;
+            var parentRatio = parentScalingReqd && parent && sourceParent ? parent.length / sourceParent.length : 1;
             
             var dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);           
             
@@ -230,7 +231,7 @@ module BABYLON {
                             mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
                             
                         // scale based on skeleton dimension ratio when root bone, and value is passed
-                        } else if (dimensionsScalingReqd) {
+                        } else if (dimensionsScalingReqd && skelDimensionsRatio) {
                             origTranslation = mat.getTranslation();
                             mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));                            
 
@@ -255,21 +256,17 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          */
         public translate(vec: Vector3, space = Space.LOCAL, mesh?: AbstractMesh): void {
-
             var lm = this.getLocalMatrix();
 
             if(space == Space.LOCAL){
-
                 lm.m[12] += vec.x;
                 lm.m[13] += vec.y;
                 lm.m[14] += vec.z;
-
-            }else{
-
-                var wm:Matrix;
+            } else {
+                var wm: Nullable<Matrix> = null;
 
                 //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
-                if(mesh){
+                if (mesh){
                     wm = mesh.getWorldMatrix();
                 }
 
@@ -277,11 +274,13 @@ module BABYLON {
                 var tmat = Bone._tmpMats[0];
                 var tvec = Bone._tmpVecs[0];
 
-                if (mesh) {
-                    tmat.copyFrom(this._parent.getAbsoluteTransform());
-                    tmat.multiplyToRef(wm, tmat);
-                }else {
-                    tmat.copyFrom(this._parent.getAbsoluteTransform());
+                if (this._parent) {
+                    if (mesh && wm) {
+                        tmat.copyFrom(this._parent.getAbsoluteTransform());
+                        tmat.multiplyToRef(wm, tmat);
+                    } else {
+                        tmat.copyFrom(this._parent.getAbsoluteTransform());
+                    }
                 }
 
                 tmat.m[12] = 0;
@@ -311,15 +310,12 @@ module BABYLON {
 
             var lm = this.getLocalMatrix();
 
-            if(space == Space.LOCAL){
-
+            if (space == Space.LOCAL) {
                 lm.m[12] = position.x;
                 lm.m[13] = position.y;
                 lm.m[14] = position.z;
-
-            }else{
-
-                var wm:Matrix;
+            } else {
+                var wm: Nullable<Matrix> = null;
 
                 //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
                 if(mesh){
@@ -331,11 +327,13 @@ module BABYLON {
                 var tmat = Bone._tmpMats[0];
                 var vec = Bone._tmpVecs[0];
 
-                if (mesh) {
-                    tmat.copyFrom(this._parent.getAbsoluteTransform());
-                    tmat.multiplyToRef(wm, tmat);
-                }else {
-                    tmat.copyFrom(this._parent.getAbsoluteTransform());
+                if (this._parent) {
+                    if (mesh && wm) {
+                        tmat.copyFrom(this._parent.getAbsoluteTransform());
+                        tmat.multiplyToRef(wm, tmat);
+                    } else {
+                        tmat.copyFrom(this._parent.getAbsoluteTransform());
+                    }
                 }
 
                 tmat.invert();
@@ -675,14 +673,12 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          * @returns The position of the bone
          */
-        public getPosition(space = Space.LOCAL, mesh?: AbstractMesh): Vector3 {
-
+        public getPosition(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null): Vector3 {
             var pos = Vector3.Zero();
 
             this.getPositionToRef(space, mesh, pos);
 
             return pos;
-
         }
 
         /**
@@ -691,22 +687,19 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          * @param result The vector3 to copy the position to.
          */
-        public getPositionToRef(space = Space.LOCAL, mesh: AbstractMesh, result: Vector3): void {
-
-            if(space == Space.LOCAL){
+        public getPositionToRef(space = Space.LOCAL, mesh: Nullable<AbstractMesh>, result: Vector3): void {
 
+            if (space == Space.LOCAL){
                 var lm = this.getLocalMatrix();
 
                 result.x = lm.m[12];
                 result.y = lm.m[13];
                 result.z = lm.m[14];
-
-            }else{
-                
-                var wm;
+            } else {               
+                var wm: Nullable<Matrix> = null;
                 
                 //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
-                if(mesh){
+                if (mesh){
                     wm = mesh.getWorldMatrix();
                 }
                 
@@ -714,7 +707,7 @@ module BABYLON {
                 
                 var tmat = Bone._tmpMats[0];
 
-                if (mesh) {
+                if (mesh && wm) {
                     tmat.copyFrom(this.getAbsoluteTransform());
                     tmat.multiplyToRef(wm, tmat);
                 }else{
@@ -724,9 +717,7 @@ module BABYLON {
                 result.x = tmat.m[12];
                 result.y = tmat.m[13];
                 result.z = tmat.m[14];
-
             }
-
         }
 
         /**
@@ -734,7 +725,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @returns The absolute position of the bone
          */
-        public getAbsolutePosition(mesh?: AbstractMesh): Vector3 {
+        public getAbsolutePosition(mesh: Nullable<AbstractMesh> = null): Vector3 {
 
             var pos = Vector3.Zero();
 
@@ -813,7 +804,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @returns The world direction
          */
-        public getDirection(localAxis: Vector3, mesh?: AbstractMesh): Vector3{
+        public getDirection(localAxis: Vector3, mesh: Nullable<AbstractMesh> = null): Vector3{
 
             var result = Vector3.Zero();
 
@@ -829,9 +820,9 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @param result The vector3 that the world direction will be copied to.
          */
-        public getDirectionToRef(localAxis: Vector3, mesh: AbstractMesh, result: Vector3): void {
+        public getDirectionToRef(localAxis: Vector3, mesh: Nullable<AbstractMesh> = null, result: Vector3): void {
 
-            var wm:Matrix;
+            var wm: Nullable<Matrix> = null;
 
             //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
             if(mesh){
@@ -844,7 +835,7 @@ module BABYLON {
 
             mat.copyFrom(this.getAbsoluteTransform());
 
-            if(mesh){
+            if (mesh && wm) {
                 mat.multiplyToRef(wm, mat);
             }
 
@@ -860,7 +851,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          * @returns The euler rotation
          */
-        public getRotation(space = Space.LOCAL, mesh?: AbstractMesh): Vector3 {
+        public getRotation(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null): Vector3 {
 
             var result = Vector3.Zero();
 
@@ -876,7 +867,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          * @param result The vector3 that the rotation should be copied to.
          */
-        public getRotationToRef(space = Space.LOCAL, mesh: AbstractMesh, result: Vector3): void {
+        public getRotationToRef(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null, result: Vector3): void {
 
             var quat = Bone._tmpQuat;
 
@@ -892,7 +883,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          * @returns The quaternion rotation
          */
-        public getRotationQuaternion(space = Space.LOCAL, mesh?: AbstractMesh): Quaternion {
+        public getRotationQuaternion(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null): Quaternion {
 
             var result = Quaternion.Identity();
 
@@ -908,7 +899,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.  This is only used in world space.
          * @param result The quaternion that the rotation should be copied to.
          */
-        public getRotationQuaternionToRef(space = Space.LOCAL, mesh: AbstractMesh, result: Quaternion): void{
+        public getRotationQuaternionToRef(space = Space.LOCAL, mesh: Nullable<AbstractMesh> = null, result: Quaternion): void{
 
             if(space == Space.LOCAL){
 
@@ -989,7 +980,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @returns The world position
          */
-        public getAbsolutePositionFromLocal(position:Vector3, mesh?:AbstractMesh): Vector3{
+        public getAbsolutePositionFromLocal(position:Vector3, mesh: Nullable<AbstractMesh> = null): Vector3{
 
             var result = Vector3.Zero();
 
@@ -1005,9 +996,9 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @param result The vector3 that the world position should be copied to.
          */
-        public getAbsolutePositionFromLocalToRef(position:Vector3, mesh:AbstractMesh, result:Vector3): void{
+        public getAbsolutePositionFromLocalToRef(position:Vector3, mesh: Nullable<AbstractMesh> = null, result:Vector3): void{
 
-            var wm:Matrix;
+            var wm: Nullable<Matrix> = null;
 
             //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
             if(mesh){
@@ -1018,7 +1009,7 @@ module BABYLON {
 
             var tmat = Bone._tmpMats[0];
             
-            if (mesh) {
+            if (mesh && wm) {
                 tmat.copyFrom(this.getAbsoluteTransform());
                 tmat.multiplyToRef(wm, tmat);
             }else{
@@ -1035,7 +1026,7 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @returns The local position
          */
-        public getLocalPositionFromAbsolute(position:Vector3, mesh?:AbstractMesh): Vector3{
+        public getLocalPositionFromAbsolute(position:Vector3, mesh: Nullable<AbstractMesh> = null): Vector3{
 
             var result = Vector3.Zero();
 
@@ -1051,9 +1042,9 @@ module BABYLON {
          * @param mesh The mesh that this bone is attached to.
          * @param result The vector3 that the local position should be copied to.
          */
-        public getLocalPositionFromAbsoluteToRef(position:Vector3, mesh:AbstractMesh, result:Vector3): void{
+        public getLocalPositionFromAbsoluteToRef(position:Vector3, mesh: Nullable<AbstractMesh> = null, result:Vector3): void{
 
-            var wm:Matrix;
+            var wm: Nullable<Matrix> = null;
 
             //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
             if(mesh){
@@ -1066,7 +1057,7 @@ module BABYLON {
 
             tmat.copyFrom(this.getAbsoluteTransform());
             
-            if (mesh) {
+            if (mesh && wm) {
                 tmat.multiplyToRef(wm, tmat);
             }
 

+ 28 - 22
src/Bones/babylon.boneIKController.ts

@@ -7,7 +7,7 @@ module BABYLON {
         
         public targetMesh: AbstractMesh;
         public poleTargetMesh: AbstractMesh;
-        public poleTargetBone: Bone;
+        public poleTargetBone: Nullable<Bone>;
         public targetPosition = Vector3.Zero();
         public poleTargetPosition = Vector3.Zero();
         public poleTargetLocalOffset = Vector3.Zero();
@@ -19,7 +19,7 @@ module BABYLON {
         private _bone1Mat = Matrix.Identity();
         private _bone2Ang = Math.PI;
 
-        private _bone1: Bone;
+        private _bone1: Nullable<Bone>;
         private _bone2: Bone;
         private _bone1Length: number;
         private _bone2Length: number;
@@ -60,6 +60,10 @@ module BABYLON {
 
             this._bone2 = bone;
             this._bone1 = bone.getParent();
+
+            if (!this._bone1) {
+                return;
+            }
             
             this.mesh = mesh;
 
@@ -76,7 +80,7 @@ module BABYLON {
                     this._bendAxis.z = 1;
                 }
             }
-
+            
             if (this._bone1.length) {
 
                 var boneScale1 = this._bone1.getScale();
@@ -95,7 +99,6 @@ module BABYLON {
 
                 this._bone1Length = Vector3.Distance(pos1, pos2);
                 this._bone2Length = Vector3.Distance(pos2, pos3);
-
             }
 
             this._bone1.getRotationMatrixToRef(Space.WORLD, mesh, this._bone1Mat);
@@ -113,14 +116,10 @@ module BABYLON {
                     this.poleTargetMesh = options.poleTargetMesh;
                     this.poleTargetMesh.computeWorldMatrix(true);
 
-                }else if(options.poleTargetBone){
-
+                } else if(options.poleTargetBone){
                     this.poleTargetBone = options.poleTargetBone;
-
-                }else if(this._bone1.getParent()){
-
+                } else if(this._bone1.getParent()){
                     this.poleTargetBone = this._bone1.getParent();
-
                 }
 
                 if(options.poleTargetLocalOffset){
@@ -169,6 +168,11 @@ module BABYLON {
         public update(): void {
 	
             var bone1 = this._bone1;
+
+            if (!bone1) {
+                return;
+            }
+
             var target = this.targetPosition;
             var poleTarget = this.poleTargetPosition;
 
@@ -272,19 +276,21 @@ module BABYLON {
                 mat1.multiplyToRef(mat2, mat1);
             }
 
-            if(this.slerpAmount < 1){
-                if(!this._slerping){
-                    Quaternion.FromRotationMatrixToRef(this._bone1Mat, this._bone1Quat);
+            if (this._bone1) {
+                if(this.slerpAmount < 1){
+                    if(!this._slerping){
+                        Quaternion.FromRotationMatrixToRef(this._bone1Mat, this._bone1Quat);
+                    }
+                    Quaternion.FromRotationMatrixToRef(mat1, _tmpQuat);
+                    Quaternion.SlerpToRef(this._bone1Quat, _tmpQuat, this.slerpAmount, this._bone1Quat);
+                    angC = this._bone2Ang * (1.0 - this.slerpAmount) + angC * this.slerpAmount;
+                    this._bone1.setRotationQuaternion(this._bone1Quat, Space.WORLD, this.mesh);
+                    this._slerping = true;
+                } else {
+                    this._bone1.setRotationMatrix(mat1, Space.WORLD, this.mesh);
+                    this._bone1Mat.copyFrom(mat1);
+                    this._slerping = false;
                 }
-                Quaternion.FromRotationMatrixToRef(mat1, _tmpQuat);
-                Quaternion.SlerpToRef(this._bone1Quat, _tmpQuat, this.slerpAmount, this._bone1Quat);
-                angC = this._bone2Ang * (1.0 - this.slerpAmount) + angC * this.slerpAmount;
-                this._bone1.setRotationQuaternion(this._bone1Quat, Space.WORLD, this.mesh);
-                this._slerping = true;
-            } else {
-                this._bone1.setRotationMatrix(mat1, Space.WORLD, this.mesh);
-                this._bone1Mat.copyFrom(mat1);
-                this._slerping = false;
             }
 
             this._bone2.setAxisAngle(this._bendAxis, angC, Space.LOCAL);

+ 4 - 4
src/Bones/babylon.boneLookController.ts

@@ -277,7 +277,7 @@ module BABYLON {
             var upAxis = BoneLookController._tmpVecs[1];
             upAxis.copyFrom(this.upAxis);
 
-            if(this.upAxisSpace == Space.BONE){
+            if(this.upAxisSpace == Space.BONE && parentBone){
                 if (this._transformYawPitch){
                     Vector3.TransformCoordinatesToRef(upAxis, this._transformYawPitchInv, upAxis);
                 }
@@ -304,7 +304,7 @@ module BABYLON {
                 var spaceMat = BoneLookController._tmpMats[2];
                 var spaceMatInv = BoneLookController._tmpMats[3];
 
-                if(this.upAxisSpace == Space.BONE && upAxis.y == 1){
+                if(this.upAxisSpace == Space.BONE && upAxis.y == 1 && parentBone){
 
                     parentBone.getRotationMatrixToRef(Space.WORLD, this.mesh, spaceMat);
                     
@@ -337,14 +337,14 @@ module BABYLON {
 
                 spaceMat.invertToRef(spaceMatInv);
                 
-                var xzlen:number;
+                var xzlen: Nullable<number> = null;
 
                 if(checkPitch){
                     var localTarget = BoneLookController._tmpVecs[3];
                     target.subtractToRef(bonePos, localTarget);
                     Vector3.TransformCoordinatesToRef(localTarget, spaceMatInv, localTarget);
 
-                    var xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
+                    xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
                     var pitch = Math.atan2(localTarget.y, xzlen);
                     var newPitch = pitch;
 

+ 32 - 20
src/Bones/babylon.skeleton.ts

@@ -14,7 +14,7 @@
         private _identity = Matrix.Identity();
         private _synchronizedWithMesh: AbstractMesh;
 
-        private _ranges: { [name: string]: AnimationRange; } = {};
+        private _ranges: { [name: string]: Nullable<AnimationRange> } = {};
 
         private _lastAbsoluteTransformsUpdateId = -1;
 
@@ -108,18 +108,18 @@
                     this.bones[i].animations[0].deleteRange(name, deleteFrames);
                 }
             }
-            this._ranges[name] = undefined; // said much faster than 'delete this._range[name]' 
+            this._ranges[name] = null; // said much faster than 'delete this._range[name]' 
         }
 
-        public getAnimationRange(name: string): AnimationRange {
+        public getAnimationRange(name: string): Nullable<AnimationRange> {
             return this._ranges[name];
         }
 
         /**
          *  Returns as an Array, all AnimationRanges defined on this skeleton
          */
-        public getAnimationRanges(): AnimationRange[] {
-            var animationRanges: AnimationRange[] = [];
+        public getAnimationRanges(): Nullable<AnimationRange>[] {
+            var animationRanges: Nullable<AnimationRange>[] = [];
             var name: string;
             var i: number = 0;
             for (name in this._ranges) {
@@ -167,7 +167,9 @@
             }
             // do not call createAnimationRange(), since it also is done to bones, which was already done
             var range = source.getAnimationRange(name);
-            this._ranges[name] = new AnimationRange(name, range.from + frameOffset, range.to + frameOffset);
+            if (range) {
+                this._ranges[name] = new AnimationRange(name, range.from + frameOffset, range.to + frameOffset);
+            }
             return ret;
         }
 
@@ -190,7 +192,7 @@
             return ret;
         }
 
-        public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Animatable {
+        public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Nullable<Animatable> {
             var range = this.getAnimationRange(name);
 
             if (!range) {
@@ -216,7 +218,7 @@
             }
         }
 
-        public _computeTransformMatrices(targetMatrix: Float32Array, initialSkinMatrix: Matrix): void {
+        public _computeTransformMatrices(targetMatrix: Float32Array, initialSkinMatrix: Nullable<Matrix>): void {
 
             this.onBeforeComputeObservable.notifyObservers(this);
 
@@ -235,7 +237,7 @@
                 }
 
                 if (bone._index !== -1) {
-                    var mappedIndex = bone._index === undefined ? index : bone._index;
+                    var mappedIndex = bone._index === null ? index : bone._index;
                     bone.getInvertedAbsoluteTransform().multiplyToArray(bone.getWorldMatrix(), targetMatrix, mappedIndex * 16);
                 }
             }
@@ -309,8 +311,9 @@
                 var source = this.bones[index];
                 var parentBone = null;
 
-                if (source.getParent()) {
-                    var parentIndex = this.bones.indexOf(source.getParent());
+                let parent = source.getParent();
+                if (parent) {
+                    var parentIndex = this.bones.indexOf(parent);
                     parentBone = result.bones[parentIndex];
                 }
 
@@ -321,7 +324,11 @@
             if (this._ranges) {
                 result._ranges = {};
                 for (var rangeName in this._ranges) {
-                    result._ranges[rangeName] = this._ranges[rangeName].clone();
+                    let range = this._ranges[rangeName];
+
+                    if (range) {
+                        result._ranges[rangeName] = range.clone();
+                    }
                 }
             }
 
@@ -362,9 +369,10 @@
 
             for (var index = 0; index < this.bones.length; index++) {
                 var bone = this.bones[index];
+                let parent = bone.getParent();
 
                 var serializedBone: any = {
-                    parentBoneIndex: bone.getParent() ? this.bones.indexOf(bone.getParent()) : -1,
+                    parentBoneIndex: parent ? this.bones.indexOf(parent) : -1,
                     name: bone.name,
                     matrix: bone.getBaseMatrix().toArray(),
                     rest: bone.getRestPose().toArray()
@@ -382,10 +390,16 @@
 
                 serializationObject.ranges = [];
                 for (var name in this._ranges) {
+                    let source = this._ranges[name];
+
+                    if (!source) {
+                        continue;
+                    }
+
                     var range: any = {};
                     range.name = name;
-                    range.from = this._ranges[name].from;
-                    range.to = this._ranges[name].to;
+                    range.from = source.from;
+                    range.to = source.to;
                     serializationObject.ranges.push(range);
                 }
             }
@@ -408,7 +422,7 @@
                 if (parsedBone.parentBoneIndex > -1) {
                     parentBone = skeleton.bones[parsedBone.parentBoneIndex];
                 }
-                var rest: Matrix = parsedBone.rest ? Matrix.FromArray(parsedBone.rest) : null;
+                var rest: Nullable<Matrix> = parsedBone.rest ? Matrix.FromArray(parsedBone.rest) : null;
                 var bone = new Bone(parsedBone.name, skeleton, parentBone, Matrix.FromArray(parsedBone.matrix), rest);
 
                 if (parsedBone.length) {
@@ -441,16 +455,14 @@
             
         }
 
-        public getPoseMatrix(): Matrix {
-            
-            var poseMatrix: Matrix;
+        public getPoseMatrix(): Nullable<Matrix> {        
+            var poseMatrix: Nullable<Matrix> = null;
             
             if(this._meshesWithPoseMatrix.length > 0){
                 poseMatrix = this._meshesWithPoseMatrix[0].getPoseMatrix();
             }
 
             return poseMatrix;
-
         }
 
         public sortBones(): void {

+ 1 - 1
src/Cameras/Inputs/babylon.arcRotateCameraGamepadInput.ts

@@ -2,7 +2,7 @@ module BABYLON {
     export class ArcRotateCameraGamepadInput implements ICameraInput<ArcRotateCamera> {
         camera: ArcRotateCamera;
 
-        public gamepad: Gamepad;
+        public gamepad: Nullable<Gamepad>;
         private _onGamepadConnectedObserver : Observer<Gamepad>;
         private _onGamepadDisconnectedObserver : Observer<Gamepad>;
 

+ 8 - 4
src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts

@@ -29,8 +29,8 @@ module BABYLON {
 
         private _ctrlPressed: boolean;
         private _altPressed: boolean;
-        private _onCanvasBlurObserver: Observer<Engine>;
-        private _onKeyboardObserver: Observer<KeyboardInfo>;
+        private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
+        private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
         private _engine: Engine;
         private _scene: Scene;
 
@@ -95,8 +95,12 @@ module BABYLON {
 
         public detachControl(element: HTMLElement) {
             if (this._scene) {
-                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
-                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                if (this._onKeyboardObserver) {
+                    this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                }
+                if (this._onCanvasBlurObserver) {
+                    this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                }
                 this._onKeyboardObserver = null;
                 this._onCanvasBlurObserver = null;
             }

+ 2 - 2
src/Cameras/Inputs/babylon.arcRotateCameraMouseWheelInput.ts

@@ -2,8 +2,8 @@ module BABYLON {
     export class ArcRotateCameraMouseWheelInput implements ICameraInput<ArcRotateCamera> {
         camera: ArcRotateCamera;
 
-        private _wheel: (p: PointerInfo, s: EventState) => void;
-        private _observer: Observer<PointerInfo>;
+        private _wheel: Nullable<(p: PointerInfo, s: EventState) => void>;
+        private _observer: Nullable<Observer<PointerInfo>>;
 
         @serialize()
         public wheelPrecision = 3.0;

+ 43 - 29
src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts

@@ -34,18 +34,19 @@ module BABYLON {
         public pinchInwards = true;
 
         private _pointerInput: (p: PointerInfo, s: EventState) => void;
-        private _observer: Observer<PointerInfo>;
-        private _onMouseMove: (e: MouseEvent) => any;
-        private _onGestureStart: (e: PointerEvent) => void;
-        private _onGesture: (e: MSGestureEvent) => void;
-        private _MSGestureHandler: MSGesture;
-        private _onLostFocus: (e: FocusEvent) => any;
-        private _onContextMenu: (e: PointerEvent) => void;
+        private _observer: Nullable<Observer<PointerInfo>>;
+        private _onMouseMove: Nullable<(e: MouseEvent) => any>;
+        private _onGestureStart: Nullable<(e: PointerEvent) => void>;
+        private _onGesture: Nullable<(e: MSGestureEvent) => void>;
+        private _MSGestureHandler: Nullable<MSGesture>;
+        private _onLostFocus: Nullable<(e: FocusEvent) => any>;
+        private _onContextMenu: Nullable<(e: PointerEvent) => void>;
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             var engine = this.camera.getEngine();
-            var cacheSoloPointer: { x: number, y: number, pointerId: number, type: any }; // cache pointer object for better perf on camera rotation
-            var pointA: { x: number, y: number, pointerId: number, type: any }, pointB: { x: number, y: number, pointerId: number, type: any };
+            var cacheSoloPointer: Nullable<{ x: number, y: number, pointerId: number, type: any }>; // cache pointer object for better perf on camera rotation
+            var pointA: Nullable<{ x: number, y: number, pointerId: number, type: any }> = null;
+            var pointB: Nullable<{ x: number, y: number, pointerId: number, type: any }> = null;
             var previousPinchSquaredDistance = 0;
             var initialDistance = 0;
             var twoFingerActivityCount = 0;
@@ -62,7 +63,7 @@ module BABYLON {
                     return;
                 }
 
-                if (p.type === PointerEventTypes.POINTERDOWN) {
+                if (p.type === PointerEventTypes.POINTERDOWN && evt.srcElement) {
                     try {
                         evt.srcElement.setPointerCapture(evt.pointerId);
                     } catch (e) {
@@ -74,10 +75,10 @@ module BABYLON {
 
                     // manage pointers
                     cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
-                    if (pointA === undefined) {
+                    if (pointA === null) {
                         pointA = cacheSoloPointer;
                     }
-                    else if (pointB === undefined) {
+                    else if (pointB === null) {
                         pointB = cacheSoloPointer;
                     }
                     if (!noPreventDefault) {
@@ -88,7 +89,7 @@ module BABYLON {
                 else if (p.type === PointerEventTypes.POINTERDOUBLETAP) {
                     this.camera.restoreState();
                 }
-                else if (p.type === PointerEventTypes.POINTERUP) {
+                else if (p.type === PointerEventTypes.POINTERUP && evt.srcElement) {
                     try {
                         evt.srcElement.releasePointerCapture(evt.pointerId);
                     } catch (e) {
@@ -103,7 +104,7 @@ module BABYLON {
                     initialDistance = 0;
 
                     if((<any>p.event).pointerType !== "touch") {
-                        pointB = undefined; // Mouse and pen are mono pointer
+                        pointB = null; // Mouse and pen are mono pointer
                     }
 
                     //would be better to use pointers.remove(evt.pointerId) for multitouch gestures, 
@@ -111,22 +112,22 @@ module BABYLON {
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
                     if (engine.badOS) {
-                        pointA = pointB = undefined;
+                        pointA = pointB = null;
                     }
                     else {
                         //only remove the impacted pointer in case of multitouch allowing on most 
                         //platforms switching from rotate to zoom and pan seamlessly.
                         if (pointB && pointA && pointA.pointerId == evt.pointerId) {
                             pointA = pointB;
-                            pointB = undefined;
+                            pointB = null;
                             cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
                         }
                         else if (pointA && pointB && pointB.pointerId == evt.pointerId) {
-                            pointB = undefined;
+                            pointB = null;
                             cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
                         }
                         else {
-                            pointA = pointB = undefined;
+                            pointA = pointB = null;
                         }
                     }
 
@@ -139,7 +140,7 @@ module BABYLON {
                     }
 
                     // One button down
-                    if (pointA && pointB === undefined) {
+                    if (pointA && pointB === null && cacheSoloPointer) {
                         if (this.panningSensibility !== 0 &&
                             ((evt.ctrlKey && this.camera._useCtrlForPanning) || this._isPanClick)) {
                             this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
@@ -214,7 +215,7 @@ module BABYLON {
                                 previousMultiTouchPanPosition.isPinching = true;
                             }
                             else {
-                                if (cacheSoloPointer.pointerId === ed.pointerId && this.panningSensibility !== 0 && this.multiTouchPanning) {
+                                if (cacheSoloPointer && cacheSoloPointer.pointerId === ed.pointerId && this.panningSensibility !== 0 && this.multiTouchPanning) {
                                     if (!previousMultiTouchPanPosition.isPaning) {
                                         previousMultiTouchPanPosition.isPaning = true;
                                         previousMultiTouchPanPosition.isPinching = false;
@@ -228,7 +229,7 @@ module BABYLON {
                                 }
                             }
 
-                            if (cacheSoloPointer.pointerId === evt.pointerId) {
+                            if (cacheSoloPointer && cacheSoloPointer.pointerId === evt.pointerId) {
                                 previousMultiTouchPanPosition.x = ed.x;
                                 previousMultiTouchPanPosition.y = ed.y;
                             }
@@ -251,7 +252,7 @@ module BABYLON {
 
             this._onLostFocus = () => {
                 //this._keys = [];
-                pointA = pointB = undefined;
+                pointA = pointB = null;
                 previousPinchSquaredDistance = 0;
                 previousMultiTouchPanPosition.isPaning = false;
                 previousMultiTouchPanPosition.isPinching = false;
@@ -311,18 +312,31 @@ module BABYLON {
         }
 
         public detachControl(element: HTMLElement) {
-            Tools.UnregisterTopRootEvents([
-                { name: "blur", handler: this._onLostFocus }
-            ]);
+            if (this._onLostFocus) {
+                Tools.UnregisterTopRootEvents([
+                    { name: "blur", handler: this._onLostFocus }
+                ]);
+            }
 
             if (element && this._observer) {
                 this.camera.getScene().onPointerObservable.remove(this._observer);
                 this._observer = null;
 
-                element.removeEventListener("contextmenu", this._onContextMenu);
-                element.removeEventListener("mousemove", this._onMouseMove);
-                element.removeEventListener("MSPointerDown", this._onGestureStart);
-                element.removeEventListener("MSGestureChange", this._onGesture);
+                if (this._onContextMenu) {
+                    element.removeEventListener("contextmenu", this._onContextMenu);
+                }
+
+                if (this._onMouseMove) {
+                    element.removeEventListener("mousemove", this._onMouseMove);
+                }
+
+                if (this._onGestureStart) {
+                    element.removeEventListener("MSPointerDown", this._onGestureStart);
+                }
+
+                if (this._onGesture) {
+                    element.removeEventListener("MSGestureChange", this._onGesture);
+                }
 
                 this._isPanClick = false;
                 this.pinchInwards = true;

+ 11 - 3
src/Cameras/Inputs/babylon.arcRotateCameraVRDeviceOrientationInput.ts

@@ -23,9 +23,17 @@ module BABYLON {
         }
 
         public _onOrientationEvent(evt: DeviceOrientationEvent): void {
-            this._alpha = +evt.alpha | 0;
-            this._beta = +evt.beta | 0;
-            this._gamma = +evt.gamma | 0;
+            if (evt.alpha !== null) {
+                this._alpha = +evt.alpha | 0;
+            }
+
+            if (evt.beta !== null) {
+                this._beta = +evt.beta | 0;
+            }
+
+            if (evt.gamma !== null) {               
+                this._gamma = +evt.gamma | 0;
+            }
             this._dirty = true;
         }
 

+ 3 - 3
src/Cameras/Inputs/babylon.freeCameraDeviceOrientationInput.ts

@@ -42,9 +42,9 @@ module BABYLON {
         }
 
         private _deviceOrientation = (evt: DeviceOrientationEvent) => {
-            this._alpha = evt.alpha;
-            this._beta = evt.beta;
-            this._gamma = evt.gamma;
+            this._alpha = evt.alpha !== null ? evt.alpha : 0;
+            this._beta = evt.beta !== null ? evt.beta : 0;
+            this._gamma = evt.gamma !== null ? evt.gamma : 0;
         }
 
         detachControl(element: HTMLElement) {

+ 1 - 1
src/Cameras/Inputs/babylon.freeCameraGamepadInput.ts

@@ -2,7 +2,7 @@ module BABYLON {
     export class FreeCameraGamepadInput implements ICameraInput<FreeCamera> {
         camera: FreeCamera;
 
-        public gamepad: Gamepad;        
+        public gamepad: Nullable<Gamepad>;        
         private _onGamepadConnectedObserver : Observer<Gamepad>;
         private _onGamepadDisconnectedObserver : Observer<Gamepad>;
 

+ 9 - 4
src/Cameras/Inputs/babylon.freeCameraKeyboardMoveInput.ts

@@ -2,8 +2,8 @@ module BABYLON {
     export class FreeCameraKeyboardMoveInput implements ICameraInput<FreeCamera> {
         camera: FreeCamera;
         private _keys = new Array<number>();
-        private _onCanvasBlurObserver: Observer<Engine>;
-        private _onKeyboardObserver: Observer<KeyboardInfo>;
+        private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
+        private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
         private _engine: Engine;
         private _scene: Scene;        
 
@@ -68,8 +68,13 @@ module BABYLON {
 
         detachControl(element : HTMLElement) {
             if (this._scene) {
-                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
-                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                if (this._onKeyboardObserver) {
+                    this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+                }
+
+                if (this._onCanvasBlurObserver) {
+                    this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                }
                 this._onKeyboardObserver = null;
                 this._onCanvasBlurObserver = null;
             }

+ 9 - 6
src/Cameras/Inputs/babylon.freeCameraMouseInput.ts

@@ -9,10 +9,10 @@ module BABYLON {
         public angularSensibility = 2000.0;
 
         private _pointerInput: (p: PointerInfo, s: EventState) => void;
-        private _onMouseMove: (e: MouseEvent) => any;
-        private _observer: Observer<PointerInfo>;
+        private _onMouseMove: Nullable<(e: MouseEvent) => any>;
+        private _observer: Nullable<Observer<PointerInfo>>;
 
-        private previousPosition: { x: number, y: number };
+        private previousPosition: Nullable<{ x: number, y: number }> = null;
 
         constructor(public touchEnabled = true) {
         }           
@@ -36,7 +36,7 @@ module BABYLON {
                         return;
                     }
 
-                    if (p.type === PointerEventTypes.POINTERDOWN) {
+                    if (p.type === PointerEventTypes.POINTERDOWN && evt.srcElement) {
                         try {
                             evt.srcElement.setPointerCapture(evt.pointerId);
                         } catch (e) {
@@ -53,7 +53,7 @@ module BABYLON {
                             element.focus();
                         }
                     }
-                    else if (p.type === PointerEventTypes.POINTERUP) {
+                    else if (p.type === PointerEventTypes.POINTERUP && evt.srcElement) {
                         try {
                             evt.srcElement.releasePointerCapture(evt.pointerId);
                         } catch (e) {
@@ -129,7 +129,10 @@ module BABYLON {
         detachControl(element: HTMLElement) {
             if (this._observer && element) {
                 this.camera.getScene().onPointerObservable.remove(this._observer);
-                element.removeEventListener("mousemove", this._onMouseMove);
+
+                if (this._onMouseMove) {
+                    element.removeEventListener("mousemove", this._onMouseMove);
+                }
 
                 this._observer = null;
                 this._onMouseMove = null;

+ 17 - 12
src/Cameras/Inputs/babylon.freeCameraTouchInput.ts

@@ -2,13 +2,13 @@ module BABYLON {
     export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
         camera: FreeCamera;
 
-        private _offsetX: number = null;
-        private _offsetY: number = null;
+        private _offsetX: Nullable<number> = null;
+        private _offsetY: Nullable<number> = null;
         private _pointerCount: number = 0;
         private _pointerPressed = new Array<number>();
         private _pointerInput: (p: PointerInfo, s: EventState) => void;
-        private _observer: Observer<PointerInfo>;
-        private _onLostFocus: (e: FocusEvent) => any;
+        private _observer: Nullable<Observer<PointerInfo>>;
+        private _onLostFocus: Nullable<(e: FocusEvent) => any>;
 
         @serialize()
         public touchAngularSensibility: number = 200000.0;
@@ -17,7 +17,7 @@ module BABYLON {
         public touchMoveSensibility: number = 250.0;
 
         attachControl(element: HTMLElement, noPreventDefault?: boolean) {
-            var previousPosition: {x: number, y: number};
+            var previousPosition: Nullable<{x: number, y: number}> = null;
 
             if (this._pointerInput === undefined) {
                 this._onLostFocus = (evt) => {
@@ -93,17 +93,22 @@ module BABYLON {
 
             this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
 
-            element.addEventListener("blur", this._onLostFocus);
+            if (this._onLostFocus) {
+                element.addEventListener("blur", this._onLostFocus);
+            }
         }
 
         detachControl(element: HTMLElement) {
             if (this._pointerInput && element) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
-                this._observer = null;
-
-                element.removeEventListener("blur", this._onLostFocus);
+                if (this._observer) {
+                    this.camera.getScene().onPointerObservable.remove(this._observer);
+                    this._observer = null;
+                }
 
-                this._onLostFocus = null;
+                if (this._onLostFocus) {
+                    element.removeEventListener("blur", this._onLostFocus);
+                    this._onLostFocus = null;
+                }
                 this._pointerPressed = [];
                 this._offsetX = null;
                 this._offsetY = null;
@@ -112,7 +117,7 @@ module BABYLON {
         }
 
         checkInputs() {
-            if (this._offsetX) {
+            if (this._offsetX && this._offsetY) {
                 var camera = this.camera;
                 camera.cameraRotation.y += this._offsetX / this.touchAngularSensibility;
 

+ 9 - 5
src/Engine/babylon.engine.ts

@@ -232,14 +232,14 @@
         public maxVertexUniformVectors: number;
         public maxFragmentUniformVectors: number;
         public standardDerivatives: boolean;
-        public s3tc: WEBGL_compressed_texture_s3tc;
+        public s3tc: Nullable<WEBGL_compressed_texture_s3tc>;
         public pvrtc: any; //WEBGL_compressed_texture_pvrtc;
         public etc1: any; //WEBGL_compressed_texture_etc1;
         public etc2: any; //WEBGL_compressed_texture_etc;
         public astc: any; //WEBGL_compressed_texture_astc;
         public textureFloat: boolean;
         public vertexArrayObject: boolean;
-        public textureAnisotropicFilterExtension: EXT_texture_filter_anisotropic;
+        public textureAnisotropicFilterExtension: Nullable<EXT_texture_filter_anisotropic>;
         public maxAnisotropy: number;
         public instancedArrays: boolean;
         public uintIndices: boolean;
@@ -769,7 +769,7 @@
          * @param {boolean} [antialias] - enable antialias
          * @param options - further options to be sent to the getContext function
          */
-        constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext, antialias?: boolean, options?: EngineOptions, adaptToDeviceRatio = false) {
+        constructor(canvasOrContext: Nullable<HTMLCanvasElement | WebGLRenderingContext>, antialias?: boolean, options?: EngineOptions, adaptToDeviceRatio = false) {
             let canvas: Nullable<HTMLCanvasElement> = null;
             Engine.Instances.push(this);
 
@@ -1078,8 +1078,12 @@
 
             // Constants
             this._gl.HALF_FLOAT_OES = 0x8D61; // Half floating-point type (16-bit).
-            this._gl.RGBA16F = 0x881A; // RGBA 16-bit floating-point color-renderable internal sized format.
-            this._gl.RGBA32F = 0x8814; // RGBA 32-bit floating-point color-renderable internal sized format.
+            if (this._gl.RGBA16F !== 0x881A) {
+                this._gl.RGBA16F = 0x881A; // RGBA 16-bit floating-point color-renderable internal sized format.
+            }
+            if (this._gl.RGBA32F !== 0x8814) {
+                this._gl.RGBA32F = 0x8814; // RGBA 32-bit floating-point color-renderable internal sized format.
+            }
             this._gl.DEPTH24_STENCIL8 = 35056;
 
             // Extensions

+ 8 - 6
src/Engine/babylon.nullEngine.ts

@@ -120,7 +120,7 @@
 
         public createShaderProgram(vertexCode: string, fragmentCode: string, defines: string, context?: WebGLRenderingContext): WebGLProgram {
             return {
-                __SPECTOR_rebuildProgram: undefined,
+                __SPECTOR_rebuildProgram: null,
             };
         }
 
@@ -261,7 +261,7 @@
             return {};
         }
 
-        public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: () => void = null, onError: () => void = null, buffer: ArrayBuffer | HTMLImageElement = null, fallBack?: InternalTexture, format?: number): InternalTexture {
+        public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<() => void> = null, buffer: Nullable<ArrayBuffer | HTMLImageElement> = null, fallBack?: InternalTexture, format?: number): InternalTexture {
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_URL);
             var url = String(urlArg); 
 
@@ -272,8 +272,10 @@
             texture.baseWidth = this._options.textureSize;
             texture.baseHeight = this._options.textureSize;
             texture.width = this._options.textureSize;
-            texture.height = this._options.textureSize;            
-            texture.format = format;
+            texture.height = this._options.textureSize;  
+            if (format) {          
+                texture.format = format;    
+            }
 
             texture.isReady = true;            
 
@@ -313,11 +315,11 @@
             texture.height = height;
             texture.isReady = true;
             texture.samples = 1;
-            texture.generateMipMaps = fullOptions.generateMipMaps;
+            texture.generateMipMaps = fullOptions.generateMipMaps ? true : false;
             texture.samplingMode = fullOptions.samplingMode;
             texture.type = fullOptions.type;
             texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
-            texture._generateStencilBuffer = fullOptions.generateStencilBuffer;
+            texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
             return texture;
         }     
         

+ 2 - 1
src/tsconfig.json

@@ -9,6 +9,7 @@
     "lib": ["dom", "es2015.promise", "es5"],
     "noImplicitReturns": true,
     "noImplicitThis": true,
-    "noUnusedLocals": true
+    "noUnusedLocals": true,
+    "strictNullChecks": true
   }
 }