sebavan 6 年之前
父節點
當前提交
85e55d0d93
共有 100 個文件被更改,包括 21314 次插入21315 次删除
  1. 1 1
      gui/src/2D/controls/virtualKeyboard.ts
  2. 2 2
      gui/src/3D/controls/holographicButton.ts
  3. 2 2
      loaders/src/OBJ/objFileLoader.ts
  4. 1 1
      loaders/src/glTF/1.0/glTFBinaryExtension.ts
  5. 25 25
      loaders/src/glTF/1.0/glTFLoaderInterfaces.ts
  6. 4 4
      loaders/src/glTF/1.0/glTFMaterialsCommonExtension.ts
  7. 1 1
      loaders/src/glTF/2.0/Extensions/EXT_lights_image_based.ts
  8. 90 90
      loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts
  9. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  10. 1 1
      loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts
  11. 253 253
      loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts
  12. 216 216
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  13. 1726 1726
      loaders/src/glTF/2.0/glTFLoader.ts
  14. 87 87
      loaders/src/glTF/2.0/glTFLoaderExtension.ts
  15. 209 209
      loaders/src/glTF/2.0/glTFLoaderInterfaces.ts
  16. 1 1
      loaders/src/glTF/glTFFileLoader.ts
  17. 1 1
      loaders/src/legacy/legacy-glTF.ts
  18. 2 2
      loaders/src/legacy/legacy-glTF1.ts
  19. 4 4
      loaders/src/legacy/legacy-glTF2.ts
  20. 5 5
      materialsLibrary/src/custom/customMaterial.ts
  21. 3 3
      materialsLibrary/src/grid/gridMaterial.ts
  22. 4 4
      materialsLibrary/src/sky/skyMaterial.ts
  23. 1 1
      materialsLibrary/src/water/waterMaterial.ts
  24. 2 2
      serializers/src/glTF/2.0/glTFData.ts
  25. 200 200
      src/Actions/action.ts
  26. 68 68
      src/Actions/actionEvent.ts
  27. 568 568
      src/Actions/actionManager.ts
  28. 280 280
      src/Actions/condition.ts
  29. 464 464
      src/Actions/directActions.ts
  30. 72 72
      src/Actions/directAudioActions.ts
  31. 124 124
      src/Actions/interpolateValueAction.ts
  32. 337 337
      src/Animations/animatable.ts
  33. 1056 1056
      src/Animations/animation.ts
  34. 404 404
      src/Animations/animationGroup.ts
  35. 15 15
      src/Animations/animationPropertiesOverride.ts
  36. 275 275
      src/Animations/easing.ts
  37. 507 507
      src/Animations/runtimeAnimation.ts
  38. 163 163
      src/Audio/analyser.ts
  39. 320 320
      src/Audio/audioEngine.ts
  40. 305 305
      src/Audio/audioSceneComponent.ts
  41. 869 870
      src/Audio/sound.ts
  42. 144 144
      src/Audio/soundTrack.ts
  43. 143 143
      src/Audio/weightedsound.ts
  44. 171 171
      src/Behaviors/Cameras/autoRotationBehavior.ts
  45. 166 166
      src/Behaviors/Cameras/bouncingBehavior.ts
  46. 451 451
      src/Behaviors/Cameras/framingBehavior.ts
  47. 144 144
      src/Behaviors/Meshes/attachToBoxBehavior.ts
  48. 73 73
      src/Behaviors/Meshes/fadeInOutBehavior.ts
  49. 87 87
      src/Behaviors/Meshes/multiPointerScaleBehavior.ts
  50. 359 359
      src/Behaviors/Meshes/pointerDragBehavior.ts
  51. 217 217
      src/Behaviors/Meshes/sixDofDragBehavior.ts
  52. 40 40
      src/Behaviors/behavior.ts
  53. 893 893
      src/Bones/bone.ts
  54. 263 263
      src/Bones/boneIKController.ts
  55. 453 453
      src/Bones/boneLookController.ts
  56. 570 570
      src/Bones/skeleton.ts
  57. 101 101
      src/Cameras/Inputs/arcRotateCameraGamepadInput.ts
  58. 205 205
      src/Cameras/Inputs/arcRotateCameraKeyboardMoveInput.ts
  59. 78 78
      src/Cameras/Inputs/arcRotateCameraMouseWheelInput.ts
  60. 341 341
      src/Cameras/Inputs/arcRotateCameraPointersInput.ts
  61. 82 82
      src/Cameras/Inputs/arcRotateCameraVRDeviceOrientationInput.ts
  62. 171 171
      src/Cameras/Inputs/flyCameraKeyboardInput.ts
  63. 240 240
      src/Cameras/Inputs/flyCameraMouseInput.ts
  64. 173 173
      src/Cameras/Inputs/followCameraKeyboardMoveInput.ts
  65. 92 92
      src/Cameras/Inputs/freeCameraDeviceOrientationInput.ts
  66. 114 114
      src/Cameras/Inputs/freeCameraGamepadInput.ts
  67. 147 147
      src/Cameras/Inputs/freeCameraKeyboardMoveInput.ts
  68. 143 143
      src/Cameras/Inputs/freeCameraMouseInput.ts
  69. 144 144
      src/Cameras/Inputs/freeCameraTouchInput.ts
  70. 81 81
      src/Cameras/Inputs/freeCameraVirtualJoystickInput.ts
  71. 29 29
      src/Cameras/Stereoscopic/anaglyphArcRotateCamera.ts
  72. 25 25
      src/Cameras/Stereoscopic/anaglyphFreeCamera.ts
  73. 25 25
      src/Cameras/Stereoscopic/anaglyphGamepadCamera.ts
  74. 25 25
      src/Cameras/Stereoscopic/anaglyphUniversalCamera.ts
  75. 30 30
      src/Cameras/Stereoscopic/stereoscopicArcRotateCamera.ts
  76. 27 27
      src/Cameras/Stereoscopic/stereoscopicFreeCamera.ts
  77. 27 27
      src/Cameras/Stereoscopic/stereoscopicGamepadCamera.ts
  78. 27 27
      src/Cameras/Stereoscopic/stereoscopicUniversalCamera.ts
  79. 118 118
      src/Cameras/VR/vrCameraMetrics.ts
  80. 31 31
      src/Cameras/VR/vrDeviceOrientationArcRotateCamera.ts
  81. 27 27
      src/Cameras/VR/vrDeviceOrientationFreeCamera.ts
  82. 26 26
      src/Cameras/VR/vrDeviceOrientationGamepadCamera.ts
  83. 1686 1686
      src/Cameras/VR/vrExperienceHelper.ts
  84. 665 665
      src/Cameras/VR/webVRCamera.ts
  85. 87 87
      src/Cameras/XR/webXRCamera.ts
  86. 123 123
      src/Cameras/XR/webXREnterExitUI.ts
  87. 170 170
      src/Cameras/XR/webXRExperienceHelper.ts
  88. 83 83
      src/Cameras/XR/webXRInput.ts
  89. 50 50
      src/Cameras/XR/webXRManagedOutputCanvas.ts
  90. 159 159
      src/Cameras/XR/webXRSessionManager.ts
  91. 913 913
      src/Cameras/arcRotateCamera.ts
  92. 43 43
      src/Cameras/arcRotateCameraInputsManager.ts
  93. 1058 1058
      src/Cameras/camera.ts
  94. 240 240
      src/Cameras/cameraInputsManager.ts
  95. 61 61
      src/Cameras/deviceOrientationCamera.ts
  96. 342 342
      src/Cameras/flyCamera.ts
  97. 28 28
      src/Cameras/flyCameraInputsManager.ts
  98. 190 190
      src/Cameras/followCamera.ts
  99. 43 43
      src/Cameras/followCameraInputsManager.ts
  100. 0 0
      src/Cameras/freeCamera.ts

+ 1 - 1
gui/src/2D/controls/virtualKeyboard.ts

@@ -265,7 +265,7 @@ export class VirtualKeyboard extends StackPanel {
         }
     }
 
-    private _removeConnectedInputObservables(connectedInputText: ConnectedInputText) : void {
+    private _removeConnectedInputObservables(connectedInputText: ConnectedInputText): void {
         connectedInputText.input._connectedVirtualKeyboard = null;
         connectedInputText.input.onFocusObservable.remove(connectedInputText.onFocusObserver);
         connectedInputText.input.onBlurObservable.remove(connectedInputText.onBlurObserver);

+ 2 - 2
gui/src/3D/controls/holographicButton.ts

@@ -65,8 +65,8 @@ export class HolographicButton extends Button3D {
         }
         if (!this._tooltipFade) {
             // Create tooltip with mesh and text
-            this._tooltipMesh = MeshBuilder.CreatePlane("", {size: 1}, this._backPlate._scene);
-            var tooltipBackground = MeshBuilder.CreatePlane("", {size: 1, sideOrientation: Mesh.DOUBLESIDE}, this._backPlate._scene);
+            this._tooltipMesh = MeshBuilder.CreatePlane("", { size: 1 }, this._backPlate._scene);
+            var tooltipBackground = MeshBuilder.CreatePlane("", { size: 1, sideOrientation: Mesh.DOUBLESIDE }, this._backPlate._scene);
             var mat = new StandardMaterial("", this._backPlate._scene);
             mat.diffuseColor = Color3.FromHexString("#212121");
             tooltipBackground.material = mat;

+ 2 - 2
loaders/src/OBJ/objFileLoader.ts

@@ -698,9 +698,9 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync {
                 //Split the data for getting position, uv, and normals
                 var point = triangles[k].split("/"); // ["-1", "-1", "-1"]
                 // Set position indice
-                var indicePositionFromObj =  positions.length + parseInt(point[0]);
+                var indicePositionFromObj = positions.length + parseInt(point[0]);
                 // Set uv indice
-                var indiceUvsFromObj =  uvs.length + parseInt(point[1]);
+                var indiceUvsFromObj = uvs.length + parseInt(point[1]);
                 // Set normal indice
                 var indiceNormalFromObj = normals.length + parseInt(point[2]);
 

+ 1 - 1
loaders/src/glTF/1.0/glTFBinaryExtension.ts

@@ -20,7 +20,7 @@ interface IGLTFBinaryExtensionImage {
 
 /** @hidden */
 export class GLTFBinaryExtension extends GLTFLoaderExtension {
-    private _bin : ArrayBufferView;
+    private _bin: ArrayBufferView;
 
     public constructor() {
         super("KHR_binary_glTF");

+ 25 - 25
loaders/src/glTF/1.0/glTFLoaderInterfaces.ts

@@ -101,7 +101,7 @@ export enum EBlendingFunction {
 
 /** @hidden */
 export interface IGLTFProperty {
-    extensions?: {[key: string]: any};
+    extensions?: { [key: string]: any };
     extras?: Object;
 }
 
@@ -193,11 +193,11 @@ export interface IGLTFTechniqueStates {
 
 /** @hidden */
 export interface IGLTFTechnique extends IGLTFChildRootProperty {
-    parameters: {[key: string]: IGLTFTechniqueParameter};
+    parameters: { [key: string]: IGLTFTechniqueParameter };
     program: string;
 
-    attributes: {[key: string]: string};
-    uniforms: {[key: string]: string};
+    attributes: { [key: string]: string };
+    uniforms: { [key: string]: string };
     states: IGLTFTechniqueStates;
 }
 
@@ -209,7 +209,7 @@ export interface IGLTFMaterial extends IGLTFChildRootProperty {
 
 /** @hidden */
 export interface IGLTFMeshPrimitive extends IGLTFProperty {
-    attributes: {[key: string]: string};
+    attributes: { [key: string]: string };
     indices: string;
     material: string;
 
@@ -325,8 +325,8 @@ export interface IGLTFAnimationSampler {
 /** @hidden */
 export interface IGLTFAnimation extends IGLTFChildRootProperty {
     channels?: IGLTFAnimationChannel[];
-    parameters?:  {[key: string]: string};
-    samplers?: {[key: string]: IGLTFAnimationSampler};
+    parameters?: { [key: string]: string };
+    samplers?: { [key: string]: IGLTFAnimationSampler };
 }
 
 /** @hidden */
@@ -370,26 +370,26 @@ export interface IGLTFScene extends IGLTFChildRootProperty {
 
 /** @hidden */
 export interface IGLTFRuntime {
-    extensions: {[key: string]: any};
-    accessors: {[key: string]: IGLTFAccessor};
-    buffers: {[key: string]: IGLTFBuffer};
-    bufferViews: {[key: string]: IGLTFBufferView};
-    meshes: {[key: string]: IGLTFMesh};
-    lights: {[key: string]: IGLTFLight};
-    cameras: {[key: string]: IGLTFCamera};
-    nodes: {[key: string]: IGLTFNode};
-    images: {[key: string]: IGLTFImage};
-    textures: {[key: string]: IGLTFTexture};
-    shaders: {[key: string]: IGLTFShader};
-    programs: {[key: string]: IGLTFProgram};
-    samplers: {[key: string]: IGLTFSampler};
-    techniques: {[key: string]: IGLTFTechnique};
-    materials: {[key: string]: IGLTFMaterial};
-    animations: {[key: string]: IGLTFAnimation};
-    skins: {[key: string]: IGLTFSkins};
+    extensions: { [key: string]: any };
+    accessors: { [key: string]: IGLTFAccessor };
+    buffers: { [key: string]: IGLTFBuffer };
+    bufferViews: { [key: string]: IGLTFBufferView };
+    meshes: { [key: string]: IGLTFMesh };
+    lights: { [key: string]: IGLTFLight };
+    cameras: { [key: string]: IGLTFCamera };
+    nodes: { [key: string]: IGLTFNode };
+    images: { [key: string]: IGLTFImage };
+    textures: { [key: string]: IGLTFTexture };
+    shaders: { [key: string]: IGLTFShader };
+    programs: { [key: string]: IGLTFProgram };
+    samplers: { [key: string]: IGLTFSampler };
+    techniques: { [key: string]: IGLTFTechnique };
+    materials: { [key: string]: IGLTFMaterial };
+    animations: { [key: string]: IGLTFAnimation };
+    skins: { [key: string]: IGLTFSkins };
 
     currentScene?: Object;
-    scenes: {[key: string]: IGLTFScene}; // v1.1
+    scenes: { [key: string]: IGLTFScene }; // v1.1
 
     extensionsUsed: string[];
     extensionsRequired?: string[]; // v1.1

+ 4 - 4
loaders/src/glTF/1.0/glTFMaterialsCommonExtension.ts

@@ -31,7 +31,7 @@ interface IGLTFMaterialsCommonExtension {
 }
 
 interface IGLTFRuntimeCommonExtension {
-    lights: {[key: string]: IGLTFLightCommonExtension};
+    lights: { [key: string]: IGLTFLightCommonExtension };
 }
 
 interface IGLTFLightCommonExtension {
@@ -113,9 +113,9 @@ export class GLTFMaterialsCommonExtension extends GLTFLoaderExtension {
                         var spot = light.spot;
                         if (spot) {
                             var spotLight = new SpotLight(light.name, new Vector3(0, 10, 0), new Vector3(0, -1, 0),
-                                                        spot.fallOffAngle || Math.PI,
-                                                        spot.fallOffExponent || 0.0,
-                                                        gltfRuntime.scene);
+                                spot.fallOffAngle || Math.PI,
+                                spot.fallOffExponent || 0.0,
+                                gltfRuntime.scene);
                             spotLight.diffuse = Color3.FromArray(spot.color || [1, 1, 1]);
                         }
                         break;

+ 1 - 1
loaders/src/glTF/2.0/Extensions/EXT_lights_image_based.ts

@@ -80,7 +80,7 @@ export class EXT_lights_image_based implements IGLTFLoaderExtension {
 
             this._loader.logClose();
 
-            return Promise.all(promises).then(() => {});
+            return Promise.all(promises).then(() => { });
         });
     }
 

+ 90 - 90
loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts

@@ -11,108 +11,108 @@ import { INode } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
 
-    const NAME = "KHR_lights_punctual";
-
-    enum LightType {
-        DIRECTIONAL = "directional",
-        POINT = "point",
-        SPOT = "spot"
-    }
-
-    interface ILightReference {
-        light: number;
-    }
-
-    interface ILight extends IChildRootProperty {
-        type: LightType;
-        color?: number[];
-        intensity?: number;
-        range?: number;
-        spot?: {
-            innerConeAngle?: number;
-            outerConeAngle?: number;
-        };
+const NAME = "KHR_lights_punctual";
+
+enum LightType {
+    DIRECTIONAL = "directional",
+    POINT = "point",
+    SPOT = "spot"
+}
+
+interface ILightReference {
+    light: number;
+}
+
+interface ILight extends IChildRootProperty {
+    type: LightType;
+    color?: number[];
+    intensity?: number;
+    range?: number;
+    spot?: {
+        innerConeAngle?: number;
+        outerConeAngle?: number;
+    };
+}
+
+interface ILights {
+    lights: ILight[];
+}
+
+/**
+ * [Specification](https://github.com/KhronosGroup/glTF/blob/1048d162a44dbcb05aefc1874bfd423cf60135a6/extensions/2.0/Khronos/KHR_lights_punctual/README.md) (Experimental)
+ */
+export class KHR_lights implements IGLTFLoaderExtension {
+    /** The name of this extension. */
+    public readonly name = NAME;
+
+    /** Defines whether this extension is enabled. */
+    public enabled = true;
+
+    private _loader: GLTFLoader;
+    private _lights?: ILight[];
+
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
     }
 
-    interface ILights {
-        lights: ILight[];
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
+        delete this._lights;
     }
 
-    /**
-     * [Specification](https://github.com/KhronosGroup/glTF/blob/1048d162a44dbcb05aefc1874bfd423cf60135a6/extensions/2.0/Khronos/KHR_lights_punctual/README.md) (Experimental)
-     */
-    export class KHR_lights implements IGLTFLoaderExtension {
-        /** The name of this extension. */
-        public readonly name = NAME;
-
-        /** Defines whether this extension is enabled. */
-        public enabled = true;
-
-        private _loader: GLTFLoader;
-        private _lights?: ILight[];
-
-        /** @hidden */
-        constructor(loader: GLTFLoader) {
-            this._loader = loader;
+    /** @hidden */
+    public onLoading(): void {
+        const extensions = this._loader.gltf.extensions;
+        if (extensions && extensions[this.name]) {
+            const extension = extensions[this.name] as ILights;
+            this._lights = extension.lights;
         }
+    }
 
-        /** @hidden */
-        public dispose() {
-            delete this._loader;
-            delete this._lights;
-        }
+    /** @hidden */
+    public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
+        return GLTFLoader.LoadExtensionAsync<ILightReference, TransformNode>(context, node, this.name, (extensionContext, extension) => {
+            return this._loader.loadNodeAsync(context, node, (babylonMesh) => {
+                let babylonLight: Light;
 
-        /** @hidden */
-        public onLoading(): void {
-            const extensions = this._loader.gltf.extensions;
-            if (extensions && extensions[this.name]) {
-                const extension = extensions[this.name] as ILights;
-                this._lights = extension.lights;
-            }
-        }
+                const light = ArrayItem.Get(extensionContext, this._lights, extension.light);
+                const name = light.name || babylonMesh.name;
 
-        /** @hidden */
-        public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
-            return GLTFLoader.LoadExtensionAsync<ILightReference, TransformNode>(context, node, this.name, (extensionContext, extension) => {
-                return this._loader.loadNodeAsync(context, node, (babylonMesh) => {
-                    let babylonLight: Light;
-
-                    const light = ArrayItem.Get(extensionContext, this._lights, extension.light);
-                    const name = light.name || babylonMesh.name;
-
-                    switch (light.type) {
-                        case LightType.DIRECTIONAL: {
-                            babylonLight = new DirectionalLight(name, Vector3.Backward(), this._loader.babylonScene);
-                            break;
-                        }
-                        case LightType.POINT: {
-                            babylonLight = new PointLight(name, Vector3.Zero(), this._loader.babylonScene);
-                            break;
-                        }
-                        case LightType.SPOT: {
-                            const babylonSpotLight = new SpotLight(name, Vector3.Zero(), Vector3.Backward(), 0, 1, this._loader.babylonScene);
-                            babylonSpotLight.angle = ((light.spot && light.spot.outerConeAngle) || Math.PI / 4) * 2;
-                            babylonSpotLight.innerAngle = ((light.spot && light.spot.innerConeAngle) || 0) * 2;
-                            babylonLight = babylonSpotLight;
-                            break;
-                        }
-                        default: {
-                            throw new Error(`${extensionContext}: Invalid light type (${light.type})`);
-                        }
+                switch (light.type) {
+                    case LightType.DIRECTIONAL: {
+                        babylonLight = new DirectionalLight(name, Vector3.Backward(), this._loader.babylonScene);
+                        break;
+                    }
+                    case LightType.POINT: {
+                        babylonLight = new PointLight(name, Vector3.Zero(), this._loader.babylonScene);
+                        break;
                     }
+                    case LightType.SPOT: {
+                        const babylonSpotLight = new SpotLight(name, Vector3.Zero(), Vector3.Backward(), 0, 1, this._loader.babylonScene);
+                        babylonSpotLight.angle = ((light.spot && light.spot.outerConeAngle) || Math.PI / 4) * 2;
+                        babylonSpotLight.innerAngle = ((light.spot && light.spot.innerConeAngle) || 0) * 2;
+                        babylonLight = babylonSpotLight;
+                        break;
+                    }
+                    default: {
+                        throw new Error(`${extensionContext}: Invalid light type (${light.type})`);
+                    }
+                }
 
-                    babylonLight.falloffType = Light.FALLOFF_GLTF;
-                    babylonLight.diffuse = light.color ? Color3.FromArray(light.color) : Color3.White();
-                    babylonLight.intensity = light.intensity == undefined ? 1 : light.intensity;
-                    babylonLight.range = light.range == undefined ? Number.MAX_VALUE : light.range;
-                    babylonLight.parent = babylonMesh;
+                babylonLight.falloffType = Light.FALLOFF_GLTF;
+                babylonLight.diffuse = light.color ? Color3.FromArray(light.color) : Color3.White();
+                babylonLight.intensity = light.intensity == undefined ? 1 : light.intensity;
+                babylonLight.range = light.range == undefined ? Number.MAX_VALUE : light.range;
+                babylonLight.parent = babylonMesh;
 
-                    GLTFLoader.AddPointerMetadata(babylonLight, extensionContext);
+                GLTFLoader.AddPointerMetadata(babylonLight, extensionContext);
 
-                    assign(babylonMesh);
-                });
+                assign(babylonMesh);
             });
-        }
+        });
     }
+}
 
-    GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_lights(loader));
+GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_lights(loader));

+ 2 - 2
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -46,7 +46,7 @@ export class KHR_materials_pbrSpecularGlossiness implements IGLTFLoaderExtension
             promises.push(this._loader.loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
             promises.push(this._loadSpecularGlossinessPropertiesAsync(extensionContext, material, extension, babylonMaterial));
             this._loader.loadMaterialAlphaProperties(context, material, babylonMaterial);
-            return Promise.all(promises).then(() => {});
+            return Promise.all(promises).then(() => { });
         });
     }
 
@@ -88,7 +88,7 @@ export class KHR_materials_pbrSpecularGlossiness implements IGLTFLoaderExtension
             babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
         }
 
-        return Promise.all(promises).then(() => {});
+        return Promise.all(promises).then(() => { });
     }
 }
 

+ 1 - 1
loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts

@@ -71,7 +71,7 @@ export class KHR_materials_unlit implements IGLTFLoaderExtension {
 
         this._loader.loadMaterialAlphaProperties(context, material, babylonMaterial);
 
-        return Promise.all(promises).then(() => {});
+        return Promise.all(promises).then(() => { });
     }
 }
 

+ 253 - 253
loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts

@@ -11,301 +11,301 @@ import { IArrayItem, IScene, INode, IAnimation } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
 
-    const NAME = "MSFT_audio_emitter";
-
-    interface IClipReference {
-        clip: number;
-        weight?: number;
-    }
-
-    interface IEmittersReference {
-        emitters: number[];
-    }
-
-    const enum DistanceModel {
-        linear = "linear",
-        inverse = "inverse",
-        exponential = "exponential",
-    }
-
-    interface IEmitter {
-        name?: string;
-        distanceModel?: DistanceModel;
-        refDistance?: number;
-        maxDistance?: number;
-        rolloffFactor?: number;
-        innerAngle?: number;
-        outerAngle?: number;
-        loop?: boolean;
-        volume?: number;
-        clips: IClipReference[];
-    }
-
-    const enum AudioMimeType {
-        WAV = "audio/wav",
-    }
-
-    interface IClip {
-        uri?: string;
-        bufferView?: number;
-        mimeType?: AudioMimeType;
-    }
-
-    interface ILoaderClip extends IClip, IArrayItem {
-        _objectURL?: Promise<string>;
-    }
-
-    interface ILoaderEmitter extends IEmitter, IArrayItem {
-        _babylonData?: {
-            sound?: WeightedSound;
-            loaded: Promise<void>;
-        };
-        _babylonSounds: Sound[];
+const NAME = "MSFT_audio_emitter";
+
+interface IClipReference {
+    clip: number;
+    weight?: number;
+}
+
+interface IEmittersReference {
+    emitters: number[];
+}
+
+const enum DistanceModel {
+    linear = "linear",
+    inverse = "inverse",
+    exponential = "exponential",
+}
+
+interface IEmitter {
+    name?: string;
+    distanceModel?: DistanceModel;
+    refDistance?: number;
+    maxDistance?: number;
+    rolloffFactor?: number;
+    innerAngle?: number;
+    outerAngle?: number;
+    loop?: boolean;
+    volume?: number;
+    clips: IClipReference[];
+}
+
+const enum AudioMimeType {
+    WAV = "audio/wav",
+}
+
+interface IClip {
+    uri?: string;
+    bufferView?: number;
+    mimeType?: AudioMimeType;
+}
+
+interface ILoaderClip extends IClip, IArrayItem {
+    _objectURL?: Promise<string>;
+}
+
+interface ILoaderEmitter extends IEmitter, IArrayItem {
+    _babylonData?: {
+        sound?: WeightedSound;
+        loaded: Promise<void>;
+    };
+    _babylonSounds: Sound[];
+}
+
+interface IMSFTAudioEmitter {
+    clips: ILoaderClip[];
+    emitters: ILoaderEmitter[];
+}
+
+const enum AnimationEventAction {
+    play = "play",
+    pause = "pause",
+    stop = "stop",
+}
+
+interface IAnimationEvent {
+    action: AnimationEventAction;
+    emitter: number;
+    time: number;
+    startOffset?: number;
+}
+
+interface ILoaderAnimationEvent extends IAnimationEvent, IArrayItem {
+}
+
+interface ILoaderAnimationEvents {
+    events: ILoaderAnimationEvent[];
+}
+
+/**
+ * [Specification](https://github.com/najadojo/glTF/tree/MSFT_audio_emitter/extensions/2.0/Vendor/MSFT_audio_emitter)
+ */
+export class MSFT_audio_emitter implements IGLTFLoaderExtension {
+    /** The name of this extension. */
+    public readonly name = NAME;
+
+    /** Defines whether this extension is enabled. */
+    public enabled = true;
+
+    private _loader: GLTFLoader;
+    private _clips: Array<ILoaderClip>;
+    private _emitters: Array<ILoaderEmitter>;
+
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
     }
 
-    interface IMSFTAudioEmitter {
-        clips: ILoaderClip[];
-        emitters: ILoaderEmitter[];
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
+        delete this._clips;
+        delete this._emitters;
     }
 
-    const enum AnimationEventAction {
-        play = "play",
-        pause = "pause",
-        stop = "stop",
-    }
+    /** @hidden */
+    public onLoading(): void {
+        const extensions = this._loader.gltf.extensions;
+        if (extensions && extensions[this.name]) {
+            const extension = extensions[this.name] as IMSFTAudioEmitter;
 
-    interface IAnimationEvent {
-        action: AnimationEventAction;
-        emitter: number;
-        time: number;
-        startOffset?: number;
-    }
+            this._clips = extension.clips;
+            this._emitters = extension.emitters;
 
-    interface ILoaderAnimationEvent extends IAnimationEvent, IArrayItem {
-    }
-
-    interface ILoaderAnimationEvents {
-        events: ILoaderAnimationEvent[];
-    }
-
-    /**
-     * [Specification](https://github.com/najadojo/glTF/tree/MSFT_audio_emitter/extensions/2.0/Vendor/MSFT_audio_emitter)
-     */
-    export class MSFT_audio_emitter implements IGLTFLoaderExtension {
-        /** The name of this extension. */
-        public readonly name = NAME;
-
-        /** Defines whether this extension is enabled. */
-        public enabled = true;
-
-        private _loader: GLTFLoader;
-        private _clips: Array<ILoaderClip>;
-        private _emitters: Array<ILoaderEmitter>;
-
-        /** @hidden */
-        constructor(loader: GLTFLoader) {
-            this._loader = loader;
+            ArrayItem.Assign(this._clips);
+            ArrayItem.Assign(this._emitters);
         }
+    }
 
-        /** @hidden */
-        public dispose() {
-            delete this._loader;
-            delete this._clips;
-            delete this._emitters;
-        }
+    /** @hidden */
+    public loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>> {
+        return GLTFLoader.LoadExtensionAsync<IEmittersReference>(context, scene, this.name, (extensionContext, extension) => {
+            const promises = new Array<Promise<any>>();
 
-        /** @hidden */
-        public onLoading(): void {
-            const extensions = this._loader.gltf.extensions;
-            if (extensions && extensions[this.name]) {
-                const extension = extensions[this.name] as IMSFTAudioEmitter;
+            promises.push(this._loader.loadSceneAsync(context, scene));
 
-                this._clips = extension.clips;
-                this._emitters = extension.emitters;
+            for (const emitterIndex of extension.emitters) {
+                const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);
+                if (emitter.refDistance != undefined || emitter.maxDistance != undefined || emitter.rolloffFactor != undefined ||
+                    emitter.distanceModel != undefined || emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
+                    throw new Error(`${extensionContext}: Direction or Distance properties are not allowed on emitters attached to a scene`);
+                }
 
-                ArrayItem.Assign(this._clips);
-                ArrayItem.Assign(this._emitters);
+                promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter));
             }
-        }
 
-        /** @hidden */
-        public loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>> {
-            return GLTFLoader.LoadExtensionAsync<IEmittersReference>(context, scene, this.name, (extensionContext, extension) => {
-                const promises = new Array<Promise<any>>();
+            return Promise.all(promises).then(() => { });
+        });
+    }
 
-                promises.push(this._loader.loadSceneAsync(context, scene));
+    /** @hidden */
+    public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
+        return GLTFLoader.LoadExtensionAsync<IEmittersReference, TransformNode>(context, node, this.name, (extensionContext, extension) => {
+            const promises = new Array<Promise<any>>();
 
+            return this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {
                 for (const emitterIndex of extension.emitters) {
                     const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);
-                    if (emitter.refDistance != undefined || emitter.maxDistance != undefined || emitter.rolloffFactor != undefined ||
-                        emitter.distanceModel != undefined || emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
-                        throw new Error(`${extensionContext}: Direction or Distance properties are not allowed on emitters attached to a scene`);
-                    }
-
-                    promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter));
+                    promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter).then(() => {
+                        for (const sound of emitter._babylonSounds) {
+                            sound.attachToMesh(babylonMesh);
+                            if (emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
+                                sound.setLocalDirectionToMesh(Vector3.Forward());
+                                sound.setDirectionalCone(
+                                    2 * Tools.ToDegrees(emitter.innerAngle == undefined ? Math.PI : emitter.innerAngle),
+                                    2 * Tools.ToDegrees(emitter.outerAngle == undefined ? Math.PI : emitter.outerAngle),
+                                    0);
+                            }
+                        }
+                    }));
                 }
 
-                return Promise.all(promises).then(() => {});
+                assign(babylonMesh);
+            }).then((babylonMesh) => {
+                return Promise.all(promises).then(() => {
+                    return babylonMesh;
+                });
             });
-        }
+        });
+    }
 
-        /** @hidden */
-        public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
-            return GLTFLoader.LoadExtensionAsync<IEmittersReference, TransformNode>(context, node, this.name, (extensionContext, extension) => {
+    /** @hidden */
+    public loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>> {
+        return GLTFLoader.LoadExtensionAsync<ILoaderAnimationEvents, AnimationGroup>(context, animation, this.name, (extensionContext, extension) => {
+            return this._loader.loadAnimationAsync(context, animation).then((babylonAnimationGroup) => {
                 const promises = new Array<Promise<any>>();
 
-                return this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {
-                    for (const emitterIndex of extension.emitters) {
-                        const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);
-                        promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter).then(() => {
-                            for (const sound of emitter._babylonSounds) {
-                                sound.attachToMesh(babylonMesh);
-                                if (emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
-                                    sound.setLocalDirectionToMesh(Vector3.Forward());
-                                    sound.setDirectionalCone(
-                                        2 * Tools.ToDegrees(emitter.innerAngle == undefined ? Math.PI : emitter.innerAngle),
-                                        2 * Tools.ToDegrees(emitter.outerAngle == undefined ? Math.PI : emitter.outerAngle),
-                                        0);
-                                }
-                            }
-                        }));
-                    }
-
-                    assign(babylonMesh);
-                }).then((babylonMesh) => {
-                    return Promise.all(promises).then(() => {
-                        return babylonMesh;
-                    });
+                ArrayItem.Assign(extension.events);
+                for (const event of extension.events) {
+                    promises.push(this._loadAnimationEventAsync(`${extensionContext}/events/${event.index}`, context, animation, event, babylonAnimationGroup));
+                }
+
+                return Promise.all(promises).then(() => {
+                    return babylonAnimationGroup;
                 });
             });
-        }
-
-        /** @hidden */
-        public loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>> {
-            return GLTFLoader.LoadExtensionAsync<ILoaderAnimationEvents, AnimationGroup>(context, animation, this.name, (extensionContext, extension) => {
-                return this._loader.loadAnimationAsync(context, animation).then((babylonAnimationGroup) => {
-                    const promises = new Array<Promise<any>>();
+        });
+    }
 
-                    ArrayItem.Assign(extension.events);
-                    for (const event of extension.events) {
-                        promises.push(this._loadAnimationEventAsync(`${extensionContext}/events/${event.index}`, context, animation, event, babylonAnimationGroup));
-                    }
+    private _loadClipAsync(context: string, clip: ILoaderClip): Promise<string> {
+        if (clip._objectURL) {
+            return clip._objectURL;
+        }
 
-                    return Promise.all(promises).then(() => {
-                        return babylonAnimationGroup;
-                    });
-                });
-            });
+        let promise: Promise<ArrayBufferView>;
+        if (clip.uri) {
+            promise = this._loader.loadUriAsync(context, clip.uri);
+        }
+        else {
+            const bufferView = ArrayItem.Get(`${context}/bufferView`, this._loader.gltf.bufferViews, clip.bufferView);
+            promise = this._loader.loadBufferViewAsync(`#/bufferViews/${bufferView.index}`, bufferView);
         }
 
-        private _loadClipAsync(context: string, clip: ILoaderClip): Promise<string> {
-            if (clip._objectURL) {
-                return clip._objectURL;
-            }
+        clip._objectURL = promise.then((data) => {
+            return URL.createObjectURL(new Blob([data], { type: clip.mimeType }));
+        });
 
-            let promise: Promise<ArrayBufferView>;
-            if (clip.uri) {
-                promise = this._loader.loadUriAsync(context, clip.uri);
-            }
-            else {
-                const bufferView = ArrayItem.Get(`${context}/bufferView`, this._loader.gltf.bufferViews, clip.bufferView);
-                promise = this._loader.loadBufferViewAsync(`#/bufferViews/${bufferView.index}`, bufferView);
+        return clip._objectURL;
+    }
+
+    private _loadEmitterAsync(context: string, emitter: ILoaderEmitter): Promise<void> {
+        emitter._babylonSounds = emitter._babylonSounds || [];
+        if (!emitter._babylonData) {
+            const clipPromises = new Array<Promise<any>>();
+            const name = emitter.name || `emitter${emitter.index}`;
+            const options = {
+                loop: false,
+                autoplay: false,
+                volume: emitter.volume == undefined ? 1 : emitter.volume,
+            };
+
+            for (let i = 0; i < emitter.clips.length; i++) {
+                const clipContext = `#/extensions/${this.name}/clips`;
+                const clip = ArrayItem.Get(clipContext, this._clips, emitter.clips[i].clip);
+                clipPromises.push(this._loadClipAsync(`${clipContext}/${emitter.clips[i].clip}`, clip).then((objectURL: string) => {
+                    const sound = emitter._babylonSounds[i] = new Sound(name, objectURL, this._loader.babylonScene, null, options);
+                    sound.refDistance = emitter.refDistance || 1;
+                    sound.maxDistance = emitter.maxDistance || 256;
+                    sound.rolloffFactor = emitter.rolloffFactor || 1;
+                    sound.distanceModel = emitter.distanceModel || 'exponential';
+                    sound._positionInEmitterSpace = true;
+                }));
             }
 
-            clip._objectURL = promise.then((data) => {
-                return URL.createObjectURL(new Blob([data], { type: clip.mimeType }));
+            const promise = Promise.all(clipPromises).then(() => {
+                const weights = emitter.clips.map((clip) => { return clip.weight || 1; });
+                const weightedSound = new WeightedSound(emitter.loop || false, emitter._babylonSounds, weights);
+                if (emitter.innerAngle) { weightedSound.directionalConeInnerAngle = 2 * Tools.ToDegrees(emitter.innerAngle); }
+                if (emitter.outerAngle) { weightedSound.directionalConeOuterAngle = 2 * Tools.ToDegrees(emitter.outerAngle); }
+                if (emitter.volume) { weightedSound.volume = emitter.volume; }
+                emitter._babylonData!.sound = weightedSound;
             });
 
-            return clip._objectURL;
+            emitter._babylonData = {
+                loaded: promise
+            };
         }
 
-        private _loadEmitterAsync(context: string, emitter: ILoaderEmitter): Promise<void> {
-            emitter._babylonSounds = emitter._babylonSounds || [];
-            if (!emitter._babylonData) {
-                const clipPromises = new Array<Promise<any>>();
-                const name = emitter.name || `emitter${emitter.index}`;
-                const options = {
-                    loop: false,
-                    autoplay: false,
-                    volume: emitter.volume == undefined ? 1 : emitter.volume,
-                };
-
-                for (let i = 0; i < emitter.clips.length; i++) {
-                    const clipContext = `#/extensions/${this.name}/clips`;
-                    const clip = ArrayItem.Get(clipContext, this._clips, emitter.clips[i].clip);
-                    clipPromises.push(this._loadClipAsync(`${clipContext}/${emitter.clips[i].clip}`, clip).then((objectURL: string) => {
-                        const sound = emitter._babylonSounds[i] = new Sound(name, objectURL, this._loader.babylonScene, null, options);
-                        sound.refDistance = emitter.refDistance || 1;
-                        sound.maxDistance = emitter.maxDistance || 256;
-                        sound.rolloffFactor = emitter.rolloffFactor || 1;
-                        sound.distanceModel = emitter.distanceModel || 'exponential';
-                        sound._positionInEmitterSpace = true;
-                    }));
-                }
-
-                const promise = Promise.all(clipPromises).then(() => {
-                    const weights = emitter.clips.map((clip) => { return clip.weight || 1; });
-                    const weightedSound = new WeightedSound(emitter.loop || false, emitter._babylonSounds, weights);
-                    if (emitter.innerAngle) { weightedSound.directionalConeInnerAngle = 2 * Tools.ToDegrees(emitter.innerAngle); }
-                    if (emitter.outerAngle) { weightedSound.directionalConeOuterAngle = 2 * Tools.ToDegrees(emitter.outerAngle); }
-                    if (emitter.volume) { weightedSound.volume = emitter.volume; }
-                    emitter._babylonData!.sound = weightedSound;
-                });
+        return emitter._babylonData.loaded;
+    }
 
-                emitter._babylonData = {
-                    loaded: promise
+    private _getEventAction(context: string, sound: WeightedSound, action: AnimationEventAction, time: number, startOffset?: number): (currentFrame: number) => void {
+        switch (action) {
+            case AnimationEventAction.play: {
+                return (currentFrame: number) => {
+                    const frameOffset = (startOffset || 0) + (currentFrame - time);
+                    sound.play(frameOffset);
                 };
             }
-
-            return emitter._babylonData.loaded;
-        }
-
-        private _getEventAction(context: string, sound: WeightedSound, action: AnimationEventAction, time: number, startOffset?: number): (currentFrame: number) => void {
-            switch (action) {
-                case AnimationEventAction.play: {
-                    return (currentFrame: number) => {
-                        const frameOffset = (startOffset || 0) + (currentFrame - time);
-                        sound.play(frameOffset);
-                    };
-                }
-                case AnimationEventAction.stop: {
-                    return (currentFrame: number) => {
-                        sound.stop();
-                    };
-                }
-                case AnimationEventAction.pause: {
-                    return (currentFrame: number) => {
-                        sound.pause();
-                    };
-                }
-                default: {
-                    throw new Error(`${context}: Unsupported action ${action}`);
-                }
+            case AnimationEventAction.stop: {
+                return (currentFrame: number) => {
+                    sound.stop();
+                };
+            }
+            case AnimationEventAction.pause: {
+                return (currentFrame: number) => {
+                    sound.pause();
+                };
+            }
+            default: {
+                throw new Error(`${context}: Unsupported action ${action}`);
             }
         }
+    }
 
-        private _loadAnimationEventAsync(context: string, animationContext: string, animation: IAnimation, event: ILoaderAnimationEvent, babylonAnimationGroup: AnimationGroup): Promise<void> {
-            if (babylonAnimationGroup.targetedAnimations.length == 0) {
-                return Promise.resolve();
-            }
-            const babylonAnimation = babylonAnimationGroup.targetedAnimations[0];
-            const emitterIndex = event.emitter;
-            const emitter = ArrayItem.Get(`#/extensions/${this.name}/emitters`, this._emitters, emitterIndex);
-            return this._loadEmitterAsync(context, emitter).then(() => {
-                const sound = emitter._babylonData!.sound;
-                if (sound) {
-                    var babylonAnimationEvent = new AnimationEvent(event.time, this._getEventAction(context, sound, event.action, event.time, event.startOffset));
-                    babylonAnimation.animation.addEvent(babylonAnimationEvent);
-                    // Make sure all started audio stops when this animation is terminated.
-                    babylonAnimationGroup.onAnimationGroupEndObservable.add(() => {
-                        sound.stop();
-                    });
-                    babylonAnimationGroup.onAnimationGroupPauseObservable.add(() => {
-                        sound.pause();
-                    });
-                }
-            });
+    private _loadAnimationEventAsync(context: string, animationContext: string, animation: IAnimation, event: ILoaderAnimationEvent, babylonAnimationGroup: AnimationGroup): Promise<void> {
+        if (babylonAnimationGroup.targetedAnimations.length == 0) {
+            return Promise.resolve();
         }
+        const babylonAnimation = babylonAnimationGroup.targetedAnimations[0];
+        const emitterIndex = event.emitter;
+        const emitter = ArrayItem.Get(`#/extensions/${this.name}/emitters`, this._emitters, emitterIndex);
+        return this._loadEmitterAsync(context, emitter).then(() => {
+            const sound = emitter._babylonData!.sound;
+            if (sound) {
+                var babylonAnimationEvent = new AnimationEvent(event.time, this._getEventAction(context, sound, event.action, event.time, event.startOffset));
+                babylonAnimation.animation.addEvent(babylonAnimationEvent);
+                // Make sure all started audio stops when this animation is terminated.
+                babylonAnimationGroup.onAnimationGroupEndObservable.add(() => {
+                    sound.stop();
+                });
+                babylonAnimationGroup.onAnimationGroupPauseObservable.add(() => {
+                    sound.pause();
+                });
+            }
+        });
     }
+}
 
-    GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_audio_emitter(loader));
+GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_audio_emitter(loader));

+ 216 - 216
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -9,279 +9,279 @@ import { INode, IMaterial } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
 
-    const NAME = "MSFT_lod";
+const NAME = "MSFT_lod";
 
-    interface IMSFTLOD {
-        ids: number[];
-    }
+interface IMSFTLOD {
+    ids: number[];
+}
+
+/**
+ * [Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod)
+ */
+export class MSFT_lod implements IGLTFLoaderExtension {
+    /** The name of this extension. */
+    public readonly name = NAME;
+
+    /** Defines whether this extension is enabled. */
+    public enabled = true;
 
     /**
-     * [Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod)
+     * Maximum number of LODs to load, starting from the lowest LOD.
      */
-    export class MSFT_lod implements IGLTFLoaderExtension {
-        /** The name of this extension. */
-        public readonly name = NAME;
-
-        /** Defines whether this extension is enabled. */
-        public enabled = true;
-
-        /**
-         * Maximum number of LODs to load, starting from the lowest LOD.
-         */
-        public maxLODsToLoad = Number.MAX_VALUE;
-
-        /**
-         * Observable raised when all node LODs of one level are loaded.
-         * The event data is the index of the loaded LOD starting from zero.
-         * Dispose the loader to cancel the loading of the next level of LODs.
-         */
-        public onNodeLODsLoadedObservable = new Observable<number>();
-
-        /**
-         * Observable raised when all material LODs of one level are loaded.
-         * The event data is the index of the loaded LOD starting from zero.
-         * Dispose the loader to cancel the loading of the next level of LODs.
-         */
-        public onMaterialLODsLoadedObservable = new Observable<number>();
-
-        private _loader: GLTFLoader;
-
-        private _nodeIndexLOD: Nullable<number> = null;
-        private _nodeSignalLODs = new Array<Deferred<void>>();
-        private _nodePromiseLODs = new Array<Array<Promise<any>>>();
-
-        private _materialIndexLOD: Nullable<number> = null;
-        private _materialSignalLODs = new Array<Deferred<void>>();
-        private _materialPromiseLODs = new Array<Array<Promise<any>>>();
-
-        /** @hidden */
-        constructor(loader: GLTFLoader) {
-            this._loader = loader;
-        }
+    public maxLODsToLoad = Number.MAX_VALUE;
 
-        /** @hidden */
-        public dispose() {
-            delete this._loader;
+    /**
+     * Observable raised when all node LODs of one level are loaded.
+     * The event data is the index of the loaded LOD starting from zero.
+     * Dispose the loader to cancel the loading of the next level of LODs.
+     */
+    public onNodeLODsLoadedObservable = new Observable<number>();
 
-            this._nodeIndexLOD = null;
-            this._nodeSignalLODs.length = 0;
-            this._nodePromiseLODs.length = 0;
+    /**
+     * Observable raised when all material LODs of one level are loaded.
+     * The event data is the index of the loaded LOD starting from zero.
+     * Dispose the loader to cancel the loading of the next level of LODs.
+     */
+    public onMaterialLODsLoadedObservable = new Observable<number>();
 
-            this._materialIndexLOD = null;
-            this._materialSignalLODs.length = 0;
-            this._materialPromiseLODs.length = 0;
+    private _loader: GLTFLoader;
 
-            this.onMaterialLODsLoadedObservable.clear();
-            this.onNodeLODsLoadedObservable.clear();
-        }
+    private _nodeIndexLOD: Nullable<number> = null;
+    private _nodeSignalLODs = new Array<Deferred<void>>();
+    private _nodePromiseLODs = new Array<Array<Promise<any>>>();
 
-        /** @hidden */
-        public onReady(): void {
-            for (let indexLOD = 0; indexLOD < this._nodePromiseLODs.length; indexLOD++) {
-                const promise = Promise.all(this._nodePromiseLODs[indexLOD]).then(() => {
-                    if (indexLOD !== 0) {
-                        this._loader.endPerformanceCounter(`Node LOD ${indexLOD}`);
-                    }
+    private _materialIndexLOD: Nullable<number> = null;
+    private _materialSignalLODs = new Array<Deferred<void>>();
+    private _materialPromiseLODs = new Array<Array<Promise<any>>>();
 
-                    this._loader.log(`Loaded node LOD ${indexLOD}`);
-                    this.onNodeLODsLoadedObservable.notifyObservers(indexLOD);
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
+    }
 
-                    if (indexLOD !== this._nodePromiseLODs.length - 1) {
-                        this._loader.startPerformanceCounter(`Node LOD ${indexLOD + 1}`);
-                        if (this._nodeSignalLODs[indexLOD]) {
-                            this._nodeSignalLODs[indexLOD].resolve();
-                        }
-                    }
-                });
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
 
-                this._loader._completePromises.push(promise);
-            }
+        this._nodeIndexLOD = null;
+        this._nodeSignalLODs.length = 0;
+        this._nodePromiseLODs.length = 0;
 
-            for (let indexLOD = 0; indexLOD < this._materialPromiseLODs.length; indexLOD++) {
-                const promise = Promise.all(this._materialPromiseLODs[indexLOD]).then(() => {
-                    if (indexLOD !== 0) {
-                        this._loader.endPerformanceCounter(`Material LOD ${indexLOD}`);
-                    }
+        this._materialIndexLOD = null;
+        this._materialSignalLODs.length = 0;
+        this._materialPromiseLODs.length = 0;
 
-                    this._loader.log(`Loaded material LOD ${indexLOD}`);
-                    this.onMaterialLODsLoadedObservable.notifyObservers(indexLOD);
+        this.onMaterialLODsLoadedObservable.clear();
+        this.onNodeLODsLoadedObservable.clear();
+    }
 
-                    if (indexLOD !== this._materialPromiseLODs.length - 1) {
-                        this._loader.startPerformanceCounter(`Material LOD ${indexLOD + 1}`);
-                        if (this._materialSignalLODs[indexLOD]) {
-                            this._materialSignalLODs[indexLOD].resolve();
-                        }
+    /** @hidden */
+    public onReady(): void {
+        for (let indexLOD = 0; indexLOD < this._nodePromiseLODs.length; indexLOD++) {
+            const promise = Promise.all(this._nodePromiseLODs[indexLOD]).then(() => {
+                if (indexLOD !== 0) {
+                    this._loader.endPerformanceCounter(`Node LOD ${indexLOD}`);
+                }
+
+                this._loader.log(`Loaded node LOD ${indexLOD}`);
+                this.onNodeLODsLoadedObservable.notifyObservers(indexLOD);
+
+                if (indexLOD !== this._nodePromiseLODs.length - 1) {
+                    this._loader.startPerformanceCounter(`Node LOD ${indexLOD + 1}`);
+                    if (this._nodeSignalLODs[indexLOD]) {
+                        this._nodeSignalLODs[indexLOD].resolve();
                     }
-                });
+                }
+            });
 
-                this._loader._completePromises.push(promise);
-            }
+            this._loader._completePromises.push(promise);
         }
 
-        /** @hidden */
-        public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
-            return GLTFLoader.LoadExtensionAsync<IMSFTLOD, TransformNode>(context, node, this.name, (extensionContext, extension) => {
-                let firstPromise: Promise<TransformNode>;
-
-                const nodeLODs = this._getLODs(extensionContext, node, this._loader.gltf.nodes, extension.ids);
-                this._loader.logOpen(`${extensionContext}`);
+        for (let indexLOD = 0; indexLOD < this._materialPromiseLODs.length; indexLOD++) {
+            const promise = Promise.all(this._materialPromiseLODs[indexLOD]).then(() => {
+                if (indexLOD !== 0) {
+                    this._loader.endPerformanceCounter(`Material LOD ${indexLOD}`);
+                }
 
-                for (let indexLOD = 0; indexLOD < nodeLODs.length; indexLOD++) {
-                    const nodeLOD = nodeLODs[indexLOD];
+                this._loader.log(`Loaded material LOD ${indexLOD}`);
+                this.onMaterialLODsLoadedObservable.notifyObservers(indexLOD);
 
-                    if (indexLOD !== 0) {
-                        this._nodeIndexLOD = indexLOD;
-                        this._nodeSignalLODs[indexLOD] = this._nodeSignalLODs[indexLOD] || new Deferred();
+                if (indexLOD !== this._materialPromiseLODs.length - 1) {
+                    this._loader.startPerformanceCounter(`Material LOD ${indexLOD + 1}`);
+                    if (this._materialSignalLODs[indexLOD]) {
+                        this._materialSignalLODs[indexLOD].resolve();
                     }
+                }
+            });
 
-                    const assign = (babylonTransformNode: TransformNode) => { babylonTransformNode.setEnabled(false); };
-                    const promise = this._loader.loadNodeAsync(`#/nodes/${nodeLOD.index}`, nodeLOD, assign).then((babylonMesh) => {
-                        if (indexLOD !== 0) {
-                            // TODO: should not rely on _babylonMesh
-                            const previousNodeLOD = nodeLODs[indexLOD - 1];
-                            if (previousNodeLOD._babylonTransformNode) {
-                                previousNodeLOD._babylonTransformNode.dispose();
-                                delete previousNodeLOD._babylonTransformNode;
-                                this._disposeUnusedMaterials();
-                            }
-                        }
+            this._loader._completePromises.push(promise);
+        }
+    }
 
-                        babylonMesh.setEnabled(true);
-                        return babylonMesh;
-                    });
+    /** @hidden */
+    public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
+        return GLTFLoader.LoadExtensionAsync<IMSFTLOD, TransformNode>(context, node, this.name, (extensionContext, extension) => {
+            let firstPromise: Promise<TransformNode>;
 
-                    if (indexLOD === 0) {
-                        firstPromise = promise;
-                    }
-                    else {
-                        this._nodeIndexLOD = null;
-                    }
+            const nodeLODs = this._getLODs(extensionContext, node, this._loader.gltf.nodes, extension.ids);
+            this._loader.logOpen(`${extensionContext}`);
+
+            for (let indexLOD = 0; indexLOD < nodeLODs.length; indexLOD++) {
+                const nodeLOD = nodeLODs[indexLOD];
 
-                    this._nodePromiseLODs[indexLOD] = this._nodePromiseLODs[indexLOD] || [];
-                    this._nodePromiseLODs[indexLOD].push(promise);
+                if (indexLOD !== 0) {
+                    this._nodeIndexLOD = indexLOD;
+                    this._nodeSignalLODs[indexLOD] = this._nodeSignalLODs[indexLOD] || new Deferred();
                 }
 
-                this._loader.logClose();
-                return firstPromise!;
-            });
-        }
+                const assign = (babylonTransformNode: TransformNode) => { babylonTransformNode.setEnabled(false); };
+                const promise = this._loader.loadNodeAsync(`#/nodes/${nodeLOD.index}`, nodeLOD, assign).then((babylonMesh) => {
+                    if (indexLOD !== 0) {
+                        // TODO: should not rely on _babylonMesh
+                        const previousNodeLOD = nodeLODs[indexLOD - 1];
+                        if (previousNodeLOD._babylonTransformNode) {
+                            previousNodeLOD._babylonTransformNode.dispose();
+                            delete previousNodeLOD._babylonTransformNode;
+                            this._disposeUnusedMaterials();
+                        }
+                    }
+
+                    babylonMesh.setEnabled(true);
+                    return babylonMesh;
+                });
 
-        /** @hidden */
-        public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
-            // Don't load material LODs if already loading a node LOD.
-            if (this._nodeIndexLOD) {
-                return null;
+                if (indexLOD === 0) {
+                    firstPromise = promise;
+                }
+                else {
+                    this._nodeIndexLOD = null;
+                }
+
+                this._nodePromiseLODs[indexLOD] = this._nodePromiseLODs[indexLOD] || [];
+                this._nodePromiseLODs[indexLOD].push(promise);
             }
 
-            return GLTFLoader.LoadExtensionAsync<IMSFTLOD, Material>(context, material, this.name, (extensionContext, extension) => {
-                let firstPromise: Promise<Material>;
+            this._loader.logClose();
+            return firstPromise!;
+        });
+    }
 
-                const materialLODs = this._getLODs(extensionContext, material, this._loader.gltf.materials, extension.ids);
-                this._loader.logOpen(`${extensionContext}`);
+    /** @hidden */
+    public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
+        // Don't load material LODs if already loading a node LOD.
+        if (this._nodeIndexLOD) {
+            return null;
+        }
 
-                for (let indexLOD = 0; indexLOD < materialLODs.length; indexLOD++) {
-                    const materialLOD = materialLODs[indexLOD];
+        return GLTFLoader.LoadExtensionAsync<IMSFTLOD, Material>(context, material, this.name, (extensionContext, extension) => {
+            let firstPromise: Promise<Material>;
 
-                    if (indexLOD !== 0) {
-                        this._materialIndexLOD = indexLOD;
-                    }
+            const materialLODs = this._getLODs(extensionContext, material, this._loader.gltf.materials, extension.ids);
+            this._loader.logOpen(`${extensionContext}`);
 
-                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD.index}`, materialLOD, babylonMesh, babylonDrawMode, (babylonMaterial) => {
-                        if (indexLOD === 0) {
-                            assign(babylonMaterial);
-                        }
-                    }).then((babylonMaterial) => {
-                        if (indexLOD !== 0) {
-                            assign(babylonMaterial);
-
-                            // TODO: should not rely on _data
-                            const previousDataLOD = materialLODs[indexLOD - 1]._data!;
-                            if (previousDataLOD[babylonDrawMode]) {
-                                previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
-                                delete previousDataLOD[babylonDrawMode];
-                            }
-                        }
+            for (let indexLOD = 0; indexLOD < materialLODs.length; indexLOD++) {
+                const materialLOD = materialLODs[indexLOD];
 
-                        return babylonMaterial;
-                    });
+                if (indexLOD !== 0) {
+                    this._materialIndexLOD = indexLOD;
+                }
 
+                const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD.index}`, materialLOD, babylonMesh, babylonDrawMode, (babylonMaterial) => {
                     if (indexLOD === 0) {
-                        firstPromise = promise;
+                        assign(babylonMaterial);
                     }
-                    else {
-                        this._materialIndexLOD = null;
+                }).then((babylonMaterial) => {
+                    if (indexLOD !== 0) {
+                        assign(babylonMaterial);
+
+                        // TODO: should not rely on _data
+                        const previousDataLOD = materialLODs[indexLOD - 1]._data!;
+                        if (previousDataLOD[babylonDrawMode]) {
+                            previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
+                            delete previousDataLOD[babylonDrawMode];
+                        }
                     }
 
-                    this._materialPromiseLODs[indexLOD] = this._materialPromiseLODs[indexLOD] || [];
-                    this._materialPromiseLODs[indexLOD].push(promise);
+                    return babylonMaterial;
+                });
+
+                if (indexLOD === 0) {
+                    firstPromise = promise;
+                }
+                else {
+                    this._materialIndexLOD = null;
                 }
 
-                this._loader.logClose();
-                return firstPromise!;
+                this._materialPromiseLODs[indexLOD] = this._materialPromiseLODs[indexLOD] || [];
+                this._materialPromiseLODs[indexLOD].push(promise);
+            }
+
+            this._loader.logClose();
+            return firstPromise!;
+        });
+    }
+
+    /** @hidden */
+    public _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> {
+        // Defer the loading of uris if loading a material or node LOD.
+        if (this._materialIndexLOD !== null) {
+            this._loader.log(`deferred`);
+            const previousIndexLOD = this._materialIndexLOD - 1;
+            this._materialSignalLODs[previousIndexLOD] = this._materialSignalLODs[previousIndexLOD] || new Deferred<void>();
+            return this._materialSignalLODs[previousIndexLOD].promise.then(() => {
+                return this._loader.loadUriAsync(context, uri);
+            });
+        }
+        else if (this._nodeIndexLOD !== null) {
+            this._loader.log(`deferred`);
+            const previousIndexLOD = this._nodeIndexLOD - 1;
+            this._nodeSignalLODs[previousIndexLOD] = this._nodeSignalLODs[previousIndexLOD] || new Deferred<void>();
+            return this._nodeSignalLODs[this._nodeIndexLOD - 1].promise.then(() => {
+                return this._loader.loadUriAsync(context, uri);
             });
         }
 
-        /** @hidden */
-        public _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> {
-            // Defer the loading of uris if loading a material or node LOD.
-            if (this._materialIndexLOD !== null) {
-                this._loader.log(`deferred`);
-                const previousIndexLOD = this._materialIndexLOD - 1;
-                this._materialSignalLODs[previousIndexLOD] = this._materialSignalLODs[previousIndexLOD] || new Deferred<void>();
-                return this._materialSignalLODs[previousIndexLOD].promise.then(() => {
-                    return this._loader.loadUriAsync(context, uri);
-                });
-            }
-            else if (this._nodeIndexLOD !== null) {
-                this._loader.log(`deferred`);
-                const previousIndexLOD = this._nodeIndexLOD - 1;
-                this._nodeSignalLODs[previousIndexLOD] = this._nodeSignalLODs[previousIndexLOD] || new Deferred<void>();
-                return this._nodeSignalLODs[this._nodeIndexLOD - 1].promise.then(() => {
-                    return this._loader.loadUriAsync(context, uri);
-                });
-            }
+        return null;
+    }
 
-            return null;
+    /**
+     * Gets an array of LOD properties from lowest to highest.
+     */
+    private _getLODs<T>(context: string, property: T, array: ArrayLike<T> | undefined, ids: number[]): T[] {
+        if (this.maxLODsToLoad <= 0) {
+            throw new Error("maxLODsToLoad must be greater than zero");
         }
 
-        /**
-         * Gets an array of LOD properties from lowest to highest.
-         */
-        private _getLODs<T>(context: string, property: T, array: ArrayLike<T> | undefined, ids: number[]): T[] {
-            if (this.maxLODsToLoad <= 0) {
-                throw new Error("maxLODsToLoad must be greater than zero");
-            }
-
-            const properties = new Array<T>();
+        const properties = new Array<T>();
 
-            for (let i = ids.length - 1; i >= 0; i--) {
-                properties.push(ArrayItem.Get(`${context}/ids/${ids[i]}`, array, ids[i]));
-                if (properties.length === this.maxLODsToLoad) {
-                    return properties;
-                }
+        for (let i = ids.length - 1; i >= 0; i--) {
+            properties.push(ArrayItem.Get(`${context}/ids/${ids[i]}`, array, ids[i]));
+            if (properties.length === this.maxLODsToLoad) {
+                return properties;
             }
-
-            properties.push(property);
-            return properties;
         }
 
-        private _disposeUnusedMaterials(): void {
-            // TODO: should not rely on _data
-            const materials = this._loader.gltf.materials;
-            if (materials) {
-                for (const material of materials) {
-                    if (material._data) {
-                        for (const drawMode in material._data) {
-                            const data = material._data[drawMode];
-                            if (data.babylonMeshes.length === 0) {
-                                data.babylonMaterial.dispose(false, true);
-                                delete material._data[drawMode];
-                            }
+        properties.push(property);
+        return properties;
+    }
+
+    private _disposeUnusedMaterials(): void {
+        // TODO: should not rely on _data
+        const materials = this._loader.gltf.materials;
+        if (materials) {
+            for (const material of materials) {
+                if (material._data) {
+                    for (const drawMode in material._data) {
+                        const data = material._data[drawMode];
+                        if (data.babylonMeshes.length === 0) {
+                            data.babylonMaterial.dispose(false, true);
+                            delete material._data[drawMode];
                         }
                     }
                 }
             }
         }
     }
+}
 
-    GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_lod(loader));
+GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_lod(loader));

文件差異過大導致無法顯示
+ 1726 - 1726
loaders/src/glTF/2.0/glTFLoader.ts


+ 87 - 87
loaders/src/glTF/2.0/glTFLoaderExtension.ts

@@ -11,103 +11,103 @@ import { IDisposable } from "babylonjs/scene";
 import { IScene, INode, ICamera, IMeshPrimitive, IMaterial, ITextureInfo, IAnimation } from "./glTFLoaderInterfaces";
 import { IGLTFLoaderExtension as IGLTFBaseLoaderExtension } from "../glTFFileLoader";
 
+/**
+ * Interface for a glTF loader extension.
+ */
+export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposable {
     /**
-     * Interface for a glTF loader extension.
+     * Called after the loader state changes to LOADING.
      */
-    export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposable {
-        /**
-         * Called after the loader state changes to LOADING.
-         */
-        onLoading?(): void;
+    onLoading?(): void;
 
-        /**
-         * Called after the loader state changes to READY.
-         */
-        onReady?(): void;
+    /**
+     * Called after the loader state changes to READY.
+     */
+    onReady?(): void;
 
-        /**
-         * Define this method to modify the default behavior when loading scenes.
-         * @param context The context when loading the asset
-         * @param scene The glTF scene property
-         * @returns A promise that resolves when the load is complete or null if not handled
-         */
-        loadSceneAsync?(context: string, scene: IScene): Nullable<Promise<void>>;
+    /**
+     * Define this method to modify the default behavior when loading scenes.
+     * @param context The context when loading the asset
+     * @param scene The glTF scene property
+     * @returns A promise that resolves when the load is complete or null if not handled
+     */
+    loadSceneAsync?(context: string, scene: IScene): Nullable<Promise<void>>;
 
-        /**
-         * Define this method to modify the default behavior when loading nodes.
-         * @param context The context when loading the asset
-         * @param node The glTF node property
-         * @param assign A function called synchronously after parsing the glTF properties
-         * @returns A promise that resolves with the loaded Babylon transform node when the load is complete or null if not handled
-         */
-        loadNodeAsync?(context: string, node: INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
+    /**
+     * Define this method to modify the default behavior when loading nodes.
+     * @param context The context when loading the asset
+     * @param node The glTF node property
+     * @param assign A function called synchronously after parsing the glTF properties
+     * @returns A promise that resolves with the loaded Babylon transform node when the load is complete or null if not handled
+     */
+    loadNodeAsync?(context: string, node: INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
 
-        /**
-         * Define this method to modify the default behavior when loading cameras.
-         * @param context The context when loading the asset
-         * @param camera The glTF camera property
-         * @param assign A function called synchronously after parsing the glTF properties
-         * @returns A promise that resolves with the loaded Babylon camera when the load is complete or null if not handled
-         */
-        loadCameraAsync?(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+    /**
+     * Define this method to modify the default behavior when loading cameras.
+     * @param context The context when loading the asset
+     * @param camera The glTF camera property
+     * @param assign A function called synchronously after parsing the glTF properties
+     * @returns A promise that resolves with the loaded Babylon camera when the load is complete or null if not handled
+     */
+    loadCameraAsync?(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
 
-        /**
-         * @hidden Define this method to modify the default behavior when loading vertex data for mesh primitives.
-         * @param context The context when loading the asset
-         * @param primitive The glTF mesh primitive property
-         * @returns A promise that resolves with the loaded geometry when the load is complete or null if not handled
-         */
-        _loadVertexDataAsync?(context: string, primitive: IMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>>;
+    /**
+     * @hidden Define this method to modify the default behavior when loading vertex data for mesh primitives.
+     * @param context The context when loading the asset
+     * @param primitive The glTF mesh primitive property
+     * @returns A promise that resolves with the loaded geometry when the load is complete or null if not handled
+     */
+    _loadVertexDataAsync?(context: string, primitive: IMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>>;
 
-        /**
-         * @hidden Define this method to modify the default behavior when loading materials. Load material creates the material and then loads material properties.
-         * @param context The context when loading the asset
-         * @param material The glTF material property
-         * @param assign A function called synchronously after parsing the glTF properties
-         * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
-         */
-        _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+    /**
+     * @hidden Define this method to modify the default behavior when loading materials. Load material creates the material and then loads material properties.
+     * @param context The context when loading the asset
+     * @param material The glTF material property
+     * @param assign A function called synchronously after parsing the glTF properties
+     * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
+     */
+    _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
 
-        /**
-         * Define this method to modify the default behavior when creating materials.
-         * @param context The context when loading the asset
-         * @param material The glTF material property
-         * @param babylonDrawMode The draw mode for the Babylon material
-         * @returns The Babylon material or null if not handled
-         */
-        createMaterial?(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+    /**
+     * Define this method to modify the default behavior when creating materials.
+     * @param context The context when loading the asset
+     * @param material The glTF material property
+     * @param babylonDrawMode The draw mode for the Babylon material
+     * @returns The Babylon material or null if not handled
+     */
+    createMaterial?(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
 
-        /**
-         * Define this method to modify the default behavior when loading material properties.
-         * @param context The context when loading the asset
-         * @param material The glTF material property
-         * @param babylonMaterial The Babylon material
-         * @returns A promise that resolves when the load is complete or null if not handled
-         */
-        loadMaterialPropertiesAsync?(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
+    /**
+     * Define this method to modify the default behavior when loading material properties.
+     * @param context The context when loading the asset
+     * @param material The glTF material property
+     * @param babylonMaterial The Babylon material
+     * @returns A promise that resolves when the load is complete or null if not handled
+     */
+    loadMaterialPropertiesAsync?(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
 
-        /**
-         * Define this method to modify the default behavior when loading texture infos.
-         * @param context The context when loading the asset
-         * @param textureInfo The glTF texture info property
-         * @param assign A function called synchronously after parsing the glTF properties
-         * @returns A promise that resolves with the loaded Babylon texture when the load is complete or null if not handled
-         */
-        loadTextureInfoAsync?(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>>;
+    /**
+     * Define this method to modify the default behavior when loading texture infos.
+     * @param context The context when loading the asset
+     * @param textureInfo The glTF texture info property
+     * @param assign A function called synchronously after parsing the glTF properties
+     * @returns A promise that resolves with the loaded Babylon texture when the load is complete or null if not handled
+     */
+    loadTextureInfoAsync?(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>>;
 
-        /**
-         * Define this method to modify the default behavior when loading animations.
-         * @param context The context when loading the asset
-         * @param animation The glTF animation property
-         * @returns A promise that resolves with the loaded Babylon animation group when the load is complete or null if not handled
-         */
-        loadAnimationAsync?(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
+    /**
+     * Define this method to modify the default behavior when loading animations.
+     * @param context The context when loading the asset
+     * @param animation The glTF animation property
+     * @returns A promise that resolves with the loaded Babylon animation group when the load is complete or null if not handled
+     */
+    loadAnimationAsync?(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
 
-        /**
-         * Define this method to modify the default behavior when loading uris.
-         * @param context The context when loading the asset
-         * @param uri The uri to load
-         * @returns A promise that resolves with the loaded data when the load is complete or null if not handled
-         */
-        _loadUriAsync?(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
-    }
+    /**
+     * Define this method to modify the default behavior when loading uris.
+     * @param context The context when loading the asset
+     * @param uri The uri to load
+     * @returns A promise that resolves with the loaded data when the load is complete or null if not handled
+     */
+    _loadUriAsync?(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
+}

+ 209 - 209
loaders/src/glTF/2.0/glTFLoaderInterfaces.ts

@@ -9,231 +9,231 @@ import { Mesh } from "babylonjs/Meshes/mesh";
 
 import * as IGLTF2 from "babylonjs-gltf2interface";
 
+/**
+ * Loader interface with an index field.
+ */
+export interface IArrayItem {
     /**
-     * Loader interface with an index field.
+     * The index of this item in the array.
      */
-    export interface IArrayItem {
-        /**
-         * The index of this item in the array.
-         */
-        index: number;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IAccessor extends IGLTF2.IAccessor, IArrayItem {
-        /** @hidden */
-        _data?: Promise<ArrayBufferView>;
-
-        /** @hidden */
-        _babylonVertexBuffer?: Promise<VertexBuffer>;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IAnimationChannel extends IGLTF2.IAnimationChannel, IArrayItem {
-    }
+    index: number;
+}
 
+/**
+ * Loader interface with additional members.
+ */
+export interface IAccessor extends IGLTF2.IAccessor, IArrayItem {
     /** @hidden */
-    export interface _IAnimationSamplerData {
-        input: Float32Array;
-        interpolation: IGLTF2.AnimationSamplerInterpolation;
-        output: Float32Array;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IAnimationSampler extends IGLTF2.IAnimationSampler, IArrayItem {
-        /** @hidden */
-        _data?: Promise<_IAnimationSamplerData>;
-    }
+    _data?: Promise<ArrayBufferView>;
 
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IAnimation extends IGLTF2.IAnimation, IArrayItem {
-        channels: IAnimationChannel[];
-        samplers: IAnimationSampler[];
-
-        /** @hidden */
-        _babylonAnimationGroup?: AnimationGroup;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IBuffer extends IGLTF2.IBuffer, IArrayItem {
-        /** @hidden */
-        _data?: Promise<ArrayBufferView>;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IBufferView extends IGLTF2.IBufferView, IArrayItem {
-        /** @hidden */
-        _data?: Promise<ArrayBufferView>;
-
-        /** @hidden */
-        _babylonBuffer?: Promise<Buffer>;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface ICamera extends IGLTF2.ICamera, IArrayItem {
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IImage extends IGLTF2.IImage, IArrayItem {
-        /** @hidden */
-        _data?: Promise<ArrayBufferView>;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IMaterialNormalTextureInfo extends IGLTF2.IMaterialNormalTextureInfo, ITextureInfo {
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IMaterialOcclusionTextureInfo extends IGLTF2.IMaterialOcclusionTextureInfo, ITextureInfo {
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IMaterialPbrMetallicRoughness extends IGLTF2.IMaterialPbrMetallicRoughness {
-        baseColorTexture?: ITextureInfo;
-        metallicRoughnessTexture?: ITextureInfo;
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IMaterial extends IGLTF2.IMaterial, IArrayItem {
-        pbrMetallicRoughness?: IMaterialPbrMetallicRoughness;
-        normalTexture?: IMaterialNormalTextureInfo;
-        occlusionTexture?: IMaterialOcclusionTextureInfo;
-        emissiveTexture?: ITextureInfo;
-
-        /** @hidden */
-        _data?: {
-            [babylonDrawMode: number]: {
-                babylonMaterial: Material;
-                babylonMeshes: AbstractMesh[];
-                promise: Promise<void>;
-            }
-        };
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IMesh extends IGLTF2.IMesh, IArrayItem {
-        primitives: IMeshPrimitive[];
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IMeshPrimitive extends IGLTF2.IMeshPrimitive, IArrayItem {
-        /** @hidden */
-        _instanceData?: {
-            babylonSourceMesh: Mesh;
-            promise: Promise<any>;
-        };
-    }
-
-    /**
-     * Loader interface with additional members.
-     */
-    export interface INode extends IGLTF2.INode, IArrayItem {
-        /**
-         * The parent glTF node.
-         */
-        parent?: INode;
+    /** @hidden */
+    _babylonVertexBuffer?: Promise<VertexBuffer>;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IAnimationChannel extends IGLTF2.IAnimationChannel, IArrayItem {
+}
+
+/** @hidden */
+export interface _IAnimationSamplerData {
+    input: Float32Array;
+    interpolation: IGLTF2.AnimationSamplerInterpolation;
+    output: Float32Array;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IAnimationSampler extends IGLTF2.IAnimationSampler, IArrayItem {
+    /** @hidden */
+    _data?: Promise<_IAnimationSamplerData>;
+}
 
-        /** @hidden */
-        _babylonTransformNode?: TransformNode;
+/**
+ * Loader interface with additional members.
+ */
+export interface IAnimation extends IGLTF2.IAnimation, IArrayItem {
+    channels: IAnimationChannel[];
+    samplers: IAnimationSampler[];
 
-        /** @hidden */
-        _primitiveBabylonMeshes?: AbstractMesh[];
+    /** @hidden */
+    _babylonAnimationGroup?: AnimationGroup;
+}
 
-        /** @hidden */
-        _babylonBones?: Bone[];
+/**
+ * Loader interface with additional members.
+ */
+export interface IBuffer extends IGLTF2.IBuffer, IArrayItem {
+    /** @hidden */
+    _data?: Promise<ArrayBufferView>;
+}
 
-        /** @hidden */
-        _numMorphTargets?: number;
-    }
+/**
+ * Loader interface with additional members.
+ */
+export interface IBufferView extends IGLTF2.IBufferView, IArrayItem {
+    /** @hidden */
+    _data?: Promise<ArrayBufferView>;
 
     /** @hidden */
-    export interface _ISamplerData {
-        noMipMaps: boolean;
-        samplingMode: number;
-        wrapU: number;
-        wrapV: number;
-    }
+    _babylonBuffer?: Promise<Buffer>;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface ICamera extends IGLTF2.ICamera, IArrayItem {
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IImage extends IGLTF2.IImage, IArrayItem {
+    /** @hidden */
+    _data?: Promise<ArrayBufferView>;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IMaterialNormalTextureInfo extends IGLTF2.IMaterialNormalTextureInfo, ITextureInfo {
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IMaterialOcclusionTextureInfo extends IGLTF2.IMaterialOcclusionTextureInfo, ITextureInfo {
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IMaterialPbrMetallicRoughness extends IGLTF2.IMaterialPbrMetallicRoughness {
+    baseColorTexture?: ITextureInfo;
+    metallicRoughnessTexture?: ITextureInfo;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IMaterial extends IGLTF2.IMaterial, IArrayItem {
+    pbrMetallicRoughness?: IMaterialPbrMetallicRoughness;
+    normalTexture?: IMaterialNormalTextureInfo;
+    occlusionTexture?: IMaterialOcclusionTextureInfo;
+    emissiveTexture?: ITextureInfo;
 
-    /**
-     * Loader interface with additional members.
-     */
-    export interface ISampler extends IGLTF2.ISampler, IArrayItem {
-        /** @hidden */
-        _data?: _ISamplerData;
-    }
+    /** @hidden */
+    _data?: {
+        [babylonDrawMode: number]: {
+            babylonMaterial: Material;
+            babylonMeshes: AbstractMesh[];
+            promise: Promise<void>;
+        }
+    };
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IMesh extends IGLTF2.IMesh, IArrayItem {
+    primitives: IMeshPrimitive[];
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IMeshPrimitive extends IGLTF2.IMeshPrimitive, IArrayItem {
+    /** @hidden */
+    _instanceData?: {
+        babylonSourceMesh: Mesh;
+        promise: Promise<any>;
+    };
+}
 
+/**
+ * Loader interface with additional members.
+ */
+export interface INode extends IGLTF2.INode, IArrayItem {
     /**
-     * Loader interface with additional members.
+     * The parent glTF node.
      */
-    export interface IScene extends IGLTF2.IScene, IArrayItem {
-    }
+    parent?: INode;
 
-    /**
-     * Loader interface with additional members.
-     */
-    export interface ISkin extends IGLTF2.ISkin, IArrayItem {
-        /** @hidden */
-        _data?: {
-            babylonSkeleton: Skeleton;
-            promise: Promise<void>;
-        };
-    }
+    /** @hidden */
+    _babylonTransformNode?: TransformNode;
 
-    /**
-     * Loader interface with additional members.
-     */
-    export interface ITexture extends IGLTF2.ITexture, IArrayItem {
-    }
+    /** @hidden */
+    _primitiveBabylonMeshes?: AbstractMesh[];
 
-    /**
-     * Loader interface with additional members.
-     */
-    export interface ITextureInfo extends IGLTF2.ITextureInfo {
-    }
+    /** @hidden */
+    _babylonBones?: Bone[];
 
-    /**
-     * Loader interface with additional members.
-     */
-    export interface IGLTF extends IGLTF2.IGLTF {
-        accessors?: IAccessor[];
-        animations?: IAnimation[];
-        buffers?: IBuffer[];
-        bufferViews?: IBufferView[];
-        cameras?: ICamera[];
-        images?: IImage[];
-        materials?: IMaterial[];
-        meshes?: IMesh[];
-        nodes?: INode[];
-        samplers?: ISampler[];
-        scenes?: IScene[];
-        skins?: ISkin[];
-        textures?: ITexture[];
-    }
+    /** @hidden */
+    _numMorphTargets?: number;
+}
+
+/** @hidden */
+export interface _ISamplerData {
+    noMipMaps: boolean;
+    samplingMode: number;
+    wrapU: number;
+    wrapV: number;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface ISampler extends IGLTF2.ISampler, IArrayItem {
+    /** @hidden */
+    _data?: _ISamplerData;
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IScene extends IGLTF2.IScene, IArrayItem {
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface ISkin extends IGLTF2.ISkin, IArrayItem {
+    /** @hidden */
+    _data?: {
+        babylonSkeleton: Skeleton;
+        promise: Promise<void>;
+    };
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface ITexture extends IGLTF2.ITexture, IArrayItem {
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface ITextureInfo extends IGLTF2.ITextureInfo {
+}
+
+/**
+ * Loader interface with additional members.
+ */
+export interface IGLTF extends IGLTF2.IGLTF {
+    accessors?: IAccessor[];
+    animations?: IAnimation[];
+    buffers?: IBuffer[];
+    bufferViews?: IBufferView[];
+    cameras?: ICamera[];
+    images?: IImage[];
+    materials?: IMaterial[];
+    meshes?: IMesh[];
+    nodes?: INode[];
+    samplers?: ISampler[];
+    scenes?: IScene[];
+    skins?: ISkin[];
+    textures?: ITexture[];
+}

+ 1 - 1
loaders/src/glTF/glTFFileLoader.ts

@@ -608,7 +608,7 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
             this.onValidatedObservable.clear();
         }, (reason) => {
             this._endPerformanceCounter("Validate JSON");
-             Tools.Warn(`Failed to validate: ${reason}`);
+            Tools.Warn(`Failed to validate: ${reason}`);
             this.onValidatedObservable.clear();
         });
     }

+ 1 - 1
loaders/src/legacy/legacy-glTF.ts

@@ -6,7 +6,7 @@ import * as FileLoader from "../glTF/glTFFileLoader";
  */
 var globalObject = (typeof global !== 'undefined') ? global : ((typeof window !== 'undefined') ? window : undefined);
 if (typeof globalObject !== "undefined") {
-    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || { };
+    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || {};
     for (var key in FileLoader) {
         (<any>globalObject).BABYLON[key] = (<any>FileLoader)[key];
     }

+ 2 - 2
loaders/src/legacy/legacy-glTF1.ts

@@ -6,8 +6,8 @@ import * as GLTF1 from "../glTF/1.0";
  */
 var globalObject = (typeof global !== 'undefined') ? global : ((typeof window !== 'undefined') ? window : undefined);
 if (typeof globalObject !== "undefined") {
-    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || { };
-    (<any>globalObject).BABYLON.GLTF1 = (<any>globalObject).BABYLON.GLTF1 || { };
+    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || {};
+    (<any>globalObject).BABYLON.GLTF1 = (<any>globalObject).BABYLON.GLTF1 || {};
     for (var key in GLTF1) {
         (<any>globalObject).BABYLON.GLTF1[key] = (<any>GLTF1)[key];
     }

+ 4 - 4
loaders/src/legacy/legacy-glTF2.ts

@@ -8,11 +8,11 @@ import * as GLTF2 from "../glTF/2.0";
  */
 var globalObject = (typeof global !== 'undefined') ? global : ((typeof window !== 'undefined') ? window : undefined);
 if (typeof globalObject !== "undefined") {
-    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || { };
+    (<any>globalObject).BABYLON = (<any>globalObject).BABYLON || {};
     var BABYLON = (<any>globalObject).BABYLON;
-    BABYLON.GLTF2 = BABYLON.GLTF2 || { };
-    BABYLON.GLTF2.Loader = BABYLON.GLTF2.Loader || { };
-    BABYLON.GLTF2.Loader.Extensions = BABYLON.GLTF2.Loader.Extensions || { };
+    BABYLON.GLTF2 = BABYLON.GLTF2 || {};
+    BABYLON.GLTF2.Loader = BABYLON.GLTF2.Loader || {};
+    BABYLON.GLTF2.Loader.Extensions = BABYLON.GLTF2.Loader.Extensions || {};
 
     const keys = [];
     for (var key in Extensions) {

+ 5 - 5
materialsLibrary/src/custom/customMaterial.ts

@@ -51,8 +51,8 @@ export class CustomMaterial extends StandardMaterial {
     _newUniformInstances: any[];
     _newSamplerInstances: Texture[];
 
-    public  FragmentShader : string ;
-    public  VertexShader : string ;
+    public FragmentShader: string;
+    public VertexShader: string;
 
     public AttachAfterBind(mesh: Mesh, effect: Effect) {
         for (var el in this._newUniformInstances) {
@@ -129,7 +129,7 @@ export class CustomMaterial extends StandardMaterial {
             .replace('#define CUSTOM_VERTEX_UPDATE_POSITION', (this.CustomParts.Vertex_Before_PositionUpdated ? this.CustomParts.Vertex_Before_PositionUpdated : ""))
             .replace('#define CUSTOM_VERTEX_UPDATE_NORMAL', (this.CustomParts.Vertex_Before_NormalUpdated ? this.CustomParts.Vertex_Before_NormalUpdated : ""));
 
-            // #define CUSTOM_VERTEX_MAIN_END
+        // #define CUSTOM_VERTEX_MAIN_END
 
         Effect.ShadersStore[name + "PixelShader"] = this.FragmentShader
             .replace('#define CUSTOM_FRAGMENT_BEGIN', (this.CustomParts.Fragment_Begin ? this.CustomParts.Fragment_Begin : ""))
@@ -139,9 +139,9 @@ export class CustomMaterial extends StandardMaterial {
             .replace('#define CUSTOM_FRAGMENT_UPDATE_ALPHA', (this.CustomParts.Fragment_Custom_Alpha ? this.CustomParts.Fragment_Custom_Alpha : ""))
             .replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.CustomParts.Fragment_Before_FragColor ? this.CustomParts.Fragment_Before_FragColor : ""));
 
-            // #define CUSTOM_FRAGMENT_BEFORE_LIGHTS
+        // #define CUSTOM_FRAGMENT_BEFORE_LIGHTS
 
-            // #define CUSTOM_FRAGMENT_BEFORE_FOG
+        // #define CUSTOM_FRAGMENT_BEFORE_FOG
 
         this._isCreatedShader = true;
         this._createdShaderName = name;

+ 3 - 3
materialsLibrary/src/grid/gridMaterial.ts

@@ -171,7 +171,7 @@ export class GridMaterial extends PushMaterial {
             if (defines.UV1) {
                 attribs.push(VertexBuffer.UVKind);
             }
-             if (defines.UV2) {
+            if (defines.UV2) {
                 attribs.push(VertexBuffer.UV2Kind);
             }
 
@@ -180,7 +180,7 @@ export class GridMaterial extends PushMaterial {
             subMesh.setEffect(scene.getEngine().createEffect("grid",
                 attribs,
                 ["projection", "worldView", "mainColor", "lineColor", "gridControl", "gridOffset", "vFogInfos", "vFogColor", "world", "view",
-                "opacityMatrix", "vOpacityInfos"],
+                    "opacityMatrix", "vOpacityInfos"],
                 ["opacitySampler"],
                 join,
                 undefined,
@@ -233,7 +233,7 @@ export class GridMaterial extends PushMaterial {
 
             if (this._opacityTexture && MaterialFlags.OpacityTextureEnabled) {
                 this._activeEffect.setTexture("opacitySampler", this._opacityTexture);
-                 this._activeEffect.setFloat2("vOpacityInfos", this._opacityTexture.coordinatesIndex, this._opacityTexture.level);
+                this._activeEffect.setFloat2("vOpacityInfos", this._opacityTexture.coordinatesIndex, this._opacityTexture.level);
                 this._activeEffect.setMatrix("opacityMatrix", this._opacityTexture.getTextureMatrix());
             }
         }

+ 4 - 4
materialsLibrary/src/sky/skyMaterial.ts

@@ -45,9 +45,9 @@ export class SkyMaterial extends PushMaterial {
     @serialize()
     public luminance: number = 1.0;
 
-     /**
-     * Defines the amount (scattering) of haze as opposed to molecules in atmosphere.
-     */
+    /**
+    * Defines the amount (scattering) of haze as opposed to molecules in atmosphere.
+    */
     @serialize()
     public turbidity: number = 10.0;
 
@@ -335,7 +335,7 @@ export class SkyMaterial extends PushMaterial {
      */
     public serialize(): any {
         var serializationObject = SerializationHelper.Serialize(this);
-        serializationObject.customType  = "BABYLON.SkyMaterial";
+        serializationObject.customType = "BABYLON.SkyMaterial";
         return serializationObject;
     }
 

+ 1 - 1
materialsLibrary/src/water/waterMaterial.ts

@@ -182,7 +182,7 @@ export class WaterMaterial extends PushMaterial {
      */
     public get hasRenderTargetTextures(): boolean {
         return true;
-        }
+    }
 
     /**
     * Constructor

+ 2 - 2
serializers/src/glTF/2.0/glTFData.ts

@@ -48,10 +48,10 @@ export class GLTFData {
                 mimeType = { type: "model/gltf+json" };
             }
             else if (endsWith(key, ".jpeg" || ".jpg")) {
-                mimeType = {type: ImageMimeType.JPEG};
+                mimeType = { type: ImageMimeType.JPEG };
             }
             else if (endsWith(key, ".png")) {
-                mimeType = {type: ImageMimeType.PNG};
+                mimeType = { type: ImageMimeType.PNG };
             }
 
             link.href = window.URL.createObjectURL(new Blob([blob], mimeType));

+ 200 - 200
src/Actions/action.ts

@@ -11,242 +11,242 @@ declare type Light = import("../Lights/light").Light;
 declare type Camera = import("../Cameras/camera").Camera;
 declare type Node = import("../node").Node;
 
+/**
+ * The action to be carried out following a trigger
+ * @see http://doc.babylonjs.com/how_to/how_to_use_actions#available-actions
+ */
+export class Action {
     /**
-     * The action to be carried out following a trigger
-     * @see http://doc.babylonjs.com/how_to/how_to_use_actions#available-actions
+     * Trigger for the action
      */
-    export class Action {
-        /**
-         * Trigger for the action
-         */
-        public trigger: number;
-
-        /**
-         * Internal only - manager for action
-         * @hidden
-         */
-        public _actionManager: ActionManager;
-
-        private _nextActiveAction: Action;
-        private _child: Action;
-        private _condition?: Condition;
-        private _triggerParameter: any;
-
-        /**
-        * An event triggered prior to action being executed.
-        */
-        public onBeforeExecuteObservable = new Observable<Action>();
-
-        /**
-         * Creates a new Action
-         * @param triggerOptions the trigger, with or without parameters, for the action
-         * @param condition an optional determinant of action
-         */
-        constructor(
-            /** the trigger, with or without parameters, for the action */
-            public triggerOptions: any, condition?: Condition) {
-
-            if (triggerOptions.parameter) {
-                this.trigger = triggerOptions.trigger;
-                this._triggerParameter = triggerOptions.parameter;
-            } else if (triggerOptions.trigger) {
-                this.trigger = triggerOptions.trigger;
-            } else {
-                this.trigger = triggerOptions;
-            }
+    public trigger: number;
 
-            this._nextActiveAction = this;
-            this._condition = condition;
-        }
+    /**
+     * Internal only - manager for action
+     * @hidden
+     */
+    public _actionManager: ActionManager;
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _prepare(): void {
-        }
+    private _nextActiveAction: Action;
+    private _child: Action;
+    private _condition?: Condition;
+    private _triggerParameter: any;
+
+    /**
+    * An event triggered prior to action being executed.
+    */
+    public onBeforeExecuteObservable = new Observable<Action>();
 
-        /**
-         * Gets the trigger parameters
-         * @returns the trigger parameters
-         */
-        public getTriggerParameter(): any {
-            return this._triggerParameter;
+    /**
+     * Creates a new Action
+     * @param triggerOptions the trigger, with or without parameters, for the action
+     * @param condition an optional determinant of action
+     */
+    constructor(
+        /** the trigger, with or without parameters, for the action */
+        public triggerOptions: any, condition?: Condition) {
+
+        if (triggerOptions.parameter) {
+            this.trigger = triggerOptions.trigger;
+            this._triggerParameter = triggerOptions.parameter;
+        } else if (triggerOptions.trigger) {
+            this.trigger = triggerOptions.trigger;
+        } else {
+            this.trigger = triggerOptions;
         }
 
-        /**
-         * Internal only - executes current action event
-         * @hidden
-         */
-        public _executeCurrent(evt?: ActionEvent): void {
-            if (this._nextActiveAction._condition) {
-                var condition = this._nextActiveAction._condition;
-                var currentRenderId = this._actionManager.getScene().getRenderId();
-
-                // We cache the current evaluation for the current frame
-                if (condition._evaluationId === currentRenderId) {
-                    if (!condition._currentResult) {
-                        return;
-                    }
-                } else {
-                    condition._evaluationId = currentRenderId;
-
-                    if (!condition.isValid()) {
-                        condition._currentResult = false;
-                        return;
-                    }
-
-                    condition._currentResult = true;
+        this._nextActiveAction = this;
+        this._condition = condition;
+    }
+
+    /**
+     * Internal only
+     * @hidden
+     */
+    public _prepare(): void {
+    }
+
+    /**
+     * Gets the trigger parameters
+     * @returns the trigger parameters
+     */
+    public getTriggerParameter(): any {
+        return this._triggerParameter;
+    }
+
+    /**
+     * Internal only - executes current action event
+     * @hidden
+     */
+    public _executeCurrent(evt?: ActionEvent): void {
+        if (this._nextActiveAction._condition) {
+            var condition = this._nextActiveAction._condition;
+            var currentRenderId = this._actionManager.getScene().getRenderId();
+
+            // We cache the current evaluation for the current frame
+            if (condition._evaluationId === currentRenderId) {
+                if (!condition._currentResult) {
+                    return;
                 }
-            }
+            } else {
+                condition._evaluationId = currentRenderId;
 
-            this.onBeforeExecuteObservable.notifyObservers(this);
-            this._nextActiveAction.execute(evt);
+                if (!condition.isValid()) {
+                    condition._currentResult = false;
+                    return;
+                }
 
-            this.skipToNextActiveAction();
+                condition._currentResult = true;
+            }
         }
 
-        /**
-         * Execute placeholder for child classes
-         * @param evt optional action event
-         */
-        public execute(evt?: ActionEvent): void {
+        this.onBeforeExecuteObservable.notifyObservers(this);
+        this._nextActiveAction.execute(evt);
 
-        }
+        this.skipToNextActiveAction();
+    }
 
-        /**
-         * Skips to next active action
-         */
-        public skipToNextActiveAction(): void {
-            if (this._nextActiveAction._child) {
+    /**
+     * Execute placeholder for child classes
+     * @param evt optional action event
+     */
+    public execute(evt?: ActionEvent): void {
 
-                if (!this._nextActiveAction._child._actionManager) {
-                    this._nextActiveAction._child._actionManager = this._actionManager;
-                }
+    }
 
-                this._nextActiveAction = this._nextActiveAction._child;
-            } else {
-                this._nextActiveAction = this;
+    /**
+     * Skips to next active action
+     */
+    public skipToNextActiveAction(): void {
+        if (this._nextActiveAction._child) {
+
+            if (!this._nextActiveAction._child._actionManager) {
+                this._nextActiveAction._child._actionManager = this._actionManager;
             }
-        }
 
-        /**
-         * Adds action to chain of actions, may be a DoNothingAction
-         * @param action defines the next action to execute
-         * @returns The action passed in
-         * @see https://www.babylonjs-playground.com/#1T30HR#0
-         */
-        public then(action: Action): Action {
-            this._child = action;
+            this._nextActiveAction = this._nextActiveAction._child;
+        } else {
+            this._nextActiveAction = this;
+        }
+    }
 
-            action._actionManager = this._actionManager;
-            action._prepare();
+    /**
+     * Adds action to chain of actions, may be a DoNothingAction
+     * @param action defines the next action to execute
+     * @returns The action passed in
+     * @see https://www.babylonjs-playground.com/#1T30HR#0
+     */
+    public then(action: Action): Action {
+        this._child = action;
 
-            return action;
-        }
+        action._actionManager = this._actionManager;
+        action._prepare();
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _getProperty(propertyPath: string): string {
-            return this._actionManager._getProperty(propertyPath);
-        }
+        return action;
+    }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _getEffectiveTarget(target: any, propertyPath: string): any {
-            return this._actionManager._getEffectiveTarget(target, propertyPath);
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    public _getProperty(propertyPath: string): string {
+        return this._actionManager._getProperty(propertyPath);
+    }
 
-        /**
-         * Serialize placeholder for child classes
-         * @param parent of child
-         * @returns the serialized object
-         */
-        public serialize(parent: any): any {
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    public _getEffectiveTarget(target: any, propertyPath: string): any {
+        return this._actionManager._getEffectiveTarget(target, propertyPath);
+    }
 
-        /**
-         * Internal only called by serialize
-         * @hidden
-         */
-        protected _serialize(serializedAction: any, parent?: any): any {
-            var serializationObject: any = {
-                type: 1,
-                children: [],
-                name: serializedAction.name,
-                properties: serializedAction.properties || []
-            };
-
-            // Serialize child
-            if (this._child) {
-                this._child.serialize(serializationObject);
-            }
+    /**
+     * Serialize placeholder for child classes
+     * @param parent of child
+     * @returns the serialized object
+     */
+    public serialize(parent: any): any {
+    }
 
-            // Check if "this" has a condition
-            if (this._condition) {
-                var serializedCondition = this._condition.serialize();
-                serializedCondition.children.push(serializationObject);
+    /**
+     * Internal only called by serialize
+     * @hidden
+     */
+    protected _serialize(serializedAction: any, parent?: any): any {
+        var serializationObject: any = {
+            type: 1,
+            children: [],
+            name: serializedAction.name,
+            properties: serializedAction.properties || []
+        };
+
+        // Serialize child
+        if (this._child) {
+            this._child.serialize(serializationObject);
+        }
 
-                if (parent) {
-                    parent.children.push(serializedCondition);
-                }
-                return serializedCondition;
-            }
+        // Check if "this" has a condition
+        if (this._condition) {
+            var serializedCondition = this._condition.serialize();
+            serializedCondition.children.push(serializationObject);
 
             if (parent) {
-                parent.children.push(serializationObject);
+                parent.children.push(serializedCondition);
             }
-            return serializationObject;
+            return serializedCondition;
         }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public static _SerializeValueAsString = (value: any): string => {
-            if (typeof value === "number") {
-                return value.toString();
-            }
-
-            if (typeof value === "boolean") {
-                return value ? "true" : "false";
-            }
+        if (parent) {
+            parent.children.push(serializationObject);
+        }
+        return serializationObject;
+    }
 
-            if (value instanceof Vector2) {
-                return value.x + ", " + value.y;
-            }
-            if (value instanceof Vector3) {
-                return value.x + ", " + value.y + ", " + value.z;
-            }
+    /**
+     * Internal only
+     * @hidden
+     */
+    public static _SerializeValueAsString = (value: any): string => {
+        if (typeof value === "number") {
+            return value.toString();
+        }
 
-            if (value instanceof Color3) {
-                return value.r + ", " + value.g + ", " + value.b;
-            }
-            if (value instanceof Color4) {
-                return value.r + ", " + value.g + ", " + value.b + ", " + value.a;
-            }
+        if (typeof value === "boolean") {
+            return value ? "true" : "false";
+        }
 
-            return value; // string
+        if (value instanceof Vector2) {
+            return value.x + ", " + value.y;
+        }
+        if (value instanceof Vector3) {
+            return value.x + ", " + value.y + ", " + value.z;
         }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public static _GetTargetProperty = (target: Scene | Node) => {
-            return {
-                name: "target",
-                targetType: (<Mesh>target)._isMesh ? "MeshProperties"
-                            : (<Light>target)._isLight ? "LightProperties"
-                            : (<Camera>target)._isCamera ? "CameraProperties"
-                            : "SceneProperties",
-                value: (<Scene>target)._isScene ? "Scene" : (<Node>target).name
-            };
+        if (value instanceof Color3) {
+            return value.r + ", " + value.g + ", " + value.b;
+        }
+        if (value instanceof Color4) {
+            return value.r + ", " + value.g + ", " + value.b + ", " + value.a;
         }
+
+        return value; // string
+    }
+
+    /**
+     * Internal only
+     * @hidden
+     */
+    public static _GetTargetProperty = (target: Scene | Node) => {
+        return {
+            name: "target",
+            targetType: (<Mesh>target)._isMesh ? "MeshProperties"
+                : (<Light>target)._isLight ? "LightProperties"
+                    : (<Camera>target)._isCamera ? "CameraProperties"
+                        : "SceneProperties",
+            value: (<Scene>target)._isScene ? "Scene" : (<Node>target).name
+        };
     }
+}
 
-    _TypeStore.RegisteredTypes["BABYLON.Action"] = Action;
+_TypeStore.RegisteredTypes["BABYLON.Action"] = Action;

+ 68 - 68
src/Actions/actionEvent.ts

@@ -4,78 +4,78 @@ import { Sprite } from "../Sprites/sprite";
 import { Scene } from "../scene";
 import { Vector2 } from "../Maths/math";
 
+/**
+ * ActionEvent is the event being sent when an action is triggered.
+ */
+export class ActionEvent {
     /**
-     * ActionEvent is the event being sent when an action is triggered.
+     * Creates a new ActionEvent
+     * @param source The mesh or sprite that triggered the action
+     * @param pointerX The X mouse cursor position at the time of the event
+     * @param pointerY The Y mouse cursor position at the time of the event
+     * @param meshUnderPointer The mesh that is currently pointed at (can be null)
+     * @param sourceEvent the original (browser) event that triggered the ActionEvent
+     * @param additionalData additional data for the event
      */
-    export class ActionEvent {
-        /**
-         * Creates a new ActionEvent
-         * @param source The mesh or sprite that triggered the action
-         * @param pointerX The X mouse cursor position at the time of the event
-         * @param pointerY The Y mouse cursor position at the time of the event
-         * @param meshUnderPointer The mesh that is currently pointed at (can be null)
-         * @param sourceEvent the original (browser) event that triggered the ActionEvent
-         * @param additionalData additional data for the event
-         */
-        constructor(
-            /** The mesh or sprite that triggered the action */
-            public source: any,
-            /** The X mouse cursor position at the time of the event */
-            public pointerX: number,
-            /** The Y mouse cursor position at the time of the event */
-            public pointerY: number,
-            /** The mesh that is currently pointed at (can be null) */
-            public meshUnderPointer: Nullable<AbstractMesh>,
-            /** the original (browser) event that triggered the ActionEvent */
-            public sourceEvent?: any,
-            /** additional data for the event */
-            public additionalData?: any) {
+    constructor(
+        /** The mesh or sprite that triggered the action */
+        public source: any,
+        /** The X mouse cursor position at the time of the event */
+        public pointerX: number,
+        /** The Y mouse cursor position at the time of the event */
+        public pointerY: number,
+        /** The mesh that is currently pointed at (can be null) */
+        public meshUnderPointer: Nullable<AbstractMesh>,
+        /** the original (browser) event that triggered the ActionEvent */
+        public sourceEvent?: any,
+        /** additional data for the event */
+        public additionalData?: any) {
 
-        }
+    }
 
-        /**
-         * Helper function to auto-create an ActionEvent from a source mesh.
-         * @param source The source mesh that triggered the event
-         * @param evt The original (browser) event
-         * @param additionalData additional data for the event
-         * @returns the new ActionEvent
-         */
-        public static CreateNew(source: AbstractMesh, evt?: Event, additionalData?: any): ActionEvent {
-            var scene = source.getScene();
-            return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
-        }
+    /**
+     * Helper function to auto-create an ActionEvent from a source mesh.
+     * @param source The source mesh that triggered the event
+     * @param evt The original (browser) event
+     * @param additionalData additional data for the event
+     * @returns the new ActionEvent
+     */
+    public static CreateNew(source: AbstractMesh, evt?: Event, additionalData?: any): ActionEvent {
+        var scene = source.getScene();
+        return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
+    }
 
-        /**
-         * Helper function to auto-create an ActionEvent from a source sprite
-         * @param source The source sprite that triggered the event
-         * @param scene Scene associated with the sprite
-         * @param evt The original (browser) event
-         * @param additionalData additional data for the event
-         * @returns the new ActionEvent
-         */
-        public static CreateNewFromSprite(source: Sprite, scene: Scene, evt?: Event, additionalData?: any): ActionEvent {
-            return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
-        }
+    /**
+     * Helper function to auto-create an ActionEvent from a source sprite
+     * @param source The source sprite that triggered the event
+     * @param scene Scene associated with the sprite
+     * @param evt The original (browser) event
+     * @param additionalData additional data for the event
+     * @returns the new ActionEvent
+     */
+    public static CreateNewFromSprite(source: Sprite, scene: Scene, evt?: Event, additionalData?: any): ActionEvent {
+        return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
+    }
 
-        /**
-         * Helper function to auto-create an ActionEvent from a scene. If triggered by a mesh use ActionEvent.CreateNew
-         * @param scene the scene where the event occurred
-         * @param evt The original (browser) event
-         * @returns the new ActionEvent
-         */
-        public static CreateNewFromScene(scene: Scene, evt: Event): ActionEvent {
-            return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
-        }
+    /**
+     * Helper function to auto-create an ActionEvent from a scene. If triggered by a mesh use ActionEvent.CreateNew
+     * @param scene the scene where the event occurred
+     * @param evt The original (browser) event
+     * @returns the new ActionEvent
+     */
+    public static CreateNewFromScene(scene: Scene, evt: Event): ActionEvent {
+        return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
+    }
 
-        /**
-         * Helper function to auto-create an ActionEvent from a primitive
-         * @param prim defines the target primitive
-         * @param pointerPos defines the pointer position
-         * @param evt The original (browser) event
-         * @param additionalData additional data for the event
-         * @returns the new ActionEvent
-         */
-        public static CreateNewFromPrimitive(prim: any, pointerPos: Vector2, evt?: Event, additionalData?: any): ActionEvent {
-            return new ActionEvent(prim, pointerPos.x, pointerPos.y, null, evt, additionalData);
-        }
-    }
+    /**
+     * Helper function to auto-create an ActionEvent from a primitive
+     * @param prim defines the target primitive
+     * @param pointerPos defines the pointer position
+     * @param evt The original (browser) event
+     * @param additionalData additional data for the event
+     * @returns the new ActionEvent
+     */
+    public static CreateNewFromPrimitive(prim: any, pointerPos: Vector2, evt?: Event, additionalData?: any): ActionEvent {
+        return new ActionEvent(prim, pointerPos.x, pointerPos.y, null, evt, additionalData);
+    }
+}

文件差異過大導致無法顯示
+ 568 - 568
src/Actions/actionManager.ts


+ 280 - 280
src/Actions/condition.ts

@@ -3,325 +3,325 @@ import { _TypeStore } from "../Misc/typeStore";
 
 declare type ActionManager = import("./actionManager").ActionManager;
 
+/**
+ * A Condition applied to an Action
+ */
+export class Condition {
     /**
-     * A Condition applied to an Action
-     */
-    export class Condition {
-        /**
-         * Internal only - manager for action
-         * @hidden
-         */
-        public _actionManager: ActionManager;
-
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _evaluationId: number;
-
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _currentResult: boolean;
-
-        /**
-         * Creates a new Condition
-         * @param actionManager the manager of the action the condition is applied to
-         */
-        constructor(actionManager: ActionManager) {
-            this._actionManager = actionManager;
-        }
-
-        /**
-         * Check if the current condition is valid
-         * @returns a boolean
-         */
-        public isValid(): boolean {
-            return true;
-        }
+     * Internal only - manager for action
+     * @hidden
+     */
+    public _actionManager: ActionManager;
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _getProperty(propertyPath: string): string {
-            return this._actionManager._getProperty(propertyPath);
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    public _evaluationId: number;
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        public _getEffectiveTarget(target: any, propertyPath: string): any {
-            return this._actionManager._getEffectiveTarget(target, propertyPath);
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    public _currentResult: boolean;
 
-        /**
-         * Serialize placeholder for child classes
-         * @returns the serialized object
-         */
-        public serialize(): any {
-        }
+    /**
+     * Creates a new Condition
+     * @param actionManager the manager of the action the condition is applied to
+     */
+    constructor(actionManager: ActionManager) {
+        this._actionManager = actionManager;
+    }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        protected _serialize(serializedCondition: any): any {
-            return {
-                type: 2, // Condition
-                children: [],
-                name: serializedCondition.name,
-                properties: serializedCondition.properties
-            };
-        }
+    /**
+     * Check if the current condition is valid
+     * @returns a boolean
+     */
+    public isValid(): boolean {
+        return true;
     }
 
     /**
-     * Defines specific conditional operators as extensions of Condition
+     * Internal only
+     * @hidden
      */
-    export class ValueCondition extends Condition {
+    public _getProperty(propertyPath: string): string {
+        return this._actionManager._getProperty(propertyPath);
+    }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        private static _IsEqual = 0;
+    /**
+     * Internal only
+     * @hidden
+     */
+    public _getEffectiveTarget(target: any, propertyPath: string): any {
+        return this._actionManager._getEffectiveTarget(target, propertyPath);
+    }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        private static _IsDifferent = 1;
+    /**
+     * Serialize placeholder for child classes
+     * @returns the serialized object
+     */
+    public serialize(): any {
+    }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        private static _IsGreater = 2;
+    /**
+     * Internal only
+     * @hidden
+     */
+    protected _serialize(serializedCondition: any): any {
+        return {
+            type: 2, // Condition
+            children: [],
+            name: serializedCondition.name,
+            properties: serializedCondition.properties
+        };
+    }
+}
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        private static _IsLesser = 3;
+/**
+ * Defines specific conditional operators as extensions of Condition
+ */
+export class ValueCondition extends Condition {
 
-        /**
-         * returns the number for IsEqual
-         */
-        public static get IsEqual(): number {
-            return ValueCondition._IsEqual;
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    private static _IsEqual = 0;
 
-        /**
-         * Returns the number for IsDifferent
-         */
-        public static get IsDifferent(): number {
-            return ValueCondition._IsDifferent;
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    private static _IsDifferent = 1;
 
-        /**
-         * Returns the number for IsGreater
-         */
-        public static get IsGreater(): number {
-            return ValueCondition._IsGreater;
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    private static _IsGreater = 2;
 
-        /**
-         * Returns the number for IsLesser
-         */
-        public static get IsLesser(): number {
-            return ValueCondition._IsLesser;
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    private static _IsLesser = 3;
 
-        /**
-         * Internal only The action manager for the condition
-         * @hidden
-         */
-        public _actionManager: ActionManager;
-
-        /**
-         * Internal only
-         * @hidden
-         */
-        private _target: any;
-
-        /**
-         * Internal only
-         * @hidden
-         */
-        private _effectiveTarget: any;
-
-        /**
-         * Internal only
-         * @hidden
-         */
-        private _property: string;
-
-        /**
-         * Creates a new ValueCondition
-         * @param actionManager manager for the action the condition applies to
-         * @param target for the action
-         * @param propertyPath path to specify the property of the target the conditional operator uses
-         * @param value the value compared by the conditional operator against the current value of the property
-         * @param operator the conditional operator, default ValueCondition.IsEqual
-         */
-        constructor(actionManager: ActionManager, target: any,
-            /** path to specify the property of the target the conditional operator uses  */
-            public propertyPath: string,
-            /** the value compared by the conditional operator against the current value of the property */
-            public value: any,
-            /** the conditional operator, default ValueCondition.IsEqual */
-            public operator: number = ValueCondition.IsEqual) {
-            super(actionManager);
-
-            this._target = target;
-            this._effectiveTarget = this._getEffectiveTarget(target, this.propertyPath);
-            this._property = this._getProperty(this.propertyPath);
-        }
+    /**
+     * returns the number for IsEqual
+     */
+    public static get IsEqual(): number {
+        return ValueCondition._IsEqual;
+    }
 
-        /**
-         * Compares the given value with the property value for the specified conditional operator
-         * @returns the result of the comparison
-         */
-        public isValid(): boolean {
-            switch (this.operator) {
-                case ValueCondition.IsGreater:
-                    return this._effectiveTarget[this._property] > this.value;
-                case ValueCondition.IsLesser:
-                    return this._effectiveTarget[this._property] < this.value;
-                case ValueCondition.IsEqual:
-                case ValueCondition.IsDifferent:
-                    var check: boolean;
-
-                    if (this.value.equals) {
-                        check = this.value.equals(this._effectiveTarget[this._property]);
-                    } else {
-                        check = this.value === this._effectiveTarget[this._property];
-                    }
-                    return this.operator === ValueCondition.IsEqual ? check : !check;
-            }
-
-            return false;
-        }
+    /**
+     * Returns the number for IsDifferent
+     */
+    public static get IsDifferent(): number {
+        return ValueCondition._IsDifferent;
+    }
 
-        /**
-         * Serialize the ValueCondition into a JSON compatible object
-         * @returns serialization object
-         */
-        public serialize(): any {
-            return this._serialize({
-               name: "ValueCondition",
-               properties: [
-                   Action._GetTargetProperty(this._target),
-                   { name: "propertyPath", value: this.propertyPath },
-                   { name: "value", value: Action._SerializeValueAsString(this.value) },
-                   { name: "operator", value: ValueCondition.GetOperatorName(this.operator) }
-                ]
-            });
-        }
+    /**
+     * Returns the number for IsGreater
+     */
+    public static get IsGreater(): number {
+        return ValueCondition._IsGreater;
+    }
 
-        /**
-         * Gets the name of the conditional operator for the ValueCondition
-         * @param operator the conditional operator
-         * @returns the name
-         */
-        public static GetOperatorName(operator: number): string {
-            switch (operator) {
-                case ValueCondition._IsEqual: return "IsEqual";
-                case ValueCondition._IsDifferent: return "IsDifferent";
-                case ValueCondition._IsGreater: return "IsGreater";
-                case ValueCondition._IsLesser: return "IsLesser";
-                default: return "";
-            }
-        }
+    /**
+     * Returns the number for IsLesser
+     */
+    public static get IsLesser(): number {
+        return ValueCondition._IsLesser;
     }
 
     /**
-     * Defines a predicate condition as an extension of Condition
+     * Internal only The action manager for the condition
+     * @hidden
      */
-    export class PredicateCondition extends Condition {
+    public _actionManager: ActionManager;
 
-        /**
-         * Internal only - manager for action
-         * @hidden
-         */
-        public _actionManager: ActionManager;
+    /**
+     * Internal only
+     * @hidden
+     */
+    private _target: any;
 
-        /**
-         * Creates a new PredicateCondition
-         * @param actionManager manager for the action the condition applies to
-         * @param predicate defines the predicate function used to validate the condition
-         */
-        constructor(actionManager: ActionManager,
-            /** defines the predicate function used to validate the condition */
-            public predicate: () => boolean) {
-            super(actionManager);
+    /**
+     * Internal only
+     * @hidden
+     */
+    private _effectiveTarget: any;
+
+    /**
+     * Internal only
+     * @hidden
+     */
+    private _property: string;
+
+    /**
+     * Creates a new ValueCondition
+     * @param actionManager manager for the action the condition applies to
+     * @param target for the action
+     * @param propertyPath path to specify the property of the target the conditional operator uses
+     * @param value the value compared by the conditional operator against the current value of the property
+     * @param operator the conditional operator, default ValueCondition.IsEqual
+     */
+    constructor(actionManager: ActionManager, target: any,
+        /** path to specify the property of the target the conditional operator uses  */
+        public propertyPath: string,
+        /** the value compared by the conditional operator against the current value of the property */
+        public value: any,
+        /** the conditional operator, default ValueCondition.IsEqual */
+        public operator: number = ValueCondition.IsEqual) {
+        super(actionManager);
+
+        this._target = target;
+        this._effectiveTarget = this._getEffectiveTarget(target, this.propertyPath);
+        this._property = this._getProperty(this.propertyPath);
+    }
+
+    /**
+     * Compares the given value with the property value for the specified conditional operator
+     * @returns the result of the comparison
+     */
+    public isValid(): boolean {
+        switch (this.operator) {
+            case ValueCondition.IsGreater:
+                return this._effectiveTarget[this._property] > this.value;
+            case ValueCondition.IsLesser:
+                return this._effectiveTarget[this._property] < this.value;
+            case ValueCondition.IsEqual:
+            case ValueCondition.IsDifferent:
+                var check: boolean;
+
+                if (this.value.equals) {
+                    check = this.value.equals(this._effectiveTarget[this._property]);
+                } else {
+                    check = this.value === this._effectiveTarget[this._property];
+                }
+                return this.operator === ValueCondition.IsEqual ? check : !check;
         }
 
-        /**
-         * @returns the validity of the predicate condition
-         */
-        public isValid(): boolean {
-            return this.predicate();
+        return false;
+    }
+
+    /**
+     * Serialize the ValueCondition into a JSON compatible object
+     * @returns serialization object
+     */
+    public serialize(): any {
+        return this._serialize({
+            name: "ValueCondition",
+            properties: [
+                Action._GetTargetProperty(this._target),
+                { name: "propertyPath", value: this.propertyPath },
+                { name: "value", value: Action._SerializeValueAsString(this.value) },
+                { name: "operator", value: ValueCondition.GetOperatorName(this.operator) }
+            ]
+        });
+    }
+
+    /**
+     * Gets the name of the conditional operator for the ValueCondition
+     * @param operator the conditional operator
+     * @returns the name
+     */
+    public static GetOperatorName(operator: number): string {
+        switch (operator) {
+            case ValueCondition._IsEqual: return "IsEqual";
+            case ValueCondition._IsDifferent: return "IsDifferent";
+            case ValueCondition._IsGreater: return "IsGreater";
+            case ValueCondition._IsLesser: return "IsLesser";
+            default: return "";
         }
     }
+}
+
+/**
+ * Defines a predicate condition as an extension of Condition
+ */
+export class PredicateCondition extends Condition {
 
     /**
-     * Defines a state condition as an extension of Condition
+     * Internal only - manager for action
+     * @hidden
      */
-    export class StateCondition extends Condition {
+    public _actionManager: ActionManager;
 
-        /**
-         * Internal only - manager for action
-         * @hidden
-         */
-        public _actionManager: ActionManager;
+    /**
+     * Creates a new PredicateCondition
+     * @param actionManager manager for the action the condition applies to
+     * @param predicate defines the predicate function used to validate the condition
+     */
+    constructor(actionManager: ActionManager,
+        /** defines the predicate function used to validate the condition */
+        public predicate: () => boolean) {
+        super(actionManager);
+    }
 
-        /**
-         * Internal only
-         * @hidden
-         */
-        private _target: any;
+    /**
+     * @returns the validity of the predicate condition
+     */
+    public isValid(): boolean {
+        return this.predicate();
+    }
+}
 
-        /**
-         * Creates a new StateCondition
-         * @param actionManager manager for the action the condition applies to
-         * @param target of the condition
-         * @param value to compare with target state
-         */
-        constructor(actionManager: ActionManager, target: any,
-                /** Value to compare with target state  */
-                public value: string) {
-            super(actionManager);
+/**
+ * Defines a state condition as an extension of Condition
+ */
+export class StateCondition extends Condition {
 
-            this._target = target;
-        }
+    /**
+     * Internal only - manager for action
+     * @hidden
+     */
+    public _actionManager: ActionManager;
 
-        /**
-         * Gets a boolean indicating if the current condition is met
-         * @returns the validity of the state
-         */
-        public isValid(): boolean {
-            return this._target.state === this.value;
-        }
+    /**
+     * Internal only
+     * @hidden
+     */
+    private _target: any;
 
-        /**
-         * Serialize the StateCondition into a JSON compatible object
-         * @returns serialization object
-         */
-        public serialize(): any {
-            return this._serialize({
-               name: "StateCondition",
-               properties: [
-                   Action._GetTargetProperty(this._target),
-                   { name: "value", value: this.value }
-                ]
-            });
-        }
+    /**
+     * Creates a new StateCondition
+     * @param actionManager manager for the action the condition applies to
+     * @param target of the condition
+     * @param value to compare with target state
+     */
+    constructor(actionManager: ActionManager, target: any,
+        /** Value to compare with target state  */
+        public value: string) {
+        super(actionManager);
+
+        this._target = target;
+    }
+
+    /**
+     * Gets a boolean indicating if the current condition is met
+     * @returns the validity of the state
+     */
+    public isValid(): boolean {
+        return this._target.state === this.value;
+    }
+
+    /**
+     * Serialize the StateCondition into a JSON compatible object
+     * @returns serialization object
+     */
+    public serialize(): any {
+        return this._serialize({
+            name: "StateCondition",
+            properties: [
+                Action._GetTargetProperty(this._target),
+                { name: "value", value: this.value }
+            ]
+        });
     }
+}
 
-    _TypeStore.RegisteredTypes["BABYLON.ValueCondition"] = ValueCondition;
-    _TypeStore.RegisteredTypes["BABYLON.PredicateCondition"] = PredicateCondition;
-    _TypeStore.RegisteredTypes["BABYLON.StateCondition"] = StateCondition;
+_TypeStore.RegisteredTypes["BABYLON.ValueCondition"] = ValueCondition;
+_TypeStore.RegisteredTypes["BABYLON.PredicateCondition"] = PredicateCondition;
+_TypeStore.RegisteredTypes["BABYLON.StateCondition"] = StateCondition;

文件差異過大導致無法顯示
+ 464 - 464
src/Actions/directActions.ts


+ 72 - 72
src/Actions/directAudioActions.ts

@@ -3,91 +3,91 @@ import { Condition } from "./condition";
 import { _TypeStore } from '../Misc/typeStore';
 import { Sound } from "../Audio/sound";
 
+/**
+ * This defines an action helpful to play a defined sound on a triggered action.
+ */
+export class PlaySoundAction extends Action {
+    private _sound: Sound;
+
     /**
-     * This defines an action helpful to play a defined sound on a triggered action.
+     * Instantiate the action
+     * @param triggerOptions defines the trigger options
+     * @param sound defines the sound to play
+     * @param condition defines the trigger related conditions
      */
-    export class PlaySoundAction extends Action {
-        private _sound: Sound;
-
-        /**
-         * Instantiate the action
-         * @param triggerOptions defines the trigger options
-         * @param sound defines the sound to play
-         * @param condition defines the trigger related conditions
-         */
-        constructor(triggerOptions: any, sound: Sound, condition?: Condition) {
-            super(triggerOptions, condition);
-            this._sound = sound;
-        }
-
-        /** @hidden */
-        public _prepare(): void {
-        }
+    constructor(triggerOptions: any, sound: Sound, condition?: Condition) {
+        super(triggerOptions, condition);
+        this._sound = sound;
+    }
 
-        /**
-         * Execute the action and play the sound.
-         */
-        public execute(): void {
-            if (this._sound !== undefined) {
-                this._sound.play();
-            }
-        }
+    /** @hidden */
+    public _prepare(): void {
+    }
 
-        /**
-         * Serializes the actions and its related information.
-         * @param parent defines the object to serialize in
-         * @returns the serialized object
-         */
-        public serialize(parent: any): any {
-            return super._serialize({
-                name: "PlaySoundAction",
-                properties: [{ name: "sound", value: this._sound.name }]
-            }, parent);
+    /**
+     * Execute the action and play the sound.
+     */
+    public execute(): void {
+        if (this._sound !== undefined) {
+            this._sound.play();
         }
     }
 
     /**
-     * This defines an action helpful to stop a defined sound on a triggered action.
+     * Serializes the actions and its related information.
+     * @param parent defines the object to serialize in
+     * @returns the serialized object
      */
-    export class StopSoundAction extends Action {
-        private _sound: Sound;
+    public serialize(parent: any): any {
+        return super._serialize({
+            name: "PlaySoundAction",
+            properties: [{ name: "sound", value: this._sound.name }]
+        }, parent);
+    }
+}
 
-        /**
-         * Instantiate the action
-         * @param triggerOptions defines the trigger options
-         * @param sound defines the sound to stop
-         * @param condition defines the trigger related conditions
-         */
-        constructor(triggerOptions: any, sound: Sound, condition?: Condition) {
-            super(triggerOptions, condition);
-            this._sound = sound;
-        }
+/**
+ * This defines an action helpful to stop a defined sound on a triggered action.
+ */
+export class StopSoundAction extends Action {
+    private _sound: Sound;
 
-        /** @hidden */
-        public _prepare(): void {
-        }
+    /**
+     * Instantiate the action
+     * @param triggerOptions defines the trigger options
+     * @param sound defines the sound to stop
+     * @param condition defines the trigger related conditions
+     */
+    constructor(triggerOptions: any, sound: Sound, condition?: Condition) {
+        super(triggerOptions, condition);
+        this._sound = sound;
+    }
 
-        /**
-         * Execute the action and stop the sound.
-         */
-        public execute(): void {
-            if (this._sound !== undefined) {
-                this._sound.stop();
-            }
-        }
+    /** @hidden */
+    public _prepare(): void {
+    }
 
-        /**
-         * Serializes the actions and its related information.
-         * @param parent defines the object to serialize in
-         * @returns the serialized object
-         */
-        public serialize(parent: any): any {
-            return super._serialize({
-                name: "StopSoundAction",
-                properties: [{ name: "sound", value: this._sound.name }]
-            }, parent);
+    /**
+     * Execute the action and stop the sound.
+     */
+    public execute(): void {
+        if (this._sound !== undefined) {
+            this._sound.stop();
         }
     }
 
-    _TypeStore.RegisteredTypes["BABYLON.PlaySoundAction"] = StopSoundAction;
-    _TypeStore.RegisteredTypes["BABYLON.StopSoundAction"] = StopSoundAction;
+    /**
+     * Serializes the actions and its related information.
+     * @param parent defines the object to serialize in
+     * @returns the serialized object
+     */
+    public serialize(parent: any): any {
+        return super._serialize({
+            name: "StopSoundAction",
+            properties: [{ name: "sound", value: this._sound.name }]
+        }, parent);
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.PlaySoundAction"] = StopSoundAction;
+_TypeStore.RegisteredTypes["BABYLON.StopSoundAction"] = StopSoundAction;

+ 124 - 124
src/Actions/interpolateValueAction.ts

@@ -7,141 +7,141 @@ import { Color3, Vector3, Matrix, Quaternion } from "../Maths/math";
 import { Animation } from "../Animations/animation";
 import { _TypeStore } from '../Misc/typeStore';
 
+/**
+ * This defines an action responsible to change the value of a property
+ * by interpolating between its current value and the newly set one once triggered.
+ * @see http://doc.babylonjs.com/how_to/how_to_use_actions
+ */
+export class InterpolateValueAction extends Action {
     /**
-     * This defines an action responsible to change the value of a property
-     * by interpolating between its current value and the newly set one once triggered.
-     * @see http://doc.babylonjs.com/how_to/how_to_use_actions
+     * Defines the path of the property where the value should be interpolated
      */
-    export class InterpolateValueAction extends Action {
-        /**
-         * Defines the path of the property where the value should be interpolated
-         */
-        public propertyPath: string;
-
-        /**
-         * Defines the target value at the end of the interpolation.
-         */
-        public value: any;
-
-        /**
-         * Defines the time it will take for the property to interpolate to the value.
-         */
-        public duration: number = 1000;
-
-        /**
-         * Defines if the other scene animations should be stopped when the action has been triggered
-         */
-        public stopOtherAnimations?: boolean;
-
-        /**
-         * Defines a callback raised once the interpolation animation has been done.
-         */
-        public onInterpolationDone?: () => void;
-
-        /**
-         * Observable triggered once the interpolation animation has been done.
-         */
-        public onInterpolationDoneObservable = new Observable<InterpolateValueAction>();
-
-        private _target: any;
-        private _effectiveTarget: any;
-        private _property: string;
-
-        /**
-         * Instantiate the action
-         * @param triggerOptions defines the trigger options
-         * @param target defines the object containing the value to interpolate
-         * @param propertyPath defines the path to the property in the target object
-         * @param value defines the target value at the end of the interpolation
-         * @param duration deines the time it will take for the property to interpolate to the value.
-         * @param condition defines the trigger related conditions
-         * @param stopOtherAnimations defines if the other scene animations should be stopped when the action has been triggered
-         * @param onInterpolationDone defines a callback raised once the interpolation animation has been done
-         */
-        constructor(triggerOptions: any, target: any, propertyPath: string, value: any, duration: number = 1000, condition?: Condition, stopOtherAnimations?: boolean, onInterpolationDone?: () => void) {
-            super(triggerOptions, condition);
-
-            this.propertyPath = propertyPath;
-            this.value = value;
-            this.duration = duration;
-            this.stopOtherAnimations = stopOtherAnimations;
-            this.onInterpolationDone = onInterpolationDone;
-            this._target = this._effectiveTarget = target;
-        }
+    public propertyPath: string;
 
-        /** @hidden */
-        public _prepare(): void {
-            this._effectiveTarget = this._getEffectiveTarget(this._effectiveTarget, this.propertyPath);
-            this._property = this._getProperty(this.propertyPath);
-        }
+    /**
+     * Defines the target value at the end of the interpolation.
+     */
+    public value: any;
 
-        /**
-         * Execute the action starts the value interpolation.
-         */
-        public execute(): void {
-            var scene = this._actionManager.getScene();
-            var keys = [
-                {
-                    frame: 0,
-                    value: this._effectiveTarget[this._property]
-                }, {
-                    frame: 100,
-                    value: this.value
-                }
-            ];
-
-            var dataType: number;
-
-            if (typeof this.value === "number") {
-                dataType = Animation.ANIMATIONTYPE_FLOAT;
-            } else if (this.value instanceof Color3) {
-                dataType = Animation.ANIMATIONTYPE_COLOR3;
-            } else if (this.value instanceof Vector3) {
-                dataType = Animation.ANIMATIONTYPE_VECTOR3;
-            } else if (this.value instanceof Matrix) {
-                dataType = Animation.ANIMATIONTYPE_MATRIX;
-            } else if (this.value instanceof Quaternion) {
-                dataType = Animation.ANIMATIONTYPE_QUATERNION;
-            } else {
-                Logger.Warn("InterpolateValueAction: Unsupported type (" + typeof this.value + ")");
-                return;
-            }
+    /**
+     * Defines the time it will take for the property to interpolate to the value.
+     */
+    public duration: number = 1000;
+
+    /**
+     * Defines if the other scene animations should be stopped when the action has been triggered
+     */
+    public stopOtherAnimations?: boolean;
+
+    /**
+     * Defines a callback raised once the interpolation animation has been done.
+     */
+    public onInterpolationDone?: () => void;
 
-            var animation = new Animation("InterpolateValueAction", this._property, 100 * (1000.0 / this.duration), dataType, Animation.ANIMATIONLOOPMODE_CONSTANT);
+    /**
+     * Observable triggered once the interpolation animation has been done.
+     */
+    public onInterpolationDoneObservable = new Observable<InterpolateValueAction>();
 
-            animation.setKeys(keys);
+    private _target: any;
+    private _effectiveTarget: any;
+    private _property: string;
 
-            if (this.stopOtherAnimations) {
-                scene.stopAnimation(this._effectiveTarget);
-            }
+    /**
+     * Instantiate the action
+     * @param triggerOptions defines the trigger options
+     * @param target defines the object containing the value to interpolate
+     * @param propertyPath defines the path to the property in the target object
+     * @param value defines the target value at the end of the interpolation
+     * @param duration deines the time it will take for the property to interpolate to the value.
+     * @param condition defines the trigger related conditions
+     * @param stopOtherAnimations defines if the other scene animations should be stopped when the action has been triggered
+     * @param onInterpolationDone defines a callback raised once the interpolation animation has been done
+     */
+    constructor(triggerOptions: any, target: any, propertyPath: string, value: any, duration: number = 1000, condition?: Condition, stopOtherAnimations?: boolean, onInterpolationDone?: () => void) {
+        super(triggerOptions, condition);
+
+        this.propertyPath = propertyPath;
+        this.value = value;
+        this.duration = duration;
+        this.stopOtherAnimations = stopOtherAnimations;
+        this.onInterpolationDone = onInterpolationDone;
+        this._target = this._effectiveTarget = target;
+    }
 
-            let wrapper = () => {
-                this.onInterpolationDoneObservable.notifyObservers(this);
-                if (this.onInterpolationDone) {
-                    this.onInterpolationDone();
-                }
-            };
+    /** @hidden */
+    public _prepare(): void {
+        this._effectiveTarget = this._getEffectiveTarget(this._effectiveTarget, this.propertyPath);
+        this._property = this._getProperty(this.propertyPath);
+    }
 
-            scene.beginDirectAnimation(this._effectiveTarget, [animation], 0, 100, false, 1, wrapper);
+    /**
+     * Execute the action starts the value interpolation.
+     */
+    public execute(): void {
+        var scene = this._actionManager.getScene();
+        var keys = [
+            {
+                frame: 0,
+                value: this._effectiveTarget[this._property]
+            }, {
+                frame: 100,
+                value: this.value
+            }
+        ];
+
+        var dataType: number;
+
+        if (typeof this.value === "number") {
+            dataType = Animation.ANIMATIONTYPE_FLOAT;
+        } else if (this.value instanceof Color3) {
+            dataType = Animation.ANIMATIONTYPE_COLOR3;
+        } else if (this.value instanceof Vector3) {
+            dataType = Animation.ANIMATIONTYPE_VECTOR3;
+        } else if (this.value instanceof Matrix) {
+            dataType = Animation.ANIMATIONTYPE_MATRIX;
+        } else if (this.value instanceof Quaternion) {
+            dataType = Animation.ANIMATIONTYPE_QUATERNION;
+        } else {
+            Logger.Warn("InterpolateValueAction: Unsupported type (" + typeof this.value + ")");
+            return;
         }
 
-        /**
-         * Serializes the actions and its related information.
-         * @param parent defines the object to serialize in
-         * @returns the serialized object
-         */
-        public serialize(parent: any): any {
-            return super._serialize({
-                name: "InterpolateValueAction",
-                properties: [
-                    Action._GetTargetProperty(this._target),
-                    { name: "propertyPath", value: this.propertyPath },
-                    { name: "value", value: Action._SerializeValueAsString(this.value) },
-                    { name: "duration", value: Action._SerializeValueAsString(this.duration) },
-                    { name: "stopOtherAnimations", value: Action._SerializeValueAsString(this.stopOtherAnimations) || false }
-                ]
-            }, parent);
+        var animation = new Animation("InterpolateValueAction", this._property, 100 * (1000.0 / this.duration), dataType, Animation.ANIMATIONLOOPMODE_CONSTANT);
+
+        animation.setKeys(keys);
+
+        if (this.stopOtherAnimations) {
+            scene.stopAnimation(this._effectiveTarget);
         }
+
+        let wrapper = () => {
+            this.onInterpolationDoneObservable.notifyObservers(this);
+            if (this.onInterpolationDone) {
+                this.onInterpolationDone();
+            }
+        };
+
+        scene.beginDirectAnimation(this._effectiveTarget, [animation], 0, 100, false, 1, wrapper);
+    }
+
+    /**
+     * Serializes the actions and its related information.
+     * @param parent defines the object to serialize in
+     * @returns the serialized object
+     */
+    public serialize(parent: any): any {
+        return super._serialize({
+            name: "InterpolateValueAction",
+            properties: [
+                Action._GetTargetProperty(this._target),
+                { name: "propertyPath", value: this.propertyPath },
+                { name: "value", value: Action._SerializeValueAsString(this.value) },
+                { name: "duration", value: Action._SerializeValueAsString(this.duration) },
+                { name: "stopOtherAnimations", value: Action._SerializeValueAsString(this.stopOtherAnimations) || false }
+            ]
+        }, parent);
     }
+}
 
-    _TypeStore.RegisteredTypes["BABYLON.InterpolateValueAction"] = Action;
+_TypeStore.RegisteredTypes["BABYLON.InterpolateValueAction"] = Action;

+ 337 - 337
src/Animations/animatable.ts

@@ -5,419 +5,419 @@ import { Nullable } from "../types";
 import { Observable } from "../Misc/observable";
 import { Scene } from "../scene";
 
+/**
+ * Class used to store an actual running animation
+ */
+export class Animatable {
+    private _localDelayOffset: Nullable<number> = null;
+    private _pausedDelay: Nullable<number> = null;
+    private _runtimeAnimations = new Array<RuntimeAnimation>();
+    private _paused = false;
+    private _scene: Scene;
+    private _speedRatio = 1;
+    private _weight = -1.0;
+    private _syncRoot: Animatable;
+
     /**
-     * Class used to store an actual running animation
+     * Gets or sets a boolean indicating if the animatable must be disposed and removed at the end of the animation.
+     * This will only apply for non looping animation (default is true)
      */
-    export class Animatable {
-        private _localDelayOffset: Nullable<number> = null;
-        private _pausedDelay: Nullable<number> = null;
-        private _runtimeAnimations = new Array<RuntimeAnimation>();
-        private _paused = false;
-        private _scene: Scene;
-        private _speedRatio = 1;
-        private _weight = -1.0;
-        private _syncRoot: Animatable;
-
-        /**
-         * Gets or sets a boolean indicating if the animatable must be disposed and removed at the end of the animation.
-         * This will only apply for non looping animation (default is true)
-         */
-        public disposeOnEnd = true;
-
-        /**
-         * Gets a boolean indicating if the animation has started
-         */
-        public animationStarted = false;
-
-        /**
-         * Observer raised when the animation ends
-         */
-        public onAnimationEndObservable = new Observable<Animatable>();
-
-        /**
-         * Observer raised when the animation loops
-         */
-        public onAnimationLoopObservable = new Observable<Animatable>();
-
-        /**
-         * Gets the root Animatable used to synchronize and normalize animations
-         */
-        public get syncRoot(): Animatable {
-            return this._syncRoot;
-        }
+    public disposeOnEnd = true;
 
-        /**
-         * Gets the current frame of the first RuntimeAnimation
-         * Used to synchronize Animatables
-         */
-        public get masterFrame(): number {
-            if (this._runtimeAnimations.length === 0) {
-                return 0;
-            }
+    /**
+     * Gets a boolean indicating if the animation has started
+     */
+    public animationStarted = false;
 
-            return this._runtimeAnimations[0].currentFrame;
-        }
+    /**
+     * Observer raised when the animation ends
+     */
+    public onAnimationEndObservable = new Observable<Animatable>();
 
-        /**
-         * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
-         */
-        public get weight(): number {
-            return this._weight;
-        }
+    /**
+     * Observer raised when the animation loops
+     */
+    public onAnimationLoopObservable = new Observable<Animatable>();
 
-        public set weight(value: number) {
-            if (value === -1) { // -1 is ok and means no weight
-                this._weight = -1;
-                return;
-            }
+    /**
+     * Gets the root Animatable used to synchronize and normalize animations
+     */
+    public get syncRoot(): Animatable {
+        return this._syncRoot;
+    }
 
-            // Else weight must be in [0, 1] range
-            this._weight = Math.min(Math.max(value, 0), 1.0);
+    /**
+     * Gets the current frame of the first RuntimeAnimation
+     * Used to synchronize Animatables
+     */
+    public get masterFrame(): number {
+        if (this._runtimeAnimations.length === 0) {
+            return 0;
         }
 
-        /**
-         * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
-         */
-        public get speedRatio(): number {
-            return this._speedRatio;
-        }
+        return this._runtimeAnimations[0].currentFrame;
+    }
 
-        public set speedRatio(value: number) {
-            for (var index = 0; index < this._runtimeAnimations.length; index++) {
-                var animation = this._runtimeAnimations[index];
+    /**
+     * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+     */
+    public get weight(): number {
+        return this._weight;
+    }
 
-                animation._prepareForSpeedRatioChange(value);
-            }
-            this._speedRatio = value;
+    public set weight(value: number) {
+        if (value === -1) { // -1 is ok and means no weight
+            this._weight = -1;
+            return;
         }
 
-        /**
-         * Creates a new Animatable
-         * @param scene defines the hosting scene
-         * @param target defines the target object
-         * @param fromFrame defines the starting frame number (default is 0)
-         * @param toFrame defines the ending frame number (default is 100)
-         * @param loopAnimation defines if the animation must loop (default is false)
-         * @param speedRatio defines the factor to apply to animation speed (default is 1)
-         * @param onAnimationEnd defines a callback to call when animation ends if it is not looping
-         * @param animations defines a group of animation to add to the new Animatable
-         * @param onAnimationLoop defines a callback to call when animation loops
-         */
-        constructor(scene: Scene,
-            /** defines the target object */
-            public target: any,
-            /** defines the starting frame number (default is 0) */
-            public fromFrame: number = 0,
-            /** defines the ending frame number (default is 100) */
-            public toFrame: number = 100,
-            /** defines if the animation must loop (default is false)  */
-            public loopAnimation: boolean = false,
-            speedRatio: number = 1.0,
-            /** defines a callback to call when animation ends if it is not looping */
-            public onAnimationEnd?: Nullable<() => void>,
-            animations?: Animation[],
-            /** defines a callback to call when animation loops */
-            public onAnimationLoop?: Nullable<() => void>) {
-            this._scene = scene;
-            if (animations) {
-                this.appendAnimations(target, animations);
-            }
+        // Else weight must be in [0, 1] range
+        this._weight = Math.min(Math.max(value, 0), 1.0);
+    }
 
-            this._speedRatio = speedRatio;
-            scene._activeAnimatables.push(this);
-        }
+    /**
+     * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
+     */
+    public get speedRatio(): number {
+        return this._speedRatio;
+    }
 
-        // Methods
-        /**
-         * Synchronize and normalize current Animatable with a source Animatable
-         * This is useful when using animation weights and when animations are not of the same length
-         * @param root defines the root Animatable to synchronize with
-         * @returns the current Animatable
-         */
-        public syncWith(root: Animatable): Animatable {
-            this._syncRoot = root;
-
-            if (root) {
-                // Make sure this animatable will animate after the root
-                let index = this._scene._activeAnimatables.indexOf(this);
-                if (index > -1) {
-                    this._scene._activeAnimatables.splice(index, 1);
-                    this._scene._activeAnimatables.push(this);
-                }
-            }
+    public set speedRatio(value: number) {
+        for (var index = 0; index < this._runtimeAnimations.length; index++) {
+            var animation = this._runtimeAnimations[index];
 
-            return this;
+            animation._prepareForSpeedRatioChange(value);
         }
+        this._speedRatio = value;
+    }
 
-        /**
-         * Gets the list of runtime animations
-         * @returns an array of RuntimeAnimation
-         */
-        public getAnimations(): RuntimeAnimation[] {
-            return this._runtimeAnimations;
+    /**
+     * Creates a new Animatable
+     * @param scene defines the hosting scene
+     * @param target defines the target object
+     * @param fromFrame defines the starting frame number (default is 0)
+     * @param toFrame defines the ending frame number (default is 100)
+     * @param loopAnimation defines if the animation must loop (default is false)
+     * @param speedRatio defines the factor to apply to animation speed (default is 1)
+     * @param onAnimationEnd defines a callback to call when animation ends if it is not looping
+     * @param animations defines a group of animation to add to the new Animatable
+     * @param onAnimationLoop defines a callback to call when animation loops
+     */
+    constructor(scene: Scene,
+        /** defines the target object */
+        public target: any,
+        /** defines the starting frame number (default is 0) */
+        public fromFrame: number = 0,
+        /** defines the ending frame number (default is 100) */
+        public toFrame: number = 100,
+        /** defines if the animation must loop (default is false)  */
+        public loopAnimation: boolean = false,
+        speedRatio: number = 1.0,
+        /** defines a callback to call when animation ends if it is not looping */
+        public onAnimationEnd?: Nullable<() => void>,
+        animations?: Animation[],
+        /** defines a callback to call when animation loops */
+        public onAnimationLoop?: Nullable<() => void>) {
+        this._scene = scene;
+        if (animations) {
+            this.appendAnimations(target, animations);
         }
 
-        /**
-         * Adds more animations to the current animatable
-         * @param target defines the target of the animations
-         * @param animations defines the new animations to add
-         */
-        public appendAnimations(target: any, animations: Animation[]): void {
-            for (var index = 0; index < animations.length; index++) {
-                var animation = animations[index];
+        this._speedRatio = speedRatio;
+        scene._activeAnimatables.push(this);
+    }
 
-                this._runtimeAnimations.push(new RuntimeAnimation(target, animation, this._scene, this));
+    // Methods
+    /**
+     * Synchronize and normalize current Animatable with a source Animatable
+     * This is useful when using animation weights and when animations are not of the same length
+     * @param root defines the root Animatable to synchronize with
+     * @returns the current Animatable
+     */
+    public syncWith(root: Animatable): Animatable {
+        this._syncRoot = root;
+
+        if (root) {
+            // Make sure this animatable will animate after the root
+            let index = this._scene._activeAnimatables.indexOf(this);
+            if (index > -1) {
+                this._scene._activeAnimatables.splice(index, 1);
+                this._scene._activeAnimatables.push(this);
             }
         }
 
-        /**
-         * Gets the source animation for a specific property
-         * @param property defines the propertyu to look for
-         * @returns null or the source animation for the given property
-         */
-        public getAnimationByTargetProperty(property: string): Nullable<Animation> {
-            var runtimeAnimations = this._runtimeAnimations;
-
-            for (var index = 0; index < runtimeAnimations.length; index++) {
-                if (runtimeAnimations[index].animation.targetProperty === property) {
-                    return runtimeAnimations[index].animation;
-                }
-            }
+        return this;
+    }
 
-            return null;
-        }
+    /**
+     * Gets the list of runtime animations
+     * @returns an array of RuntimeAnimation
+     */
+    public getAnimations(): RuntimeAnimation[] {
+        return this._runtimeAnimations;
+    }
 
-        /**
-         * Gets the runtime animation for a specific property
-         * @param property defines the propertyu to look for
-         * @returns null or the runtime animation for the given property
-         */
-        public getRuntimeAnimationByTargetProperty(property: string): Nullable<RuntimeAnimation> {
-            var runtimeAnimations = this._runtimeAnimations;
-
-            for (var index = 0; index < runtimeAnimations.length; index++) {
-                if (runtimeAnimations[index].animation.targetProperty === property) {
-                    return runtimeAnimations[index];
-                }
-            }
+    /**
+     * Adds more animations to the current animatable
+     * @param target defines the target of the animations
+     * @param animations defines the new animations to add
+     */
+    public appendAnimations(target: any, animations: Animation[]): void {
+        for (var index = 0; index < animations.length; index++) {
+            var animation = animations[index];
 
-            return null;
+            this._runtimeAnimations.push(new RuntimeAnimation(target, animation, this._scene, this));
         }
+    }
 
-        /**
-         * Resets the animatable to its original state
-         */
-        public reset(): void {
-            var runtimeAnimations = this._runtimeAnimations;
+    /**
+     * Gets the source animation for a specific property
+     * @param property defines the propertyu to look for
+     * @returns null or the source animation for the given property
+     */
+    public getAnimationByTargetProperty(property: string): Nullable<Animation> {
+        var runtimeAnimations = this._runtimeAnimations;
 
-            for (var index = 0; index < runtimeAnimations.length; index++) {
-                runtimeAnimations[index].reset(true);
+        for (var index = 0; index < runtimeAnimations.length; index++) {
+            if (runtimeAnimations[index].animation.targetProperty === property) {
+                return runtimeAnimations[index].animation;
             }
-
-            this._localDelayOffset = null;
-            this._pausedDelay = null;
         }
 
-        /**
-         * Allows the animatable to blend with current running animations
-         * @see http://doc.babylonjs.com/babylon101/animations#animation-blending
-         * @param blendingSpeed defines the blending speed to use
-         */
-        public enableBlending(blendingSpeed: number): void {
-            var runtimeAnimations = this._runtimeAnimations;
-
-            for (var index = 0; index < runtimeAnimations.length; index++) {
-                runtimeAnimations[index].animation.enableBlending = true;
-                runtimeAnimations[index].animation.blendingSpeed = blendingSpeed;
-            }
-        }
+        return null;
+    }
 
-        /**
-         * Disable animation blending
-         * @see http://doc.babylonjs.com/babylon101/animations#animation-blending
-         */
-        public disableBlending(): void {
-            var runtimeAnimations = this._runtimeAnimations;
+    /**
+     * Gets the runtime animation for a specific property
+     * @param property defines the propertyu to look for
+     * @returns null or the runtime animation for the given property
+     */
+    public getRuntimeAnimationByTargetProperty(property: string): Nullable<RuntimeAnimation> {
+        var runtimeAnimations = this._runtimeAnimations;
 
-            for (var index = 0; index < runtimeAnimations.length; index++) {
-                runtimeAnimations[index].animation.enableBlending = false;
+        for (var index = 0; index < runtimeAnimations.length; index++) {
+            if (runtimeAnimations[index].animation.targetProperty === property) {
+                return runtimeAnimations[index];
             }
         }
 
-        /**
-         * Jump directly to a given frame
-         * @param frame defines the frame to jump to
-         */
-        public goToFrame(frame: number): void {
-            var runtimeAnimations = this._runtimeAnimations;
-
-            if (runtimeAnimations[0]) {
-                var fps = runtimeAnimations[0].animation.framePerSecond;
-                var currentFrame = runtimeAnimations[0].currentFrame;
-                var adjustTime = frame - currentFrame;
-                var delay = adjustTime * 1000 / (fps * this.speedRatio);
-                if (this._localDelayOffset === null) {
-                    this._localDelayOffset = 0;
-                }
-                this._localDelayOffset -= delay;
-            }
+        return null;
+    }
 
-            for (var index = 0; index < runtimeAnimations.length; index++) {
-                runtimeAnimations[index].goToFrame(frame);
-            }
+    /**
+     * Resets the animatable to its original state
+     */
+    public reset(): void {
+        var runtimeAnimations = this._runtimeAnimations;
+
+        for (var index = 0; index < runtimeAnimations.length; index++) {
+            runtimeAnimations[index].reset(true);
         }
 
-        /**
-         * Pause the animation
-         */
-        public pause(): void {
-            if (this._paused) {
-                return;
-            }
-            this._paused = true;
+        this._localDelayOffset = null;
+        this._pausedDelay = null;
+    }
+
+    /**
+     * Allows the animatable to blend with current running animations
+     * @see http://doc.babylonjs.com/babylon101/animations#animation-blending
+     * @param blendingSpeed defines the blending speed to use
+     */
+    public enableBlending(blendingSpeed: number): void {
+        var runtimeAnimations = this._runtimeAnimations;
+
+        for (var index = 0; index < runtimeAnimations.length; index++) {
+            runtimeAnimations[index].animation.enableBlending = true;
+            runtimeAnimations[index].animation.blendingSpeed = blendingSpeed;
         }
+    }
+
+    /**
+     * Disable animation blending
+     * @see http://doc.babylonjs.com/babylon101/animations#animation-blending
+     */
+    public disableBlending(): void {
+        var runtimeAnimations = this._runtimeAnimations;
 
-        /**
-         * Restart the animation
-         */
-        public restart(): void {
-            this._paused = false;
+        for (var index = 0; index < runtimeAnimations.length; index++) {
+            runtimeAnimations[index].animation.enableBlending = false;
         }
+    }
 
-        private _raiseOnAnimationEnd() {
-            if (this.onAnimationEnd) {
-                this.onAnimationEnd();
+    /**
+     * Jump directly to a given frame
+     * @param frame defines the frame to jump to
+     */
+    public goToFrame(frame: number): void {
+        var runtimeAnimations = this._runtimeAnimations;
+
+        if (runtimeAnimations[0]) {
+            var fps = runtimeAnimations[0].animation.framePerSecond;
+            var currentFrame = runtimeAnimations[0].currentFrame;
+            var adjustTime = frame - currentFrame;
+            var delay = adjustTime * 1000 / (fps * this.speedRatio);
+            if (this._localDelayOffset === null) {
+                this._localDelayOffset = 0;
             }
+            this._localDelayOffset -= delay;
+        }
 
-            this.onAnimationEndObservable.notifyObservers(this);
+        for (var index = 0; index < runtimeAnimations.length; index++) {
+            runtimeAnimations[index].goToFrame(frame);
         }
+    }
 
-        /**
-         * Stop and delete the current animation
-         * @param animationName defines a string used to only stop some of the runtime animations instead of all
-         * @param targetMask - a function that determines if the animation should be stopped based on its target (all animations will be stopped if both this and animationName are empty)
-         */
-        public stop(animationName?: string, targetMask?: (target: any) => boolean): void {
-            if (animationName || targetMask) {
-                var idx = this._scene._activeAnimatables.indexOf(this);
-
-                if (idx > -1) {
-
-                    var runtimeAnimations = this._runtimeAnimations;
-
-                    for (var index = runtimeAnimations.length - 1; index >= 0; index--) {
-                        const runtimeAnimation = runtimeAnimations[index];
-                        if (animationName && runtimeAnimation.animation.name != animationName) {
-                            continue;
-                        }
-                        if (targetMask && !targetMask(runtimeAnimation.target)) {
-                            continue;
-                        }
-
-                        runtimeAnimation.dispose();
-                        runtimeAnimations.splice(index, 1);
-                    }
+    /**
+     * Pause the animation
+     */
+    public pause(): void {
+        if (this._paused) {
+            return;
+        }
+        this._paused = true;
+    }
 
-                    if (runtimeAnimations.length == 0) {
-                        this._scene._activeAnimatables.splice(idx, 1);
-                        this._raiseOnAnimationEnd();
-                    }
-                }
+    /**
+     * Restart the animation
+     */
+    public restart(): void {
+        this._paused = false;
+    }
+
+    private _raiseOnAnimationEnd() {
+        if (this.onAnimationEnd) {
+            this.onAnimationEnd();
+        }
+
+        this.onAnimationEndObservable.notifyObservers(this);
+    }
 
-            } else {
+    /**
+     * Stop and delete the current animation
+     * @param animationName defines a string used to only stop some of the runtime animations instead of all
+     * @param targetMask - a function that determines if the animation should be stopped based on its target (all animations will be stopped if both this and animationName are empty)
+     */
+    public stop(animationName?: string, targetMask?: (target: any) => boolean): void {
+        if (animationName || targetMask) {
+            var idx = this._scene._activeAnimatables.indexOf(this);
 
-                var index = this._scene._activeAnimatables.indexOf(this);
+            if (idx > -1) {
 
-                if (index > -1) {
-                    this._scene._activeAnimatables.splice(index, 1);
-                    var runtimeAnimations = this._runtimeAnimations;
+                var runtimeAnimations = this._runtimeAnimations;
 
-                    for (var index = 0; index < runtimeAnimations.length; index++) {
-                        runtimeAnimations[index].dispose();
+                for (var index = runtimeAnimations.length - 1; index >= 0; index--) {
+                    const runtimeAnimation = runtimeAnimations[index];
+                    if (animationName && runtimeAnimation.animation.name != animationName) {
+                        continue;
+                    }
+                    if (targetMask && !targetMask(runtimeAnimation.target)) {
+                        continue;
                     }
 
+                    runtimeAnimation.dispose();
+                    runtimeAnimations.splice(index, 1);
+                }
+
+                if (runtimeAnimations.length == 0) {
+                    this._scene._activeAnimatables.splice(idx, 1);
                     this._raiseOnAnimationEnd();
                 }
             }
-        }
 
-        /**
-         * Wait asynchronously for the animation to end
-         * @returns a promise which will be fullfilled when the animation ends
-         */
-        public waitAsync(): Promise<Animatable> {
-            return new Promise((resolve, reject) => {
-                this.onAnimationEndObservable.add(() => {
-                    resolve(this);
-                }, undefined, undefined, this, true);
-            });
-        }
+        } else {
 
-        /** @hidden */
-        public _animate(delay: number): boolean {
-            if (this._paused) {
-                this.animationStarted = false;
-                if (this._pausedDelay === null) {
-                    this._pausedDelay = delay;
+            var index = this._scene._activeAnimatables.indexOf(this);
+
+            if (index > -1) {
+                this._scene._activeAnimatables.splice(index, 1);
+                var runtimeAnimations = this._runtimeAnimations;
+
+                for (var index = 0; index < runtimeAnimations.length; index++) {
+                    runtimeAnimations[index].dispose();
                 }
-                return true;
-            }
 
-            if (this._localDelayOffset === null) {
-                this._localDelayOffset = delay;
-                this._pausedDelay = null;
-            } else if (this._pausedDelay !== null) {
-                this._localDelayOffset += delay - this._pausedDelay;
-                this._pausedDelay = null;
+                this._raiseOnAnimationEnd();
             }
+        }
+    }
 
-            if (this._weight === 0) { // We consider that an animation with a weight === 0 is "actively" paused
-                return true;
-            }
+    /**
+     * Wait asynchronously for the animation to end
+     * @returns a promise which will be fullfilled when the animation ends
+     */
+    public waitAsync(): Promise<Animatable> {
+        return new Promise((resolve, reject) => {
+            this.onAnimationEndObservable.add(() => {
+                resolve(this);
+            }, undefined, undefined, this, true);
+        });
+    }
 
-            // Animating
-            var running = false;
-            var runtimeAnimations = this._runtimeAnimations;
-            var index: number;
-
-            for (index = 0; index < runtimeAnimations.length; index++) {
-                var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame,
-                    this.toFrame, this.loopAnimation, this._speedRatio, this._weight,
-                    () => {
-                        this.onAnimationLoopObservable.notifyObservers(this);
-                        if (this.onAnimationLoop) {
-                            this.onAnimationLoop();
-                        }
-                    }
-                );
-                running = running || isRunning;
+    /** @hidden */
+    public _animate(delay: number): boolean {
+        if (this._paused) {
+            this.animationStarted = false;
+            if (this._pausedDelay === null) {
+                this._pausedDelay = delay;
             }
+            return true;
+        }
 
-            this.animationStarted = running;
+        if (this._localDelayOffset === null) {
+            this._localDelayOffset = delay;
+            this._pausedDelay = null;
+        } else if (this._pausedDelay !== null) {
+            this._localDelayOffset += delay - this._pausedDelay;
+            this._pausedDelay = null;
+        }
 
-            if (!running) {
-                if (this.disposeOnEnd) {
-                    // Remove from active animatables
-                    index = this._scene._activeAnimatables.indexOf(this);
-                    this._scene._activeAnimatables.splice(index, 1);
+        if (this._weight === 0) { // We consider that an animation with a weight === 0 is "actively" paused
+            return true;
+        }
 
-                    // Dispose all runtime animations
-                    for (index = 0; index < runtimeAnimations.length; index++) {
-                        runtimeAnimations[index].dispose();
+        // Animating
+        var running = false;
+        var runtimeAnimations = this._runtimeAnimations;
+        var index: number;
+
+        for (index = 0; index < runtimeAnimations.length; index++) {
+            var animation = runtimeAnimations[index];
+            var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame,
+                this.toFrame, this.loopAnimation, this._speedRatio, this._weight,
+                () => {
+                    this.onAnimationLoopObservable.notifyObservers(this);
+                    if (this.onAnimationLoop) {
+                        this.onAnimationLoop();
                     }
                 }
+            );
+            running = running || isRunning;
+        }
 
-                this._raiseOnAnimationEnd();
+        this.animationStarted = running;
+
+        if (!running) {
+            if (this.disposeOnEnd) {
+                // Remove from active animatables
+                index = this._scene._activeAnimatables.indexOf(this);
+                this._scene._activeAnimatables.splice(index, 1);
 
-                if (this.disposeOnEnd) {
-                    this.onAnimationEnd = null;
-                    this.onAnimationLoop = null;
-                    this.onAnimationLoopObservable.clear();
-                    this.onAnimationEndObservable.clear();
+                // Dispose all runtime animations
+                for (index = 0; index < runtimeAnimations.length; index++) {
+                    runtimeAnimations[index].dispose();
                 }
             }
 
-            return running;
+            this._raiseOnAnimationEnd();
+
+            if (this.disposeOnEnd) {
+                this.onAnimationEnd = null;
+                this.onAnimationLoop = null;
+                this.onAnimationLoopObservable.clear();
+                this.onAnimationEndObservable.clear();
+            }
         }
+
+        return running;
     }
+}

文件差異過大導致無法顯示
+ 1056 - 1056
src/Animations/animation.ts


+ 404 - 404
src/Animations/animationGroup.ts

@@ -5,501 +5,501 @@ import { Scene, IDisposable } from "../scene";
 import { Observable } from "../Misc/observable";
 import { Nullable } from "../types";
 import { EngineStore } from "../Engines/engineStore";
+/**
+ * This class defines the direct association between an animation and a target
+ */
+export class TargetedAnimation {
     /**
-     * This class defines the direct association between an animation and a target
-     */
-    export class TargetedAnimation {
-        /**
-         * Animation to perform
-         */
-        public animation: Animation;
-        /**
-         * Target to animate
-         */
-        public target: any;
+     * Animation to perform
+     */
+    public animation: Animation;
+    /**
+     * Target to animate
+     */
+    public target: any;
+}
+
+/**
+ * Use this class to create coordinated animations on multiple targets
+ */
+export class AnimationGroup implements IDisposable {
+    private _scene: Scene;
+
+    private _targetedAnimations = new Array<TargetedAnimation>();
+    private _animatables = new Array<Animatable>();
+    private _from = Number.MAX_VALUE;
+    private _to = -Number.MAX_VALUE;
+    private _isStarted: boolean;
+    private _isPaused: boolean;
+    private _speedRatio = 1;
+
+    /**
+     * Gets or sets the unique id of the node
+     */
+    public uniqueId: number;
+
+    /**
+     * This observable will notify when one animation have ended
+     */
+    public onAnimationEndObservable = new Observable<TargetedAnimation>();
+
+    /**
+     * Observer raised when one animation loops
+     */
+    public onAnimationLoopObservable = new Observable<TargetedAnimation>();
+
+    /**
+     * This observable will notify when all animations have ended.
+     */
+    public onAnimationGroupEndObservable = new Observable<AnimationGroup>();
+
+    /**
+     * This observable will notify when all animations have paused.
+     */
+    public onAnimationGroupPauseObservable = new Observable<AnimationGroup>();
+
+    /**
+     * This observable will notify when all animations are playing.
+     */
+    public onAnimationGroupPlayObservable = new Observable<AnimationGroup>();
+
+    /**
+     * Gets the first frame
+     */
+    public get from(): number {
+        return this._from;
     }
 
     /**
-     * Use this class to create coordinated animations on multiple targets
-     */
-    export class AnimationGroup implements IDisposable {
-        private _scene: Scene;
-
-        private _targetedAnimations = new Array<TargetedAnimation>();
-        private _animatables = new Array<Animatable>();
-        private _from = Number.MAX_VALUE;
-        private _to = -Number.MAX_VALUE;
-        private _isStarted: boolean;
-        private _isPaused: boolean;
-        private _speedRatio = 1;
-
-        /**
-         * Gets or sets the unique id of the node
-         */
-        public uniqueId: number;
-
-        /**
-         * This observable will notify when one animation have ended
-         */
-        public onAnimationEndObservable = new Observable<TargetedAnimation>();
-
-        /**
-         * Observer raised when one animation loops
-         */
-        public onAnimationLoopObservable = new Observable<TargetedAnimation>();
-
-        /**
-         * This observable will notify when all animations have ended.
-         */
-        public onAnimationGroupEndObservable = new Observable<AnimationGroup>();
-
-        /**
-         * This observable will notify when all animations have paused.
-         */
-        public onAnimationGroupPauseObservable = new Observable<AnimationGroup>();
-
-        /**
-         * This observable will notify when all animations are playing.
-         */
-        public onAnimationGroupPlayObservable = new Observable<AnimationGroup>();
-
-        /**
-         * Gets the first frame
-         */
-        public get from(): number {
-            return this._from;
-        }
+     * Gets the last frame
+     */
+    public get to(): number {
+        return this._to;
+    }
 
-        /**
-         * Gets the last frame
-         */
-        public get to(): number {
-            return this._to;
-        }
+    /**
+     * Define if the animations are started
+     */
+    public get isStarted(): boolean {
+        return this._isStarted;
+    }
 
-        /**
-         * Define if the animations are started
-         */
-        public get isStarted(): boolean {
-            return this._isStarted;
-        }
+    /**
+     * Gets a value indicating that the current group is playing
+     */
+    public get isPlaying(): boolean {
+        return this._isStarted && !this._isPaused;
+    }
 
-        /**
-         * Gets a value indicating that the current group is playing
-         */
-        public get isPlaying(): boolean {
-            return this._isStarted && !this._isPaused;
+    /**
+     * Gets or sets the speed ratio to use for all animations
+     */
+    public get speedRatio(): number {
+        return this._speedRatio;
+    }
+
+    /**
+     * Gets or sets the speed ratio to use for all animations
+     */
+    public set speedRatio(value: number) {
+        if (this._speedRatio === value) {
+            return;
         }
 
-        /**
-         * Gets or sets the speed ratio to use for all animations
-         */
-        public get speedRatio(): number {
-            return this._speedRatio;
+        this._speedRatio = value;
+
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.speedRatio = this._speedRatio;
         }
+    }
 
-        /**
-         * Gets or sets the speed ratio to use for all animations
-         */
-        public set speedRatio(value: number) {
-            if (this._speedRatio === value) {
-                return;
-            }
+    /**
+     * Gets the targeted animations for this animation group
+     */
+    public get targetedAnimations(): Array<TargetedAnimation> {
+        return this._targetedAnimations;
+    }
 
-            this._speedRatio = value;
+    /**
+     * returning the list of animatables controlled by this animation group.
+     */
+    public get animatables(): Array<Animatable> {
+        return this._animatables;
+    }
 
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.speedRatio = this._speedRatio;
-            }
-        }
+    /**
+     * Instantiates a new Animation Group.
+     * This helps managing several animations at once.
+     * @see http://doc.babylonjs.com/how_to/group
+     * @param name Defines the name of the group
+     * @param scene Defines the scene the group belongs to
+     */
+    public constructor(
+        /** The name of the animation group */
+        public name: string,
+        scene: Nullable<Scene> = null) {
+        this._scene = scene || EngineStore.LastCreatedScene!;
+        this.uniqueId = this._scene.getUniqueId();
+
+        this._scene.animationGroups.push(this);
+    }
 
-        /**
-         * Gets the targeted animations for this animation group
-         */
-        public get targetedAnimations(): Array<TargetedAnimation> {
-            return this._targetedAnimations;
+    /**
+     * Add an animation (with its target) in the group
+     * @param animation defines the animation we want to add
+     * @param target defines the target of the animation
+     * @returns the TargetedAnimation object
+     */
+    public addTargetedAnimation(animation: Animation, target: any): TargetedAnimation {
+        let targetedAnimation = {
+            animation: animation,
+            target: target
+        };
+
+        let keys = animation.getKeys();
+        if (this._from > keys[0].frame) {
+            this._from = keys[0].frame;
         }
 
-        /**
-         * returning the list of animatables controlled by this animation group.
-         */
-        public get animatables(): Array<Animatable> {
-            return this._animatables;
+        if (this._to < keys[keys.length - 1].frame) {
+            this._to = keys[keys.length - 1].frame;
         }
 
-        /**
-         * Instantiates a new Animation Group.
-         * This helps managing several animations at once.
-         * @see http://doc.babylonjs.com/how_to/group
-         * @param name Defines the name of the group
-         * @param scene Defines the scene the group belongs to
-         */
-        public constructor(
-            /** The name of the animation group */
-            public name: string,
-            scene: Nullable<Scene> = null) {
-            this._scene = scene || EngineStore.LastCreatedScene!;
-            this.uniqueId = this._scene.getUniqueId();
-
-            this._scene.animationGroups.push(this);
-        }
+        this._targetedAnimations.push(targetedAnimation);
 
-        /**
-         * Add an animation (with its target) in the group
-         * @param animation defines the animation we want to add
-         * @param target defines the target of the animation
-         * @returns the TargetedAnimation object
-         */
-        public addTargetedAnimation(animation: Animation, target: any): TargetedAnimation {
-            let targetedAnimation = {
-                animation: animation,
-                target: target
-            };
+        return targetedAnimation;
+    }
 
-            let keys = animation.getKeys();
-            if (this._from > keys[0].frame) {
-                this._from = keys[0].frame;
+    /**
+     * This function will normalize every animation in the group to make sure they all go from beginFrame to endFrame
+     * It can add constant keys at begin or end
+     * @param beginFrame defines the new begin frame for all animations or the smallest begin frame of all animations if null (defaults to null)
+     * @param endFrame defines the new end frame for all animations or the largest end frame of all animations if null (defaults to null)
+     * @returns the animation group
+     */
+    public normalize(beginFrame: Nullable<number> = null, endFrame: Nullable<number> = null): AnimationGroup {
+        if (beginFrame == null) { beginFrame = this._from; }
+        if (endFrame == null) { endFrame = this._to; }
+
+        for (var index = 0; index < this._targetedAnimations.length; index++) {
+            let targetedAnimation = this._targetedAnimations[index];
+            let keys = targetedAnimation.animation.getKeys();
+            let startKey = keys[0];
+            let endKey = keys[keys.length - 1];
+
+            if (startKey.frame > beginFrame) {
+                let newKey: IAnimationKey = {
+                    frame: beginFrame,
+                    value: startKey.value,
+                    inTangent: startKey.inTangent,
+                    outTangent: startKey.outTangent,
+                    interpolation: startKey.interpolation
+                };
+                keys.splice(0, 0, newKey);
             }
 
-            if (this._to < keys[keys.length - 1].frame) {
-                this._to = keys[keys.length - 1].frame;
+            if (endKey.frame < endFrame) {
+                let newKey: IAnimationKey = {
+                    frame: endFrame,
+                    value: endKey.value,
+                    inTangent: endKey.outTangent,
+                    outTangent: endKey.outTangent,
+                    interpolation: endKey.interpolation
+                };
+                keys.push(newKey);
             }
-
-            this._targetedAnimations.push(targetedAnimation);
-
-            return targetedAnimation;
         }
 
-        /**
-         * This function will normalize every animation in the group to make sure they all go from beginFrame to endFrame
-         * It can add constant keys at begin or end
-         * @param beginFrame defines the new begin frame for all animations or the smallest begin frame of all animations if null (defaults to null)
-         * @param endFrame defines the new end frame for all animations or the largest end frame of all animations if null (defaults to null)
-         * @returns the animation group
-         */
-        public normalize(beginFrame: Nullable<number> = null, endFrame: Nullable<number> = null): AnimationGroup {
-            if (beginFrame == null) { beginFrame = this._from; }
-            if (endFrame == null) { endFrame = this._to; }
-
-            for (var index = 0; index < this._targetedAnimations.length; index++) {
-                let targetedAnimation = this._targetedAnimations[index];
-                let keys = targetedAnimation.animation.getKeys();
-                let startKey = keys[0];
-                let endKey = keys[keys.length - 1];
-
-                if (startKey.frame > beginFrame) {
-                    let newKey: IAnimationKey = {
-                        frame: beginFrame,
-                        value: startKey.value,
-                        inTangent: startKey.inTangent,
-                        outTangent: startKey.outTangent,
-                        interpolation: startKey.interpolation
-                    };
-                    keys.splice(0, 0, newKey);
-                }
-
-                if (endKey.frame < endFrame) {
-                    let newKey: IAnimationKey = {
-                        frame: endFrame,
-                        value: endKey.value,
-                        inTangent: endKey.outTangent,
-                        outTangent: endKey.outTangent,
-                        interpolation: endKey.interpolation
-                    };
-                    keys.push(newKey);
-                }
-            }
+        this._from = beginFrame;
+        this._to = endFrame;
 
-            this._from = beginFrame;
-            this._to = endFrame;
+        return this;
+    }
 
+    /**
+     * Start all animations on given targets
+     * @param loop defines if animations must loop
+     * @param speedRatio defines the ratio to apply to animation speed (1 by default)
+     * @param from defines the from key (optional)
+     * @param to defines the to key (optional)
+     * @returns the current animation group
+     */
+    public start(loop = false, speedRatio = 1, from?: number, to?: number): AnimationGroup {
+        if (this._isStarted || this._targetedAnimations.length === 0) {
             return this;
         }
 
-        /**
-         * Start all animations on given targets
-         * @param loop defines if animations must loop
-         * @param speedRatio defines the ratio to apply to animation speed (1 by default)
-         * @param from defines the from key (optional)
-         * @param to defines the to key (optional)
-         * @returns the current animation group
-         */
-        public start(loop = false, speedRatio = 1, from?: number, to?: number): AnimationGroup {
-            if (this._isStarted || this._targetedAnimations.length === 0) {
-                return this;
-            }
+        for (const targetedAnimation of this._targetedAnimations) {
+            let animatable = this._scene.beginDirectAnimation(targetedAnimation.target, [targetedAnimation.animation], from !== undefined ? from : this._from, to !== undefined ? to : this._to, loop, speedRatio);
+            animatable.onAnimationEnd = () => {
+                this.onAnimationEndObservable.notifyObservers(targetedAnimation);
+                this._checkAnimationGroupEnded(animatable);
+            };
+            animatable.onAnimationLoop = () => {
+                this.onAnimationLoopObservable.notifyObservers(targetedAnimation);
+            };
+            this._animatables.push(animatable);
+        }
 
-            for (const targetedAnimation of this._targetedAnimations) {
-                let animatable = this._scene.beginDirectAnimation(targetedAnimation.target, [targetedAnimation.animation], from !== undefined ? from : this._from, to !== undefined ? to : this._to, loop, speedRatio);
-                animatable.onAnimationEnd = () => {
-                    this.onAnimationEndObservable.notifyObservers(targetedAnimation);
-                    this._checkAnimationGroupEnded(animatable);
-                };
-                animatable.onAnimationLoop = () => {
-                    this.onAnimationLoopObservable.notifyObservers(targetedAnimation);
-                };
-                this._animatables.push(animatable);
-            }
+        this._speedRatio = speedRatio;
 
-            this._speedRatio = speedRatio;
+        if (from !== undefined && to !== undefined && from > to && this._speedRatio > 0) {
+            this._speedRatio = -speedRatio;
+        }
 
-            if (from !== undefined && to !== undefined && from > to && this._speedRatio > 0) {
-                this._speedRatio = -speedRatio;
-            }
+        this._isStarted = true;
+        this._isPaused = false;
 
-            this._isStarted = true;
-            this._isPaused = false;
+        this.onAnimationGroupPlayObservable.notifyObservers(this);
 
-            this.onAnimationGroupPlayObservable.notifyObservers(this);
+        return this;
+    }
 
+    /**
+     * Pause all animations
+     * @returns the animation group
+     */
+    public pause(): AnimationGroup {
+        if (!this._isStarted) {
             return this;
         }
 
-        /**
-         * Pause all animations
-         * @returns the animation group
-         */
-        public pause(): AnimationGroup {
-            if (!this._isStarted) {
-                return this;
-            }
-
-            this._isPaused = true;
+        this._isPaused = true;
 
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.pause();
-            }
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.pause();
+        }
 
-            this.onAnimationGroupPauseObservable.notifyObservers(this);
+        this.onAnimationGroupPauseObservable.notifyObservers(this);
 
-            return this;
-        }
+        return this;
+    }
 
-        /**
-         * Play all animations to initial state
-         * This function will start() the animations if they were not started or will restart() them if they were paused
-         * @param loop defines if animations must loop
-         * @returns the animation group
-         */
-        public play(loop?: boolean): AnimationGroup {
-            // only if all animatables are ready and exist
-            if (this.isStarted && this._animatables.length === this._targetedAnimations.length) {
-                if (loop !== undefined) {
-                    for (var index = 0; index < this._animatables.length; index++) {
-                        let animatable = this._animatables[index];
-                        animatable.loopAnimation = loop;
-                    }
+    /**
+     * Play all animations to initial state
+     * This function will start() the animations if they were not started or will restart() them if they were paused
+     * @param loop defines if animations must loop
+     * @returns the animation group
+     */
+    public play(loop?: boolean): AnimationGroup {
+        // only if all animatables are ready and exist
+        if (this.isStarted && this._animatables.length === this._targetedAnimations.length) {
+            if (loop !== undefined) {
+                for (var index = 0; index < this._animatables.length; index++) {
+                    let animatable = this._animatables[index];
+                    animatable.loopAnimation = loop;
                 }
-                this.restart();
-            } else {
-                this.stop();
-                this.start(loop, this._speedRatio);
             }
+            this.restart();
+        } else {
+            this.stop();
+            this.start(loop, this._speedRatio);
+        }
 
-            this._isPaused = false;
+        this._isPaused = false;
 
+        return this;
+    }
+
+    /**
+     * Reset all animations to initial state
+     * @returns the animation group
+     */
+    public reset(): AnimationGroup {
+        if (!this._isStarted) {
             return this;
         }
 
-        /**
-         * Reset all animations to initial state
-         * @returns the animation group
-         */
-        public reset(): AnimationGroup {
-            if (!this._isStarted) {
-                return this;
-            }
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.reset();
+        }
 
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.reset();
-            }
+        return this;
+    }
 
+    /**
+     * Restart animations from key 0
+     * @returns the animation group
+     */
+    public restart(): AnimationGroup {
+        if (!this._isStarted) {
             return this;
         }
 
-        /**
-         * Restart animations from key 0
-         * @returns the animation group
-         */
-        public restart(): AnimationGroup {
-            if (!this._isStarted) {
-                return this;
-            }
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.restart();
+        }
 
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.restart();
-            }
+        this.onAnimationGroupPlayObservable.notifyObservers(this);
 
-            this.onAnimationGroupPlayObservable.notifyObservers(this);
+        return this;
+    }
 
+    /**
+     * Stop all animations
+     * @returns the animation group
+     */
+    public stop(): AnimationGroup {
+        if (!this._isStarted) {
             return this;
         }
 
-        /**
-         * Stop all animations
-         * @returns the animation group
-         */
-        public stop(): AnimationGroup {
-            if (!this._isStarted) {
-                return this;
-            }
+        var list = this._animatables.slice();
+        for (var index = 0; index < list.length; index++) {
+            list[index].stop();
+        }
 
-            var list = this._animatables.slice();
-            for (var index = 0; index < list.length; index++) {
-                list[index].stop();
-            }
+        this._isStarted = false;
 
-            this._isStarted = false;
+        return this;
+    }
 
-            return this;
+    /**
+     * Set animation weight for all animatables
+     * @param weight defines the weight to use
+     * @return the animationGroup
+     * @see http://doc.babylonjs.com/babylon101/animations#animation-weights
+     */
+    public setWeightForAllAnimatables(weight: number): AnimationGroup {
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.weight = weight;
         }
 
-        /**
-         * Set animation weight for all animatables
-         * @param weight defines the weight to use
-         * @return the animationGroup
-         * @see http://doc.babylonjs.com/babylon101/animations#animation-weights
-         */
-        public setWeightForAllAnimatables(weight: number): AnimationGroup {
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.weight = weight;
-            }
+        return this;
+    }
 
-            return this;
+    /**
+     * Synchronize and normalize all animatables with a source animatable
+     * @param root defines the root animatable to synchronize with
+     * @return the animationGroup
+     * @see http://doc.babylonjs.com/babylon101/animations#animation-weights
+     */
+    public syncAllAnimationsWith(root: Animatable): AnimationGroup {
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.syncWith(root);
         }
 
-        /**
-         * Synchronize and normalize all animatables with a source animatable
-         * @param root defines the root animatable to synchronize with
-         * @return the animationGroup
-         * @see http://doc.babylonjs.com/babylon101/animations#animation-weights
-         */
-        public syncAllAnimationsWith(root: Animatable): AnimationGroup {
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.syncWith(root);
-            }
+        return this;
+    }
 
+    /**
+     * Goes to a specific frame in this animation group
+     * @param frame the frame number to go to
+     * @return the animationGroup
+     */
+    public goToFrame(frame: number): AnimationGroup {
+        if (!this._isStarted) {
             return this;
         }
 
-        /**
-         * Goes to a specific frame in this animation group
-         * @param frame the frame number to go to
-         * @return the animationGroup
-         */
-        public goToFrame(frame: number): AnimationGroup {
-            if (!this._isStarted) {
-                return this;
-            }
-
-            for (var index = 0; index < this._animatables.length; index++) {
-                let animatable = this._animatables[index];
-                animatable.goToFrame(frame);
-            }
-
-            return this;
+        for (var index = 0; index < this._animatables.length; index++) {
+            let animatable = this._animatables[index];
+            animatable.goToFrame(frame);
         }
 
-        /**
-         * Dispose all associated resources
-         */
-        public dispose(): void {
-            this._targetedAnimations = [];
-            this._animatables = [];
+        return this;
+    }
 
-            var index = this._scene.animationGroups.indexOf(this);
+    /**
+     * Dispose all associated resources
+     */
+    public dispose(): void {
+        this._targetedAnimations = [];
+        this._animatables = [];
 
-            if (index > -1) {
-                this._scene.animationGroups.splice(index, 1);
-            }
+        var index = this._scene.animationGroups.indexOf(this);
 
-            this.onAnimationEndObservable.clear();
-            this.onAnimationGroupEndObservable.clear();
-            this.onAnimationGroupPauseObservable.clear();
-            this.onAnimationGroupPlayObservable.clear();
-            this.onAnimationLoopObservable.clear();
+        if (index > -1) {
+            this._scene.animationGroups.splice(index, 1);
         }
 
-        private _checkAnimationGroupEnded(animatable: Animatable) {
-            // animatable should be taken out of the array
-            let idx = this._animatables.indexOf(animatable);
-            if (idx > -1) {
-                this._animatables.splice(idx, 1);
-            }
+        this.onAnimationEndObservable.clear();
+        this.onAnimationGroupEndObservable.clear();
+        this.onAnimationGroupPauseObservable.clear();
+        this.onAnimationGroupPlayObservable.clear();
+        this.onAnimationLoopObservable.clear();
+    }
 
-            // all animatables were removed? animation group ended!
-            if (this._animatables.length === 0) {
-                this._isStarted = false;
-                this.onAnimationGroupEndObservable.notifyObservers(this);
-            }
+    private _checkAnimationGroupEnded(animatable: Animatable) {
+        // animatable should be taken out of the array
+        let idx = this._animatables.indexOf(animatable);
+        if (idx > -1) {
+            this._animatables.splice(idx, 1);
         }
 
-        // Statics
-        /**
-         * Returns a new AnimationGroup object parsed from the source provided.
-         * @param parsedAnimationGroup defines the source
-         * @param scene defines the scene that will receive the animationGroup
-         * @returns a new AnimationGroup
-         */
-        public static Parse(parsedAnimationGroup: any, scene: Scene): AnimationGroup {
-            var animationGroup = new AnimationGroup(parsedAnimationGroup.name, scene);
-            for (var i = 0; i < parsedAnimationGroup.targetedAnimations.length; i++) {
-                var targetedAnimation = parsedAnimationGroup.targetedAnimations[i];
-                var animation = Animation.Parse(targetedAnimation.animation);
-                var id = targetedAnimation.targetId;
-                if (targetedAnimation.animation.property === "influence") { // morph target animation
-                    let morphTarget = scene.getMorphTargetById(id);
-                    if (morphTarget) {
-                        animationGroup.addTargetedAnimation(animation, morphTarget);
-                    }
-                }
-                else {
-                    var targetNode = scene.getNodeByID(id);
+        // all animatables were removed? animation group ended!
+        if (this._animatables.length === 0) {
+            this._isStarted = false;
+            this.onAnimationGroupEndObservable.notifyObservers(this);
+        }
+    }
 
-                    if (targetNode != null) {
-                        animationGroup.addTargetedAnimation(animation, targetNode);
-                    }
+    // Statics
+    /**
+     * Returns a new AnimationGroup object parsed from the source provided.
+     * @param parsedAnimationGroup defines the source
+     * @param scene defines the scene that will receive the animationGroup
+     * @returns a new AnimationGroup
+     */
+    public static Parse(parsedAnimationGroup: any, scene: Scene): AnimationGroup {
+        var animationGroup = new AnimationGroup(parsedAnimationGroup.name, scene);
+        for (var i = 0; i < parsedAnimationGroup.targetedAnimations.length; i++) {
+            var targetedAnimation = parsedAnimationGroup.targetedAnimations[i];
+            var animation = Animation.Parse(targetedAnimation.animation);
+            var id = targetedAnimation.targetId;
+            if (targetedAnimation.animation.property === "influence") { // morph target animation
+                let morphTarget = scene.getMorphTargetById(id);
+                if (morphTarget) {
+                    animationGroup.addTargetedAnimation(animation, morphTarget);
                 }
             }
+            else {
+                var targetNode = scene.getNodeByID(id);
 
-            if (parsedAnimationGroup.from !== null && parsedAnimationGroup.from !== null) {
-                animationGroup.normalize(parsedAnimationGroup.from, parsedAnimationGroup.to);
+                if (targetNode != null) {
+                    animationGroup.addTargetedAnimation(animation, targetNode);
+                }
             }
-
-            return animationGroup;
         }
 
-        /**
-         * Returns the string "AnimationGroup"
-         * @returns "AnimationGroup"
-         */
-        public getClassName(): string {
-            return "AnimationGroup";
+        if (parsedAnimationGroup.from !== null && parsedAnimationGroup.from !== null) {
+            animationGroup.normalize(parsedAnimationGroup.from, parsedAnimationGroup.to);
         }
 
-        /**
-         * Creates a detailled string about the object
-         * @param fullDetails defines if the output string will support multiple levels of logging within scene loading
-         * @returns a string representing the object
-         */
-        public toString(fullDetails?: boolean): string {
-            var ret = "Name: " + this.name;
-            ret += ", type: " + this.getClassName();
-            if (fullDetails) {
-                ret += ", from: " + this._from;
-                ret += ", to: " + this._to;
-                ret += ", isStarted: " + this._isStarted;
-                ret += ", speedRatio: " + this._speedRatio;
-                ret += ", targetedAnimations length: " + this._targetedAnimations.length;
-                ret += ", animatables length: " + this._animatables;
-            }
-            return ret;
-        }
+        return animationGroup;
+    }
 
+    /**
+     * Returns the string "AnimationGroup"
+     * @returns "AnimationGroup"
+     */
+    public getClassName(): string {
+        return "AnimationGroup";
     }
+
+    /**
+     * Creates a detailled string about the object
+     * @param fullDetails defines if the output string will support multiple levels of logging within scene loading
+     * @returns a string representing the object
+     */
+    public toString(fullDetails?: boolean): string {
+        var ret = "Name: " + this.name;
+        ret += ", type: " + this.getClassName();
+        if (fullDetails) {
+            ret += ", from: " + this._from;
+            ret += ", to: " + this._to;
+            ret += ", isStarted: " + this._isStarted;
+            ret += ", speedRatio: " + this._speedRatio;
+            ret += ", targetedAnimations length: " + this._targetedAnimations.length;
+            ret += ", animatables length: " + this._animatables;
+        }
+        return ret;
+    }
+
+}

+ 15 - 15
src/Animations/animationPropertiesOverride.ts

@@ -1,21 +1,21 @@
 import { Animation } from "../Animations/animation";
 
+/**
+ * Class used to override all child animations of a given target
+ */
+export class AnimationPropertiesOverride {
     /**
-     * Class used to override all child animations of a given target
+     * Gets or sets a value indicating if animation blending must be used
      */
-    export class AnimationPropertiesOverride {
-        /**
-         * Gets or sets a value indicating if animation blending must be used
-         */
-        public enableBlending = false;
+    public enableBlending = false;
 
-        /**
-         * Gets or sets the blending speed to use when enableBlending is true
-         */
-        public blendingSpeed = 0.01;
+    /**
+     * Gets or sets the blending speed to use when enableBlending is true
+     */
+    public blendingSpeed = 0.01;
 
-        /**
-         * Gets or sets the default loop mode to use
-         */
-        public loopMode = Animation.ANIMATIONLOOPMODE_CYCLE;
-    }
+    /**
+     * Gets or sets the default loop mode to use
+     */
+    public loopMode = Animation.ANIMATIONLOOPMODE_CYCLE;
+}

+ 275 - 275
src/Animations/easing.ts

@@ -1,343 +1,343 @@
 import { BezierCurve } from "../Maths/math";
 
+/**
+ * This represents the main contract an easing function should follow.
+ * Easing functions are used throughout the animation system.
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export interface IEasingFunction {
     /**
-     * This represents the main contract an easing function should follow.
-     * Easing functions are used throughout the animation system.
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * Given an input gradient between 0 and 1, this returns the corrseponding value
+     * of the easing function.
+     * The link below provides some of the most common examples of easing functions.
+     * @see https://easings.net/
+     * @param gradient Defines the value between 0 and 1 we want the easing value for
+     * @returns the corresponding value on the curve defined by the easing function
      */
-    export interface IEasingFunction {
-        /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
-         * of the easing function.
-         * The link below provides some of the most common examples of easing functions.
-         * @see https://easings.net/
-         * @param gradient Defines the value between 0 and 1 we want the easing value for
-         * @returns the corresponding value on the curve defined by the easing function
-         */
-        ease(gradient: number): number;
-    }
+    ease(gradient: number): number;
+}
 
+/**
+ * Base class used for every default easing function.
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class EasingFunction implements IEasingFunction {
     /**
-     * Base class used for every default easing function.
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * Interpolation follows the mathematical formula associated with the easing function.
      */
-    export class EasingFunction implements IEasingFunction {
-        /**
-         * Interpolation follows the mathematical formula associated with the easing function.
-         */
-        public static readonly EASINGMODE_EASEIN = 0;
-
-        /**
-         * Interpolation follows 100% interpolation minus the output of the formula associated with the easing function.
-         */
-        public static readonly EASINGMODE_EASEOUT = 1;
-
-        /**
-         * Interpolation uses EaseIn for the first half of the animation and EaseOut for the second half.
-         */
-        public static readonly EASINGMODE_EASEINOUT = 2;
-
-        private _easingMode = EasingFunction.EASINGMODE_EASEIN;
+    public static readonly EASINGMODE_EASEIN = 0;
 
-        /**
-         * Sets the easing mode of the current function.
-         * @param easingMode Defines the willing mode (EASINGMODE_EASEIN, EASINGMODE_EASEOUT or EASINGMODE_EASEINOUT)
-         */
-        public setEasingMode(easingMode: number) {
-            var n = Math.min(Math.max(easingMode, 0), 2);
-            this._easingMode = n;
-        }
-        /**
-         * Gets the current easing mode.
-         * @returns the easing mode
-         */
-        public getEasingMode(): number {
-            return this._easingMode;
-        }
-
-        /**
-         * @hidden
-         */
-        public easeInCore(gradient: number): number {
-            throw new Error('You must implement this method');
-        }
+    /**
+     * Interpolation follows 100% interpolation minus the output of the formula associated with the easing function.
+     */
+    public static readonly EASINGMODE_EASEOUT = 1;
 
-        /**
-         * Given an input gradient between 0 and 1, this returns the corrseponding value
-         * of the easing function.
-         * @param gradient Defines the value between 0 and 1 we want the easing value for
-         * @returns the corresponding value on the curve defined by the easing function
-         */
-        public ease(gradient: number): number {
-            switch (this._easingMode) {
-                case EasingFunction.EASINGMODE_EASEIN:
-                    return this.easeInCore(gradient);
-                case EasingFunction.EASINGMODE_EASEOUT:
-                    return (1 - this.easeInCore(1 - gradient));
-            }
+    /**
+     * Interpolation uses EaseIn for the first half of the animation and EaseOut for the second half.
+     */
+    public static readonly EASINGMODE_EASEINOUT = 2;
 
-            if (gradient >= 0.5) {
-                return (((1 - this.easeInCore((1 - gradient) * 2)) * 0.5) + 0.5);
-            }
+    private _easingMode = EasingFunction.EASINGMODE_EASEIN;
 
-            return (this.easeInCore(gradient * 2) * 0.5);
-        }
+    /**
+     * Sets the easing mode of the current function.
+     * @param easingMode Defines the willing mode (EASINGMODE_EASEIN, EASINGMODE_EASEOUT or EASINGMODE_EASEINOUT)
+     */
+    public setEasingMode(easingMode: number) {
+        var n = Math.min(Math.max(easingMode, 0), 2);
+        this._easingMode = n;
+    }
+    /**
+     * Gets the current easing mode.
+     * @returns the easing mode
+     */
+    public getEasingMode(): number {
+        return this._easingMode;
     }
 
     /**
-     * Easing function with a circle shape (see link below).
-     * @see https://easings.net/#easeInCirc
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * @hidden
      */
-    export class CircleEase extends EasingFunction implements IEasingFunction {
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            gradient = Math.max(0, Math.min(1, gradient));
-            return (1.0 - Math.sqrt(1.0 - (gradient * gradient)));
-        }
+    public easeInCore(gradient: number): number {
+        throw new Error('You must implement this method');
     }
 
     /**
-     * Easing function with a ease back shape (see link below).
-     * @see https://easings.net/#easeInBack
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * Given an input gradient between 0 and 1, this returns the corrseponding value
+     * of the easing function.
+     * @param gradient Defines the value between 0 and 1 we want the easing value for
+     * @returns the corresponding value on the curve defined by the easing function
      */
-    export class BackEase extends EasingFunction implements IEasingFunction {
-        /**
-         * Instantiates a back ease easing
-         * @see https://easings.net/#easeInBack
-         * @param amplitude Defines the amplitude of the function
-         */
-        constructor(
-            /** Defines the amplitude of the function */
-            public amplitude: number = 1) {
-            super();
+    public ease(gradient: number): number {
+        switch (this._easingMode) {
+            case EasingFunction.EASINGMODE_EASEIN:
+                return this.easeInCore(gradient);
+            case EasingFunction.EASINGMODE_EASEOUT:
+                return (1 - this.easeInCore(1 - gradient));
         }
 
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            var num = Math.max(0, this.amplitude);
-            return (Math.pow(gradient, 3.0) - ((gradient * num) * Math.sin(3.1415926535897931 * gradient)));
+        if (gradient >= 0.5) {
+            return (((1 - this.easeInCore((1 - gradient) * 2)) * 0.5) + 0.5);
         }
+
+        return (this.easeInCore(gradient * 2) * 0.5);
+    }
+}
+
+/**
+ * Easing function with a circle shape (see link below).
+ * @see https://easings.net/#easeInCirc
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class CircleEase extends EasingFunction implements IEasingFunction {
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        gradient = Math.max(0, Math.min(1, gradient));
+        return (1.0 - Math.sqrt(1.0 - (gradient * gradient)));
     }
+}
 
+/**
+ * Easing function with a ease back shape (see link below).
+ * @see https://easings.net/#easeInBack
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class BackEase extends EasingFunction implements IEasingFunction {
     /**
-     * Easing function with a bouncing shape (see link below).
-     * @see https://easings.net/#easeInBounce
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * Instantiates a back ease easing
+     * @see https://easings.net/#easeInBack
+     * @param amplitude Defines the amplitude of the function
      */
-    export class BounceEase extends EasingFunction implements IEasingFunction {
-        /**
-         * Instantiates a bounce easing
-         * @see https://easings.net/#easeInBounce
-         * @param bounces Defines the number of bounces
-         * @param bounciness Defines the amplitude of the bounce
-         */
-        constructor(
-            /** Defines the number of bounces */
-            public bounces: number= 3,
-            /** Defines the amplitude of the bounce */
-            public bounciness: number= 2) {
-            super();
-        }
+    constructor(
+        /** Defines the amplitude of the function */
+        public amplitude: number = 1) {
+        super();
+    }
 
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            var y = Math.max(0.0, this.bounces);
-            var bounciness = this.bounciness;
-            if (bounciness <= 1.0) {
-                bounciness = 1.001;
-            }
-            var num9 = Math.pow(bounciness, y);
-            var num5 = 1.0 - bounciness;
-            var num4 = ((1.0 - num9) / num5) + (num9 * 0.5);
-            var num15 = gradient * num4;
-            var num65 = Math.log((-num15 * (1.0 - bounciness)) + 1.0) / Math.log(bounciness);
-            var num3 = Math.floor(num65);
-            var num13 = num3 + 1.0;
-            var num8 = (1.0 - Math.pow(bounciness, num3)) / (num5 * num4);
-            var num12 = (1.0 - Math.pow(bounciness, num13)) / (num5 * num4);
-            var num7 = (num8 + num12) * 0.5;
-            var num6 = gradient - num7;
-            var num2 = num7 - num8;
-            return (((-Math.pow(1.0 / bounciness, y - num3) / (num2 * num2)) * (num6 - num2)) * (num6 + num2));
-        }
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        var num = Math.max(0, this.amplitude);
+        return (Math.pow(gradient, 3.0) - ((gradient * num) * Math.sin(3.1415926535897931 * gradient)));
     }
+}
 
+/**
+ * Easing function with a bouncing shape (see link below).
+ * @see https://easings.net/#easeInBounce
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class BounceEase extends EasingFunction implements IEasingFunction {
     /**
-     * Easing function with a power of 3 shape (see link below).
-     * @see https://easings.net/#easeInCubic
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * Instantiates a bounce easing
+     * @see https://easings.net/#easeInBounce
+     * @param bounces Defines the number of bounces
+     * @param bounciness Defines the amplitude of the bounce
      */
-    export class CubicEase extends EasingFunction implements IEasingFunction {
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            return (gradient * gradient * gradient);
+    constructor(
+        /** Defines the number of bounces */
+        public bounces: number = 3,
+        /** Defines the amplitude of the bounce */
+        public bounciness: number = 2) {
+        super();
+    }
+
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        var y = Math.max(0.0, this.bounces);
+        var bounciness = this.bounciness;
+        if (bounciness <= 1.0) {
+            bounciness = 1.001;
         }
+        var num9 = Math.pow(bounciness, y);
+        var num5 = 1.0 - bounciness;
+        var num4 = ((1.0 - num9) / num5) + (num9 * 0.5);
+        var num15 = gradient * num4;
+        var num65 = Math.log((-num15 * (1.0 - bounciness)) + 1.0) / Math.log(bounciness);
+        var num3 = Math.floor(num65);
+        var num13 = num3 + 1.0;
+        var num8 = (1.0 - Math.pow(bounciness, num3)) / (num5 * num4);
+        var num12 = (1.0 - Math.pow(bounciness, num13)) / (num5 * num4);
+        var num7 = (num8 + num12) * 0.5;
+        var num6 = gradient - num7;
+        var num2 = num7 - num8;
+        return (((-Math.pow(1.0 / bounciness, y - num3) / (num2 * num2)) * (num6 - num2)) * (num6 + num2));
+    }
+}
+
+/**
+ * Easing function with a power of 3 shape (see link below).
+ * @see https://easings.net/#easeInCubic
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class CubicEase extends EasingFunction implements IEasingFunction {
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        return (gradient * gradient * gradient);
     }
+}
 
+/**
+ * Easing function with an elastic shape (see link below).
+ * @see https://easings.net/#easeInElastic
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class ElasticEase extends EasingFunction implements IEasingFunction {
     /**
-     * Easing function with an elastic shape (see link below).
+     * Instantiates an elastic easing function
      * @see https://easings.net/#easeInElastic
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * @param oscillations Defines the number of oscillations
+     * @param springiness Defines the amplitude of the oscillations
      */
-    export class ElasticEase extends EasingFunction implements IEasingFunction {
-        /**
-         * Instantiates an elastic easing function
-         * @see https://easings.net/#easeInElastic
-         * @param oscillations Defines the number of oscillations
-         * @param springiness Defines the amplitude of the oscillations
-         */
-        constructor(
-            /** Defines the number of oscillations*/
-            public oscillations: number= 3,
-            /** Defines the amplitude of the oscillations*/
-            public springiness: number= 3) {
-            super();
-        }
+    constructor(
+        /** Defines the number of oscillations*/
+        public oscillations: number = 3,
+        /** Defines the amplitude of the oscillations*/
+        public springiness: number = 3) {
+        super();
+    }
 
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            var num2;
-            var num3 = Math.max(0.0, this.oscillations);
-            var num = Math.max(0.0, this.springiness);
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        var num2;
+        var num3 = Math.max(0.0, this.oscillations);
+        var num = Math.max(0.0, this.springiness);
 
-            if (num == 0) {
-                num2 = gradient;
-            }else {
-                num2 = (Math.exp(num * gradient) - 1.0) / (Math.exp(num) - 1.0);
-            }
-            return (num2 * Math.sin(((6.2831853071795862 * num3) + 1.5707963267948966) * gradient));
+        if (num == 0) {
+            num2 = gradient;
+        } else {
+            num2 = (Math.exp(num * gradient) - 1.0) / (Math.exp(num) - 1.0);
         }
+        return (num2 * Math.sin(((6.2831853071795862 * num3) + 1.5707963267948966) * gradient));
     }
+}
 
+/**
+ * Easing function with an exponential shape (see link below).
+ * @see https://easings.net/#easeInExpo
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class ExponentialEase extends EasingFunction implements IEasingFunction {
     /**
-     * Easing function with an exponential shape (see link below).
+     * Instantiates an exponential easing function
      * @see https://easings.net/#easeInExpo
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * @param exponent Defines the exponent of the function
      */
-    export class ExponentialEase extends EasingFunction implements IEasingFunction {
-        /**
-         * Instantiates an exponential easing function
-         * @see https://easings.net/#easeInExpo
-         * @param exponent Defines the exponent of the function
-         */
-        constructor(
-            /** Defines the exponent of the function */
-            public exponent: number= 2) {
-            super();
-        }
-
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            if (this.exponent <= 0) {
-                return gradient;
-            }
+    constructor(
+        /** Defines the exponent of the function */
+        public exponent: number = 2) {
+        super();
+    }
 
-            return ((Math.exp(this.exponent * gradient) - 1.0) / (Math.exp(this.exponent) - 1.0));
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        if (this.exponent <= 0) {
+            return gradient;
         }
+
+        return ((Math.exp(this.exponent * gradient) - 1.0) / (Math.exp(this.exponent) - 1.0));
     }
+}
 
+/**
+ * Easing function with a power shape (see link below).
+ * @see https://easings.net/#easeInQuad
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class PowerEase extends EasingFunction implements IEasingFunction {
     /**
-     * Easing function with a power shape (see link below).
+     * Instantiates an power base easing function
      * @see https://easings.net/#easeInQuad
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * @param power Defines the power of the function
      */
-    export class PowerEase  extends EasingFunction implements IEasingFunction {
-        /**
-         * Instantiates an power base easing function
-         * @see https://easings.net/#easeInQuad
-         * @param power Defines the power of the function
-         */
-        constructor(
-            /** Defines the power of the function */
-            public power: number= 2) {
-            super();
-        }
+    constructor(
+        /** Defines the power of the function */
+        public power: number = 2) {
+        super();
+    }
 
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            var y = Math.max(0.0, this.power);
-            return Math.pow(gradient, y);
-        }
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        var y = Math.max(0.0, this.power);
+        return Math.pow(gradient, y);
     }
+}
 
-    /**
-     * Easing function with a power of 2 shape (see link below).
-     * @see https://easings.net/#easeInQuad
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
-     */
-    export class QuadraticEase extends EasingFunction implements IEasingFunction {
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            return (gradient * gradient);
-        }
+/**
+ * Easing function with a power of 2 shape (see link below).
+ * @see https://easings.net/#easeInQuad
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class QuadraticEase extends EasingFunction implements IEasingFunction {
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        return (gradient * gradient);
     }
+}
 
-    /**
-     * Easing function with a power of 4 shape (see link below).
-     * @see https://easings.net/#easeInQuart
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
-     */
-    export class QuarticEase extends EasingFunction implements IEasingFunction {
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            return (gradient * gradient * gradient * gradient);
-        }
+/**
+ * Easing function with a power of 4 shape (see link below).
+ * @see https://easings.net/#easeInQuart
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class QuarticEase extends EasingFunction implements IEasingFunction {
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        return (gradient * gradient * gradient * gradient);
     }
+}
 
-    /**
-     * Easing function with a power of 5 shape (see link below).
-     * @see https://easings.net/#easeInQuint
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
-     */
-    export class QuinticEase extends EasingFunction implements IEasingFunction {
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            return (gradient * gradient * gradient * gradient * gradient);
-        }
+/**
+ * Easing function with a power of 5 shape (see link below).
+ * @see https://easings.net/#easeInQuint
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class QuinticEase extends EasingFunction implements IEasingFunction {
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        return (gradient * gradient * gradient * gradient * gradient);
     }
+}
 
-    /**
-     * Easing function with a sin shape (see link below).
-     * @see https://easings.net/#easeInSine
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
-     */
-    export class SineEase  extends EasingFunction implements IEasingFunction {
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            return (1.0 - Math.sin(1.5707963267948966 * (1.0 - gradient)));
-        }
+/**
+ * Easing function with a sin shape (see link below).
+ * @see https://easings.net/#easeInSine
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class SineEase extends EasingFunction implements IEasingFunction {
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        return (1.0 - Math.sin(1.5707963267948966 * (1.0 - gradient)));
     }
+}
 
+/**
+ * Easing function with a bezier shape (see link below).
+ * @see http://cubic-bezier.com/#.17,.67,.83,.67
+ * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+ */
+export class BezierCurveEase extends EasingFunction implements IEasingFunction {
     /**
-     * Easing function with a bezier shape (see link below).
+     * Instantiates a bezier function
      * @see http://cubic-bezier.com/#.17,.67,.83,.67
-     * @see http://doc.babylonjs.com/babylon101/animations#easing-functions
+     * @param x1 Defines the x component of the start tangent in the bezier curve
+     * @param y1 Defines the y component of the start tangent in the bezier curve
+     * @param x2 Defines the x component of the end tangent in the bezier curve
+     * @param y2 Defines the y component of the end tangent in the bezier curve
      */
-    export class BezierCurveEase extends EasingFunction implements IEasingFunction {
-        /**
-         * Instantiates a bezier function
-         * @see http://cubic-bezier.com/#.17,.67,.83,.67
-         * @param x1 Defines the x component of the start tangent in the bezier curve
-         * @param y1 Defines the y component of the start tangent in the bezier curve
-         * @param x2 Defines the x component of the end tangent in the bezier curve
-         * @param y2 Defines the y component of the end tangent in the bezier curve
-         */
-        constructor(
-            /** Defines the x component of the start tangent in the bezier curve */
-            public x1: number= 0,
-            /** Defines the y component of the start tangent in the bezier curve */
-            public y1: number= 0,
-            /** Defines the x component of the end tangent in the bezier curve */
-            public x2: number= 1,
-            /** Defines the y component of the end tangent in the bezier curve */
-            public y2: number= 1) {
-            super();
-        }
+    constructor(
+        /** Defines the x component of the start tangent in the bezier curve */
+        public x1: number = 0,
+        /** Defines the y component of the start tangent in the bezier curve */
+        public y1: number = 0,
+        /** Defines the x component of the end tangent in the bezier curve */
+        public x2: number = 1,
+        /** Defines the y component of the end tangent in the bezier curve */
+        public y2: number = 1) {
+        super();
+    }
 
-        /** @hidden */
-        public easeInCore(gradient: number): number {
-            return BezierCurve.Interpolate(gradient, this.x1, this.y1, this.x2, this.y2);
-        }
+    /** @hidden */
+    public easeInCore(gradient: number): number {
+        return BezierCurve.Interpolate(gradient, this.x1, this.y1, this.x2, this.y2);
     }
+}

文件差異過大導致無法顯示
+ 507 - 507
src/Animations/runtimeAnimation.ts


+ 163 - 163
src/Audio/analyser.ts

@@ -3,193 +3,193 @@ import { Scene } from "../scene";
 import { IAudioEngine } from "../Audio/audioEngine";
 import { Engine } from "../Engines/engine";
 
+/**
+ * Class used to work with sound analyzer using fast fourier transform (FFT)
+ * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music
+ */
+export class Analyser {
     /**
-     * Class used to work with sound analyzer using fast fourier transform (FFT)
-     * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music
+     * Gets or sets the smoothing
+     * @ignorenaming
      */
-    export class Analyser {
-        /**
-         * Gets or sets the smoothing
-         * @ignorenaming
-         */
-        public SMOOTHING = 0.75;
-        /**
-         * Gets or sets the FFT table size
-         * @ignorenaming
-         */
-        public FFT_SIZE = 512;
-        /**
-         * Gets or sets the bar graph amplitude
-         * @ignorenaming
-         */
-        public BARGRAPHAMPLITUDE = 256;
-        /**
-         * Gets or sets the position of the debug canvas
-         * @ignorenaming
-         */
-        public DEBUGCANVASPOS = { x: 20, y: 20 };
-        /**
-         * Gets or sets the debug canvas size
-         * @ignorenaming
-         */
-        public DEBUGCANVASSIZE = { width: 320, height: 200 };
+    public SMOOTHING = 0.75;
+    /**
+     * Gets or sets the FFT table size
+     * @ignorenaming
+     */
+    public FFT_SIZE = 512;
+    /**
+     * Gets or sets the bar graph amplitude
+     * @ignorenaming
+     */
+    public BARGRAPHAMPLITUDE = 256;
+    /**
+     * Gets or sets the position of the debug canvas
+     * @ignorenaming
+     */
+    public DEBUGCANVASPOS = { x: 20, y: 20 };
+    /**
+     * Gets or sets the debug canvas size
+     * @ignorenaming
+     */
+    public DEBUGCANVASSIZE = { width: 320, height: 200 };
 
-        private _byteFreqs: Uint8Array;
-        private _byteTime: Uint8Array;
-        private _floatFreqs: Float32Array;
-        private _webAudioAnalyser: AnalyserNode;
-        private _debugCanvas: Nullable< HTMLCanvasElement>;
-        private _debugCanvasContext: Nullable<CanvasRenderingContext2D>;
-        private _scene: Scene;
-        private _registerFunc: Nullable<() => void>;
-        private _audioEngine: IAudioEngine;
+    private _byteFreqs: Uint8Array;
+    private _byteTime: Uint8Array;
+    private _floatFreqs: Float32Array;
+    private _webAudioAnalyser: AnalyserNode;
+    private _debugCanvas: Nullable<HTMLCanvasElement>;
+    private _debugCanvasContext: Nullable<CanvasRenderingContext2D>;
+    private _scene: Scene;
+    private _registerFunc: Nullable<() => void>;
+    private _audioEngine: IAudioEngine;
 
-        /**
-         * Creates a new analyser
-         * @param scene defines hosting scene
-         */
-        constructor(scene: Scene) {
-            this._scene = scene;
-            this._audioEngine = Engine.audioEngine;
-            if (this._audioEngine.canUseWebAudio && this._audioEngine.audioContext) {
-                this._webAudioAnalyser = this._audioEngine.audioContext.createAnalyser();
-                this._webAudioAnalyser.minDecibels = -140;
-                this._webAudioAnalyser.maxDecibels = 0;
-                this._byteFreqs = new Uint8Array(this._webAudioAnalyser.frequencyBinCount);
-                this._byteTime = new Uint8Array(this._webAudioAnalyser.frequencyBinCount);
-                this._floatFreqs = new Float32Array(this._webAudioAnalyser.frequencyBinCount);
-            }
+    /**
+     * Creates a new analyser
+     * @param scene defines hosting scene
+     */
+    constructor(scene: Scene) {
+        this._scene = scene;
+        this._audioEngine = Engine.audioEngine;
+        if (this._audioEngine.canUseWebAudio && this._audioEngine.audioContext) {
+            this._webAudioAnalyser = this._audioEngine.audioContext.createAnalyser();
+            this._webAudioAnalyser.minDecibels = -140;
+            this._webAudioAnalyser.maxDecibels = 0;
+            this._byteFreqs = new Uint8Array(this._webAudioAnalyser.frequencyBinCount);
+            this._byteTime = new Uint8Array(this._webAudioAnalyser.frequencyBinCount);
+            this._floatFreqs = new Float32Array(this._webAudioAnalyser.frequencyBinCount);
         }
+    }
 
-        /**
-         * Get the number of data values you will have to play with for the visualization
-         * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/frequencyBinCount
-         * @returns a number
-         */
-        public getFrequencyBinCount(): number {
-            if (this._audioEngine.canUseWebAudio) {
-                return this._webAudioAnalyser.frequencyBinCount;
-            }
-            else {
-                return 0;
-            }
+    /**
+     * Get the number of data values you will have to play with for the visualization
+     * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/frequencyBinCount
+     * @returns a number
+     */
+    public getFrequencyBinCount(): number {
+        if (this._audioEngine.canUseWebAudio) {
+            return this._webAudioAnalyser.frequencyBinCount;
+        }
+        else {
+            return 0;
         }
+    }
 
-        /**
-         * Gets the current frequency data as a byte array
-         * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteFrequencyData
-         * @returns a Uint8Array
-         */
-        public getByteFrequencyData(): Uint8Array {
-            if (this._audioEngine.canUseWebAudio) {
-                this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
-                this._webAudioAnalyser.fftSize = this.FFT_SIZE;
-                this._webAudioAnalyser.getByteFrequencyData(this._byteFreqs);
-            }
-            return this._byteFreqs;
+    /**
+     * Gets the current frequency data as a byte array
+     * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteFrequencyData
+     * @returns a Uint8Array
+     */
+    public getByteFrequencyData(): Uint8Array {
+        if (this._audioEngine.canUseWebAudio) {
+            this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
+            this._webAudioAnalyser.fftSize = this.FFT_SIZE;
+            this._webAudioAnalyser.getByteFrequencyData(this._byteFreqs);
         }
+        return this._byteFreqs;
+    }
 
-        /**
-         * Gets the current waveform as a byte array
-         * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteTimeDomainData
-         * @returns a Uint8Array
-         */
-        public getByteTimeDomainData(): Uint8Array {
-            if (this._audioEngine.canUseWebAudio) {
-                this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
-                this._webAudioAnalyser.fftSize = this.FFT_SIZE;
-                this._webAudioAnalyser.getByteTimeDomainData(this._byteTime);
-            }
-            return this._byteTime;
+    /**
+     * Gets the current waveform as a byte array
+     * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteTimeDomainData
+     * @returns a Uint8Array
+     */
+    public getByteTimeDomainData(): Uint8Array {
+        if (this._audioEngine.canUseWebAudio) {
+            this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
+            this._webAudioAnalyser.fftSize = this.FFT_SIZE;
+            this._webAudioAnalyser.getByteTimeDomainData(this._byteTime);
         }
+        return this._byteTime;
+    }
 
-        /**
-         * Gets the current frequency data as a float array
-         * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteFrequencyData
-         * @returns a Float32Array
-         */
-        public getFloatFrequencyData(): Float32Array {
-            if (this._audioEngine.canUseWebAudio) {
-                this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
-                this._webAudioAnalyser.fftSize = this.FFT_SIZE;
-                this._webAudioAnalyser.getFloatFrequencyData(this._floatFreqs);
-            }
-            return this._floatFreqs;
+    /**
+     * Gets the current frequency data as a float array
+     * @see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getByteFrequencyData
+     * @returns a Float32Array
+     */
+    public getFloatFrequencyData(): Float32Array {
+        if (this._audioEngine.canUseWebAudio) {
+            this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
+            this._webAudioAnalyser.fftSize = this.FFT_SIZE;
+            this._webAudioAnalyser.getFloatFrequencyData(this._floatFreqs);
         }
+        return this._floatFreqs;
+    }
 
-        /**
-         * Renders the debug canvas
-         */
-        public drawDebugCanvas() {
-            if (this._audioEngine.canUseWebAudio) {
-                if (!this._debugCanvas) {
-                    this._debugCanvas = document.createElement("canvas");
-                    this._debugCanvas.width = this.DEBUGCANVASSIZE.width;
-                    this._debugCanvas.height = this.DEBUGCANVASSIZE.height;
-                    this._debugCanvas.style.position = "absolute";
-                    this._debugCanvas.style.top = this.DEBUGCANVASPOS.y + "px" ;
-                    this._debugCanvas.style.left = this.DEBUGCANVASPOS.x + "px" ;
-                    this._debugCanvasContext = this._debugCanvas.getContext("2d");
-                    document.body.appendChild(this._debugCanvas);
-                    this._registerFunc = () => {
-                        this.drawDebugCanvas();
-                    };
-                    this._scene.registerBeforeRender(this._registerFunc);
-                }
-                if (this._registerFunc && this._debugCanvasContext) {
-                    var workingArray = this.getByteFrequencyData();
+    /**
+     * Renders the debug canvas
+     */
+    public drawDebugCanvas() {
+        if (this._audioEngine.canUseWebAudio) {
+            if (!this._debugCanvas) {
+                this._debugCanvas = document.createElement("canvas");
+                this._debugCanvas.width = this.DEBUGCANVASSIZE.width;
+                this._debugCanvas.height = this.DEBUGCANVASSIZE.height;
+                this._debugCanvas.style.position = "absolute";
+                this._debugCanvas.style.top = this.DEBUGCANVASPOS.y + "px";
+                this._debugCanvas.style.left = this.DEBUGCANVASPOS.x + "px";
+                this._debugCanvasContext = this._debugCanvas.getContext("2d");
+                document.body.appendChild(this._debugCanvas);
+                this._registerFunc = () => {
+                    this.drawDebugCanvas();
+                };
+                this._scene.registerBeforeRender(this._registerFunc);
+            }
+            if (this._registerFunc && this._debugCanvasContext) {
+                var workingArray = this.getByteFrequencyData();
 
-                    this._debugCanvasContext.fillStyle = 'rgb(0, 0, 0)';
-                    this._debugCanvasContext.fillRect(0, 0, this.DEBUGCANVASSIZE.width, this.DEBUGCANVASSIZE.height);
+                this._debugCanvasContext.fillStyle = 'rgb(0, 0, 0)';
+                this._debugCanvasContext.fillRect(0, 0, this.DEBUGCANVASSIZE.width, this.DEBUGCANVASSIZE.height);
 
-                    // Draw the frequency domain chart.
-                    for (var i = 0; i < this.getFrequencyBinCount(); i++) {
-                        var value = workingArray[i];
-                        var percent = value / this.BARGRAPHAMPLITUDE;
-                        var height = this.DEBUGCANVASSIZE.height * percent;
-                        var offset = this.DEBUGCANVASSIZE.height - height - 1;
-                        var barWidth = this.DEBUGCANVASSIZE.width / this.getFrequencyBinCount();
-                        var hue = i / this.getFrequencyBinCount() * 360;
-                        this._debugCanvasContext.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
-                        this._debugCanvasContext.fillRect(i * barWidth, offset, barWidth, height);
-                    }
+                // Draw the frequency domain chart.
+                for (var i = 0; i < this.getFrequencyBinCount(); i++) {
+                    var value = workingArray[i];
+                    var percent = value / this.BARGRAPHAMPLITUDE;
+                    var height = this.DEBUGCANVASSIZE.height * percent;
+                    var offset = this.DEBUGCANVASSIZE.height - height - 1;
+                    var barWidth = this.DEBUGCANVASSIZE.width / this.getFrequencyBinCount();
+                    var hue = i / this.getFrequencyBinCount() * 360;
+                    this._debugCanvasContext.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
+                    this._debugCanvasContext.fillRect(i * barWidth, offset, barWidth, height);
                 }
             }
         }
+    }
 
-        /**
-         * Stops rendering the debug canvas and removes it
-         */
-        public stopDebugCanvas() {
-            if (this._debugCanvas) {
-                if (this._registerFunc) {
-                    this._scene.unregisterBeforeRender(this._registerFunc);
-                    this._registerFunc = null;
-                }
-                document.body.removeChild(this._debugCanvas);
-                this._debugCanvas = null;
-                this._debugCanvasContext = null;
+    /**
+     * Stops rendering the debug canvas and removes it
+     */
+    public stopDebugCanvas() {
+        if (this._debugCanvas) {
+            if (this._registerFunc) {
+                this._scene.unregisterBeforeRender(this._registerFunc);
+                this._registerFunc = null;
             }
+            document.body.removeChild(this._debugCanvas);
+            this._debugCanvas = null;
+            this._debugCanvasContext = null;
         }
+    }
 
-        /**
-         * Connects two audio nodes
-         * @param inputAudioNode defines first node to connect
-         * @param outputAudioNode defines second node to connect
-         */
-        public connectAudioNodes(inputAudioNode: AudioNode, outputAudioNode: AudioNode) {
-            if (this._audioEngine.canUseWebAudio) {
-                inputAudioNode.connect(this._webAudioAnalyser);
-                this._webAudioAnalyser.connect(outputAudioNode);
-            }
+    /**
+     * Connects two audio nodes
+     * @param inputAudioNode defines first node to connect
+     * @param outputAudioNode defines second node to connect
+     */
+    public connectAudioNodes(inputAudioNode: AudioNode, outputAudioNode: AudioNode) {
+        if (this._audioEngine.canUseWebAudio) {
+            inputAudioNode.connect(this._webAudioAnalyser);
+            this._webAudioAnalyser.connect(outputAudioNode);
         }
+    }
 
-        /**
-         * Releases all associated resources
-         */
-        public dispose() {
-            if (this._audioEngine.canUseWebAudio) {
-                this._webAudioAnalyser.disconnect();
-            }
+    /**
+     * Releases all associated resources
+     */
+    public dispose() {
+        if (this._audioEngine.canUseWebAudio) {
+            this._webAudioAnalyser.disconnect();
         }
     }
+}

文件差異過大導致無法顯示
+ 320 - 320
src/Audio/audioEngine.ts


+ 305 - 305
src/Audio/audioSceneComponent.ts

@@ -9,33 +9,33 @@ import { Scene } from "../scene";
 import { AbstractScene } from "../abstractScene";
 import { AssetContainer } from "../assetContainer";
 
-    // Adds the parser to the scene parsers.
-    AbstractScene.AddParser(SceneComponentConstants.NAME_AUDIO, (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => {
-        // TODO: add sound
-        var loadedSounds: Sound[] = [];
-        var loadedSound: Sound;
-        container.sounds = container.sounds || [];
-        if (parsedData.sounds !== undefined && parsedData.sounds !== null) {
-            for (let index = 0, cache = parsedData.sounds.length; index < cache; index++) {
-                var parsedSound = parsedData.sounds[index];
-                if (Engine.audioEngine.canUseWebAudio) {
-                    if (!parsedSound.url) { parsedSound.url = parsedSound.name; }
-                    if (!loadedSounds[parsedSound.url]) {
-                        loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
-                        loadedSounds[parsedSound.url] = loadedSound;
-                        container.sounds.push(loadedSound);
-                    }
-                    else {
-                        container.sounds.push(Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]));
-                    }
-                } else {
-                    container.sounds.push(new Sound(parsedSound.name, null, scene));
+// Adds the parser to the scene parsers.
+AbstractScene.AddParser(SceneComponentConstants.NAME_AUDIO, (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => {
+    // TODO: add sound
+    var loadedSounds: Sound[] = [];
+    var loadedSound: Sound;
+    container.sounds = container.sounds || [];
+    if (parsedData.sounds !== undefined && parsedData.sounds !== null) {
+        for (let index = 0, cache = parsedData.sounds.length; index < cache; index++) {
+            var parsedSound = parsedData.sounds[index];
+            if (Engine.audioEngine.canUseWebAudio) {
+                if (!parsedSound.url) { parsedSound.url = parsedSound.name; }
+                if (!loadedSounds[parsedSound.url]) {
+                    loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
+                    loadedSounds[parsedSound.url] = loadedSound;
+                    container.sounds.push(loadedSound);
+                }
+                else {
+                    container.sounds.push(Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]));
                 }
+            } else {
+                container.sounds.push(new Sound(parsedSound.name, null, scene));
             }
         }
+    }
 
-        loadedSounds = [];
-    });
+    loadedSounds = [];
+});
 
 declare module "../abstractScene" {
     export interface AbstractScene {
@@ -85,353 +85,353 @@ declare module "../scene" {
     }
 }
 
-    Object.defineProperty(Scene.prototype, "mainSoundTrack", {
-        get: function(this: Scene) {
-            let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
-            if (!compo) {
-                compo = new AudioSceneComponent(this);
-                this._addComponent(compo);
-            }
+Object.defineProperty(Scene.prototype, "mainSoundTrack", {
+    get: function(this: Scene) {
+        let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
+        if (!compo) {
+            compo = new AudioSceneComponent(this);
+            this._addComponent(compo);
+        }
 
-            if (!this._mainSoundTrack) {
-                this._mainSoundTrack = new SoundTrack(this, { mainTrack: true });
-            }
+        if (!this._mainSoundTrack) {
+            this._mainSoundTrack = new SoundTrack(this, { mainTrack: true });
+        }
 
-            return this._mainSoundTrack;
-        },
-        enumerable: true,
-        configurable: true
-    });
-
-    Scene.prototype.getSoundByName = function(name: string): Nullable<Sound> {
-        var index: number;
-        for (index = 0; index < this.mainSoundTrack.soundCollection.length; index++) {
-            if (this.mainSoundTrack.soundCollection[index].name === name) {
-                return this.mainSoundTrack.soundCollection[index];
-            }
+        return this._mainSoundTrack;
+    },
+    enumerable: true,
+    configurable: true
+});
+
+Scene.prototype.getSoundByName = function(name: string): Nullable<Sound> {
+    var index: number;
+    for (index = 0; index < this.mainSoundTrack.soundCollection.length; index++) {
+        if (this.mainSoundTrack.soundCollection[index].name === name) {
+            return this.mainSoundTrack.soundCollection[index];
         }
+    }
 
-        if (this.soundTracks) {
-            for (var sdIndex = 0; sdIndex < this.soundTracks.length; sdIndex++) {
-                for (index = 0; index < this.soundTracks[sdIndex].soundCollection.length; index++) {
-                    if (this.soundTracks[sdIndex].soundCollection[index].name === name) {
-                        return this.soundTracks[sdIndex].soundCollection[index];
-                    }
+    if (this.soundTracks) {
+        for (var sdIndex = 0; sdIndex < this.soundTracks.length; sdIndex++) {
+            for (index = 0; index < this.soundTracks[sdIndex].soundCollection.length; index++) {
+                if (this.soundTracks[sdIndex].soundCollection[index].name === name) {
+                    return this.soundTracks[sdIndex].soundCollection[index];
                 }
             }
         }
+    }
 
-        return null;
-    };
-
-    Object.defineProperty(Scene.prototype, "audioEnabled", {
-        get: function(this: Scene) {
-            let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
-            if (!compo) {
-                compo = new AudioSceneComponent(this);
-                this._addComponent(compo);
-            }
+    return null;
+};
 
-            return compo.audioEnabled;
-        },
-        set: function(this: Scene, value: boolean) {
-            let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
-            if (!compo) {
-                compo = new AudioSceneComponent(this);
-                this._addComponent(compo);
-            }
+Object.defineProperty(Scene.prototype, "audioEnabled", {
+    get: function(this: Scene) {
+        let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
+        if (!compo) {
+            compo = new AudioSceneComponent(this);
+            this._addComponent(compo);
+        }
 
-            if (value) {
-                compo.enableAudio();
-            }
-            else {
-                compo.disableAudio();
-            }
-        },
-        enumerable: true,
-        configurable: true
-    });
-
-    Object.defineProperty(Scene.prototype, "headphone", {
-        get: function(this: Scene) {
-            let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
-            if (!compo) {
-                compo = new AudioSceneComponent(this);
-                this._addComponent(compo);
-            }
+        return compo.audioEnabled;
+    },
+    set: function(this: Scene, value: boolean) {
+        let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
+        if (!compo) {
+            compo = new AudioSceneComponent(this);
+            this._addComponent(compo);
+        }
 
-            return compo.headphone;
-        },
-        set: function(this: Scene, value: boolean) {
-            let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
-            if (!compo) {
-                compo = new AudioSceneComponent(this);
-                this._addComponent(compo);
-            }
+        if (value) {
+            compo.enableAudio();
+        }
+        else {
+            compo.disableAudio();
+        }
+    },
+    enumerable: true,
+    configurable: true
+});
+
+Object.defineProperty(Scene.prototype, "headphone", {
+    get: function(this: Scene) {
+        let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
+        if (!compo) {
+            compo = new AudioSceneComponent(this);
+            this._addComponent(compo);
+        }
 
-            if (value) {
-                compo.switchAudioModeForHeadphones();
-            }
-            else {
-                compo.switchAudioModeForNormalSpeakers();
-            }
-        },
-        enumerable: true,
-        configurable: true
-    });
+        return compo.headphone;
+    },
+    set: function(this: Scene, value: boolean) {
+        let compo = this._getComponent(SceneComponentConstants.NAME_AUDIO) as AudioSceneComponent;
+        if (!compo) {
+            compo = new AudioSceneComponent(this);
+            this._addComponent(compo);
+        }
 
+        if (value) {
+            compo.switchAudioModeForHeadphones();
+        }
+        else {
+            compo.switchAudioModeForNormalSpeakers();
+        }
+    },
+    enumerable: true,
+    configurable: true
+});
+
+/**
+ * Defines the sound scene component responsible to manage any sounds
+ * in a given scene.
+ */
+export class AudioSceneComponent implements ISceneSerializableComponent {
     /**
-     * Defines the sound scene component responsible to manage any sounds
-     * in a given scene.
+     * The component name helpfull to identify the component in the list of scene components.
      */
-    export class AudioSceneComponent implements ISceneSerializableComponent {
-        /**
-         * The component name helpfull to identify the component in the list of scene components.
-         */
-        public readonly name = SceneComponentConstants.NAME_AUDIO;
+    public readonly name = SceneComponentConstants.NAME_AUDIO;
 
-        /**
-         * The scene the component belongs to.
-         */
-        public scene: Scene;
+    /**
+     * The scene the component belongs to.
+     */
+    public scene: Scene;
 
-        private _audioEnabled = true;
-        /**
-         * Gets whether audio is enabled or not.
-         * Please use related enable/disable method to switch state.
-         */
-        public get audioEnabled(): boolean {
-            return this._audioEnabled;
-        }
+    private _audioEnabled = true;
+    /**
+     * Gets whether audio is enabled or not.
+     * Please use related enable/disable method to switch state.
+     */
+    public get audioEnabled(): boolean {
+        return this._audioEnabled;
+    }
 
-        private _headphone = false;
-        /**
-         * Gets whether audio is outputing to headphone or not.
-         * Please use the according Switch methods to change output.
-         */
-        public get headphone(): boolean {
-            return this._headphone;
-        }
+    private _headphone = false;
+    /**
+     * Gets whether audio is outputing to headphone or not.
+     * Please use the according Switch methods to change output.
+     */
+    public get headphone(): boolean {
+        return this._headphone;
+    }
 
-        /**
-         * Creates a new instance of the component for the given scene
-         * @param scene Defines the scene to register the component in
-         */
-        constructor(scene: Scene) {
-            this.scene = scene;
+    /**
+     * Creates a new instance of the component for the given scene
+     * @param scene Defines the scene to register the component in
+     */
+    constructor(scene: Scene) {
+        this.scene = scene;
 
-            scene.soundTracks = new Array<SoundTrack>();
-            scene.sounds = new Array<Sound>();
-        }
+        scene.soundTracks = new Array<SoundTrack>();
+        scene.sounds = new Array<Sound>();
+    }
 
-        /**
-         * Registers the component in a given scene
-         */
-        public register(): void {
-            this.scene._afterRenderStage.registerStep(SceneComponentConstants.STEP_AFTERRENDER_AUDIO, this, this._afterRender);
-        }
+    /**
+     * Registers the component in a given scene
+     */
+    public register(): void {
+        this.scene._afterRenderStage.registerStep(SceneComponentConstants.STEP_AFTERRENDER_AUDIO, this, this._afterRender);
+    }
 
-        /**
-         * Rebuilds the elements related to this component in case of
-         * context lost for instance.
-         */
-        public rebuild(): void {
-            // Nothing to do here. (Not rendering related)
-        }
+    /**
+     * Rebuilds the elements related to this component in case of
+     * context lost for instance.
+     */
+    public rebuild(): void {
+        // Nothing to do here. (Not rendering related)
+    }
 
-        /**
-         * Serializes the component data to the specified json object
-         * @param serializationObject The object to serialize to
-         */
-        public serialize(serializationObject: any): void {
-            serializationObject.sounds = [];
+    /**
+     * Serializes the component data to the specified json object
+     * @param serializationObject The object to serialize to
+     */
+    public serialize(serializationObject: any): void {
+        serializationObject.sounds = [];
 
-            if (this.scene.soundTracks) {
-                for (var index = 0; index < this.scene.soundTracks.length; index++) {
-                    var soundtrack = this.scene.soundTracks[index];
+        if (this.scene.soundTracks) {
+            for (var index = 0; index < this.scene.soundTracks.length; index++) {
+                var soundtrack = this.scene.soundTracks[index];
 
-                    for (var soundId = 0; soundId < soundtrack.soundCollection.length; soundId++) {
-                        serializationObject.sounds.push(soundtrack.soundCollection[soundId].serialize());
-                    }
+                for (var soundId = 0; soundId < soundtrack.soundCollection.length; soundId++) {
+                    serializationObject.sounds.push(soundtrack.soundCollection[soundId].serialize());
                 }
             }
         }
+    }
 
-        /**
-         * Adds all the element from the container to the scene
-         * @param container the container holding the elements
-         */
-        public addFromContainer(container: AbstractScene): void {
-            if (!container.sounds) {
-                return;
-            }
-            container.sounds.forEach((sound) => {
-                sound.play();
-                sound.autoplay = true;
-                this.scene.mainSoundTrack.AddSound(sound);
-            });
+    /**
+     * Adds all the element from the container to the scene
+     * @param container the container holding the elements
+     */
+    public addFromContainer(container: AbstractScene): void {
+        if (!container.sounds) {
+            return;
         }
+        container.sounds.forEach((sound) => {
+            sound.play();
+            sound.autoplay = true;
+            this.scene.mainSoundTrack.AddSound(sound);
+        });
+    }
 
-        /**
-         * Removes all the elements in the container from the scene
-         * @param container contains the elements to remove
-         */
-        public removeFromContainer(container: AbstractScene): void {
-            if (!container.sounds) {
-                return;
-            }
-            container.sounds.forEach((sound) => {
-                sound.stop();
-                sound.autoplay = false;
-                this.scene.mainSoundTrack.RemoveSound(sound);
-            });
+    /**
+     * Removes all the elements in the container from the scene
+     * @param container contains the elements to remove
+     */
+    public removeFromContainer(container: AbstractScene): void {
+        if (!container.sounds) {
+            return;
         }
+        container.sounds.forEach((sound) => {
+            sound.stop();
+            sound.autoplay = false;
+            this.scene.mainSoundTrack.RemoveSound(sound);
+        });
+    }
 
-        /**
-         * Disposes the component and the associated ressources.
-         */
-        public dispose(): void {
-            const scene = this.scene;
-            if (scene._mainSoundTrack) {
-                scene.mainSoundTrack.dispose();
-            }
+    /**
+     * Disposes the component and the associated ressources.
+     */
+    public dispose(): void {
+        const scene = this.scene;
+        if (scene._mainSoundTrack) {
+            scene.mainSoundTrack.dispose();
+        }
 
-            if (scene.soundTracks) {
-                for (var scIndex = 0; scIndex < scene.soundTracks.length; scIndex++) {
-                    scene.soundTracks[scIndex].dispose();
-                }
+        if (scene.soundTracks) {
+            for (var scIndex = 0; scIndex < scene.soundTracks.length; scIndex++) {
+                scene.soundTracks[scIndex].dispose();
             }
         }
+    }
 
-        /**
-         * Disables audio in the associated scene.
-         */
-        public disableAudio() {
-            const scene = this.scene;
-            this._audioEnabled = false;
+    /**
+     * Disables audio in the associated scene.
+     */
+    public disableAudio() {
+        const scene = this.scene;
+        this._audioEnabled = false;
 
-            let i: number;
-            for (i = 0; i < scene.mainSoundTrack.soundCollection.length; i++) {
-                scene.mainSoundTrack.soundCollection[i].pause();
-            }
-            if (scene.soundTracks) {
-                for (i = 0; i < scene.soundTracks.length; i++) {
-                    for (var j = 0; j < scene.soundTracks[i].soundCollection.length; j++) {
-                        scene.soundTracks[i].soundCollection[j].pause();
-                    }
+        let i: number;
+        for (i = 0; i < scene.mainSoundTrack.soundCollection.length; i++) {
+            scene.mainSoundTrack.soundCollection[i].pause();
+        }
+        if (scene.soundTracks) {
+            for (i = 0; i < scene.soundTracks.length; i++) {
+                for (var j = 0; j < scene.soundTracks[i].soundCollection.length; j++) {
+                    scene.soundTracks[i].soundCollection[j].pause();
                 }
             }
         }
+    }
 
-        /**
-         * Enables audio in the associated scene.
-         */
-        public enableAudio() {
-            const scene = this.scene;
-            this._audioEnabled = true;
-
-            let i: number;
-            for (i = 0; i < scene.mainSoundTrack.soundCollection.length; i++) {
-                if (scene.mainSoundTrack.soundCollection[i].isPaused) {
-                    scene.mainSoundTrack.soundCollection[i].play();
-                }
+    /**
+     * Enables audio in the associated scene.
+     */
+    public enableAudio() {
+        const scene = this.scene;
+        this._audioEnabled = true;
+
+        let i: number;
+        for (i = 0; i < scene.mainSoundTrack.soundCollection.length; i++) {
+            if (scene.mainSoundTrack.soundCollection[i].isPaused) {
+                scene.mainSoundTrack.soundCollection[i].play();
             }
-            if (scene.soundTracks) {
-                for (i = 0; i < scene.soundTracks.length; i++) {
-                    for (var j = 0; j < scene.soundTracks[i].soundCollection.length; j++) {
-                        if (scene.soundTracks[i].soundCollection[j].isPaused) {
-                            scene.soundTracks[i].soundCollection[j].play();
-                        }
+        }
+        if (scene.soundTracks) {
+            for (i = 0; i < scene.soundTracks.length; i++) {
+                for (var j = 0; j < scene.soundTracks[i].soundCollection.length; j++) {
+                    if (scene.soundTracks[i].soundCollection[j].isPaused) {
+                        scene.soundTracks[i].soundCollection[j].play();
                     }
                 }
             }
         }
+    }
 
-        /**
-         * Switch audio to headphone output.
-         */
-        public switchAudioModeForHeadphones() {
-            const scene = this.scene;
-            this._headphone = true;
-
-            scene.mainSoundTrack.switchPanningModelToHRTF();
-            if (scene.soundTracks) {
-                for (var i = 0; i < scene.soundTracks.length; i++) {
-                    scene.soundTracks[i].switchPanningModelToHRTF();
-                }
+    /**
+     * Switch audio to headphone output.
+     */
+    public switchAudioModeForHeadphones() {
+        const scene = this.scene;
+        this._headphone = true;
+
+        scene.mainSoundTrack.switchPanningModelToHRTF();
+        if (scene.soundTracks) {
+            for (var i = 0; i < scene.soundTracks.length; i++) {
+                scene.soundTracks[i].switchPanningModelToHRTF();
             }
         }
+    }
 
-        /**
-         * Switch audio to normal speakers.
-         */
-        public switchAudioModeForNormalSpeakers() {
-            const scene = this.scene;
-            this._headphone = false;
+    /**
+     * Switch audio to normal speakers.
+     */
+    public switchAudioModeForNormalSpeakers() {
+        const scene = this.scene;
+        this._headphone = false;
 
-            scene.mainSoundTrack.switchPanningModelToEqualPower();
+        scene.mainSoundTrack.switchPanningModelToEqualPower();
 
-            if (scene.soundTracks) {
-                for (var i = 0; i < scene.soundTracks.length; i++) {
-                    scene.soundTracks[i].switchPanningModelToEqualPower();
-                }
+        if (scene.soundTracks) {
+            for (var i = 0; i < scene.soundTracks.length; i++) {
+                scene.soundTracks[i].switchPanningModelToEqualPower();
             }
         }
+    }
 
-        private _afterRender() {
-            const scene = this.scene;
-            if (!this._audioEnabled || !scene._mainSoundTrack || !scene.soundTracks || (scene._mainSoundTrack.soundCollection.length === 0 && scene.soundTracks.length === 1)) {
-                return;
-            }
+    private _afterRender() {
+        const scene = this.scene;
+        if (!this._audioEnabled || !scene._mainSoundTrack || !scene.soundTracks || (scene._mainSoundTrack.soundCollection.length === 0 && scene.soundTracks.length === 1)) {
+            return;
+        }
 
-            var listeningCamera: Nullable<Camera>;
-            var audioEngine = Engine.audioEngine;
+        var listeningCamera: Nullable<Camera>;
+        var audioEngine = Engine.audioEngine;
 
-            if (scene.activeCameras.length > 0) {
-                listeningCamera = scene.activeCameras[0];
-            } else {
-                listeningCamera = scene.activeCamera;
-            }
+        if (scene.activeCameras.length > 0) {
+            listeningCamera = scene.activeCameras[0];
+        } else {
+            listeningCamera = scene.activeCamera;
+        }
 
-            if (listeningCamera && audioEngine.audioContext) {
-                audioEngine.audioContext.listener.setPosition(listeningCamera.position.x, listeningCamera.position.y, listeningCamera.position.z);
-                // for VR cameras
-                if (listeningCamera.rigCameras && listeningCamera.rigCameras.length > 0) {
-                    listeningCamera = listeningCamera.rigCameras[0];
-                }
-                var mat = Matrix.Invert(listeningCamera.getViewMatrix());
-                var cameraDirection = Vector3.TransformNormal(new Vector3(0, 0, -1), mat);
-                cameraDirection.normalize();
-                // To avoid some errors on GearVR
-                if (!isNaN(cameraDirection.x) && !isNaN(cameraDirection.y) && !isNaN(cameraDirection.z)) {
-                    audioEngine.audioContext.listener.setOrientation(cameraDirection.x, cameraDirection.y, cameraDirection.z, 0, 1, 0);
-                }
+        if (listeningCamera && audioEngine.audioContext) {
+            audioEngine.audioContext.listener.setPosition(listeningCamera.position.x, listeningCamera.position.y, listeningCamera.position.z);
+            // for VR cameras
+            if (listeningCamera.rigCameras && listeningCamera.rigCameras.length > 0) {
+                listeningCamera = listeningCamera.rigCameras[0];
+            }
+            var mat = Matrix.Invert(listeningCamera.getViewMatrix());
+            var cameraDirection = Vector3.TransformNormal(new Vector3(0, 0, -1), mat);
+            cameraDirection.normalize();
+            // To avoid some errors on GearVR
+            if (!isNaN(cameraDirection.x) && !isNaN(cameraDirection.y) && !isNaN(cameraDirection.z)) {
+                audioEngine.audioContext.listener.setOrientation(cameraDirection.x, cameraDirection.y, cameraDirection.z, 0, 1, 0);
+            }
 
-                var i: number;
-                for (i = 0; i < scene.mainSoundTrack.soundCollection.length; i++) {
-                    var sound = scene.mainSoundTrack.soundCollection[i];
-                    if (sound.useCustomAttenuation) {
-                        sound.updateDistanceFromListener();
-                    }
+            var i: number;
+            for (i = 0; i < scene.mainSoundTrack.soundCollection.length; i++) {
+                var sound = scene.mainSoundTrack.soundCollection[i];
+                if (sound.useCustomAttenuation) {
+                    sound.updateDistanceFromListener();
                 }
-                if (scene.soundTracks) {
-                    for (i = 0; i < scene.soundTracks.length; i++) {
-                        for (var j = 0; j < scene.soundTracks[i].soundCollection.length; j++) {
-                            sound = scene.soundTracks[i].soundCollection[j];
-                            if (sound.useCustomAttenuation) {
-                                sound.updateDistanceFromListener();
-                            }
+            }
+            if (scene.soundTracks) {
+                for (i = 0; i < scene.soundTracks.length; i++) {
+                    for (var j = 0; j < scene.soundTracks[i].soundCollection.length; j++) {
+                        sound = scene.soundTracks[i].soundCollection[j];
+                        if (sound.useCustomAttenuation) {
+                            sound.updateDistanceFromListener();
                         }
                     }
                 }
             }
         }
     }
+}
 
-    Sound._SceneComponentInitialization = (scene: Scene) => {
-        let compo = scene._getComponent(SceneComponentConstants.NAME_AUDIO);
-        if (!compo) {
-            compo = new AudioSceneComponent(scene);
-            scene._addComponent(compo);
-        }
-    };
+Sound._SceneComponentInitialization = (scene: Scene) => {
+    let compo = scene._getComponent(SceneComponentConstants.NAME_AUDIO);
+    if (!compo) {
+        compo = new AudioSceneComponent(scene);
+        scene._addComponent(compo);
+    }
+};

文件差異過大導致無法顯示
+ 869 - 870
src/Audio/sound.ts


+ 144 - 144
src/Audio/soundTrack.ts

@@ -4,178 +4,178 @@ import { Nullable } from "../types";
 import { Scene } from "../scene";
 import { Engine } from "../Engines/engine";
 
+/**
+ * Options allowed during the creation of a sound track.
+ */
+export interface ISoundTrackOptions {
     /**
-     * Options allowed during the creation of a sound track.
+     * The volume the sound track should take during creation
      */
-    export interface ISoundTrackOptions {
-        /**
-         * The volume the sound track should take during creation
-         */
-        volume?: number;
-        /**
-         * Define if the sound track is the main sound track of the scene
-         */
-        mainTrack?: boolean;
-    }
+    volume?: number;
+    /**
+     * Define if the sound track is the main sound track of the scene
+     */
+    mainTrack?: boolean;
+}
+
+/**
+ * It could be useful to isolate your music & sounds on several tracks to better manage volume on a grouped instance of sounds.
+ * It will be also used in a future release to apply effects on a specific track.
+ * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-sound-tracks
+ */
+export class SoundTrack {
+    /**
+     * The unique identifier of the sound track in the scene.
+     */
+    public id: number = -1;
+    /**
+     * The list of sounds included in the sound track.
+     */
+    public soundCollection: Array<Sound>;
+
+    private _outputAudioNode: Nullable<GainNode>;
+    private _scene: Scene;
+    private _isMainTrack: boolean = false;
+    private _connectedAnalyser: Analyser;
+    private _options: ISoundTrackOptions;
+    private _isInitialized = false;
 
     /**
-     * It could be useful to isolate your music & sounds on several tracks to better manage volume on a grouped instance of sounds.
-     * It will be also used in a future release to apply effects on a specific track.
+     * Creates a new sound track.
      * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-sound-tracks
+     * @param scene Define the scene the sound track belongs to
+     * @param options
      */
-    export class SoundTrack {
-        /**
-         * The unique identifier of the sound track in the scene.
-         */
-        public id: number = -1;
-        /**
-         * The list of sounds included in the sound track.
-         */
-        public soundCollection: Array<Sound>;
-
-        private _outputAudioNode: Nullable<GainNode>;
-        private _scene: Scene;
-        private _isMainTrack: boolean = false;
-        private _connectedAnalyser: Analyser;
-        private _options: ISoundTrackOptions;
-        private _isInitialized = false;
-
-        /**
-         * Creates a new sound track.
-         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-sound-tracks
-         * @param scene Define the scene the sound track belongs to
-         * @param options
-         */
-        constructor(scene: Scene, options: ISoundTrackOptions = { }) {
-            this._scene = scene;
-            this.soundCollection = new Array();
-            this._options = options;
-
-            if (!this._isMainTrack && this._scene.soundTracks) {
-                this._scene.soundTracks.push(this);
-                this.id = this._scene.soundTracks.length - 1;
-            }
+    constructor(scene: Scene, options: ISoundTrackOptions = {}) {
+        this._scene = scene;
+        this.soundCollection = new Array();
+        this._options = options;
+
+        if (!this._isMainTrack && this._scene.soundTracks) {
+            this._scene.soundTracks.push(this);
+            this.id = this._scene.soundTracks.length - 1;
         }
+    }
 
-        private _initializeSoundTrackAudioGraph() {
-            if (Engine.audioEngine.canUseWebAudio && Engine.audioEngine.audioContext) {
-                this._outputAudioNode = Engine.audioEngine.audioContext.createGain();
-                this._outputAudioNode.connect(Engine.audioEngine.masterGain);
-
-                if (this._options) {
-                    if (this._options.volume) { this._outputAudioNode.gain.value = this._options.volume; }
-                    if (this._options.mainTrack) { this._isMainTrack = this._options.mainTrack; }
-                }
+    private _initializeSoundTrackAudioGraph() {
+        if (Engine.audioEngine.canUseWebAudio && Engine.audioEngine.audioContext) {
+            this._outputAudioNode = Engine.audioEngine.audioContext.createGain();
+            this._outputAudioNode.connect(Engine.audioEngine.masterGain);
 
-                this._isInitialized = true;
+            if (this._options) {
+                if (this._options.volume) { this._outputAudioNode.gain.value = this._options.volume; }
+                if (this._options.mainTrack) { this._isMainTrack = this._options.mainTrack; }
             }
-        }
 
-        /**
-         * Release the sound track and its associated resources
-         */
-        public dispose(): void {
-            if (Engine.audioEngine && Engine.audioEngine.canUseWebAudio) {
-                if (this._connectedAnalyser) {
-                    this._connectedAnalyser.stopDebugCanvas();
-                }
-                while (this.soundCollection.length) {
-                    this.soundCollection[0].dispose();
-                }
-                if (this._outputAudioNode) {
-                    this._outputAudioNode.disconnect();
-                }
-                this._outputAudioNode = null;
-            }
+            this._isInitialized = true;
         }
+    }
 
-        /**
-         * Adds a sound to this sound track
-         * @param sound define the cound to add
-         * @ignoreNaming
-         */
-        public AddSound(sound: Sound): void {
-            if (!this._isInitialized) {
-                this._initializeSoundTrackAudioGraph();
+    /**
+     * Release the sound track and its associated resources
+     */
+    public dispose(): void {
+        if (Engine.audioEngine && Engine.audioEngine.canUseWebAudio) {
+            if (this._connectedAnalyser) {
+                this._connectedAnalyser.stopDebugCanvas();
             }
-            if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
-                sound.connectToSoundTrackAudioNode(this._outputAudioNode);
+            while (this.soundCollection.length) {
+                this.soundCollection[0].dispose();
             }
-            if (sound.soundTrackId) {
-                if (sound.soundTrackId === -1) {
-                    this._scene.mainSoundTrack.RemoveSound(sound);
-                }
-                else if (this._scene.soundTracks) {
-                    this._scene.soundTracks[sound.soundTrackId].RemoveSound(sound);
-                }
+            if (this._outputAudioNode) {
+                this._outputAudioNode.disconnect();
             }
-
-            this.soundCollection.push(sound);
-            sound.soundTrackId = this.id;
+            this._outputAudioNode = null;
         }
+    }
 
-        /**
-         * Removes a sound to this sound track
-         * @param sound define the cound to remove
-         * @ignoreNaming
-         */
-        public RemoveSound(sound: Sound): void {
-            var index = this.soundCollection.indexOf(sound);
-            if (index !== -1) {
-                this.soundCollection.splice(index, 1);
+    /**
+     * Adds a sound to this sound track
+     * @param sound define the cound to add
+     * @ignoreNaming
+     */
+    public AddSound(sound: Sound): void {
+        if (!this._isInitialized) {
+            this._initializeSoundTrackAudioGraph();
+        }
+        if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
+            sound.connectToSoundTrackAudioNode(this._outputAudioNode);
+        }
+        if (sound.soundTrackId) {
+            if (sound.soundTrackId === -1) {
+                this._scene.mainSoundTrack.RemoveSound(sound);
+            }
+            else if (this._scene.soundTracks) {
+                this._scene.soundTracks[sound.soundTrackId].RemoveSound(sound);
             }
         }
 
-        /**
-         * Set a global volume for the full sound track.
-         * @param newVolume Define the new volume of the sound track
-         */
-        public setVolume(newVolume: number): void {
-            if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
-                this._outputAudioNode.gain.value = newVolume;
-            }
+        this.soundCollection.push(sound);
+        sound.soundTrackId = this.id;
+    }
+
+    /**
+     * Removes a sound to this sound track
+     * @param sound define the cound to remove
+     * @ignoreNaming
+     */
+    public RemoveSound(sound: Sound): void {
+        var index = this.soundCollection.indexOf(sound);
+        if (index !== -1) {
+            this.soundCollection.splice(index, 1);
         }
+    }
 
-        /**
-         * Switch the panning model to HRTF:
-         * Renders a stereo output of higher quality than equalpower — it uses a convolution with measured impulse responses from human subjects.
-         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-         */
-        public switchPanningModelToHRTF(): void {
-            if (Engine.audioEngine.canUseWebAudio) {
-                for (var i = 0; i < this.soundCollection.length; i++) {
-                    this.soundCollection[i].switchPanningModelToHRTF();
-                }
-            }
+    /**
+     * Set a global volume for the full sound track.
+     * @param newVolume Define the new volume of the sound track
+     */
+    public setVolume(newVolume: number): void {
+        if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
+            this._outputAudioNode.gain.value = newVolume;
         }
+    }
 
-        /**
-         * Switch the panning model to Equal Power:
-         * Represents the equal-power panning algorithm, generally regarded as simple and efficient. equalpower is the default value.
-         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-         */
-        public switchPanningModelToEqualPower(): void {
-            if (Engine.audioEngine.canUseWebAudio) {
-                for (var i = 0; i < this.soundCollection.length; i++) {
-                    this.soundCollection[i].switchPanningModelToEqualPower();
-                }
+    /**
+     * Switch the panning model to HRTF:
+     * Renders a stereo output of higher quality than equalpower — it uses a convolution with measured impulse responses from human subjects.
+     * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+     */
+    public switchPanningModelToHRTF(): void {
+        if (Engine.audioEngine.canUseWebAudio) {
+            for (var i = 0; i < this.soundCollection.length; i++) {
+                this.soundCollection[i].switchPanningModelToHRTF();
             }
         }
+    }
 
-        /**
-         * Connect the sound track to an audio analyser allowing some amazing
-         * synchornization between the sounds/music and your visualization (VuMeter for instance).
-         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
-         * @param analyser The analyser to connect to the engine
-         */
-        public connectToAnalyser(analyser: Analyser): void {
-            if (this._connectedAnalyser) {
-                this._connectedAnalyser.stopDebugCanvas();
-            }
-            this._connectedAnalyser = analyser;
-            if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
-                this._outputAudioNode.disconnect();
-                this._connectedAnalyser.connectAudioNodes(this._outputAudioNode, Engine.audioEngine.masterGain);
+    /**
+     * Switch the panning model to Equal Power:
+     * Represents the equal-power panning algorithm, generally regarded as simple and efficient. equalpower is the default value.
+     * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+     */
+    public switchPanningModelToEqualPower(): void {
+        if (Engine.audioEngine.canUseWebAudio) {
+            for (var i = 0; i < this.soundCollection.length; i++) {
+                this.soundCollection[i].switchPanningModelToEqualPower();
             }
         }
     }
+
+    /**
+     * Connect the sound track to an audio analyser allowing some amazing
+     * synchornization between the sounds/music and your visualization (VuMeter for instance).
+     * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+     * @param analyser The analyser to connect to the engine
+     */
+    public connectToAnalyser(analyser: Analyser): void {
+        if (this._connectedAnalyser) {
+            this._connectedAnalyser.stopDebugCanvas();
+        }
+        this._connectedAnalyser = analyser;
+        if (Engine.audioEngine.canUseWebAudio && this._outputAudioNode) {
+            this._outputAudioNode.disconnect();
+            this._connectedAnalyser.connectAudioNodes(this._outputAudioNode, Engine.audioEngine.masterGain);
+        }
+    }
+}

+ 143 - 143
src/Audio/weightedsound.ts

@@ -1,175 +1,175 @@
 import { Sound } from "../Audio/sound";
 import { Logger } from "../Misc/logger";
 
+/**
+ * Wraps one or more Sound objects and selects one with random weight for playback.
+ */
+export class WeightedSound {
+    /** When true a Sound will be selected and played when the current playing Sound completes. */
+    public loop: boolean = false;
+    private _coneInnerAngle: number = 360;
+    private _coneOuterAngle: number = 360;
+    private _volume: number = 1;
+    /** A Sound is currently playing. */
+    public isPlaying: boolean = false;
+    /** A Sound is currently paused. */
+    public isPaused: boolean = false;
+
+    private _sounds: Sound[] = [];
+    private _weights: number[] = [];
+    private _currentIndex?: number;
+
     /**
-     * Wraps one or more Sound objects and selects one with random weight for playback.
+     * Creates a new WeightedSound from the list of sounds given.
+     * @param loop When true a Sound will be selected and played when the current playing Sound completes.
+     * @param sounds Array of Sounds that will be selected from.
+     * @param weights Array of number values for selection weights; length must equal sounds, values will be normalized to 1
      */
-    export class WeightedSound {
-        /** When true a Sound will be selected and played when the current playing Sound completes. */
-        public loop: boolean = false;
-        private _coneInnerAngle: number = 360;
-        private _coneOuterAngle: number = 360;
-        private _volume: number = 1;
-        /** A Sound is currently playing. */
-        public isPlaying: boolean = false;
-        /** A Sound is currently paused. */
-        public isPaused: boolean = false;
-
-        private _sounds: Sound[] = [];
-        private _weights: number[] = [];
-        private _currentIndex?: number;
-
-        /**
-         * Creates a new WeightedSound from the list of sounds given.
-         * @param loop When true a Sound will be selected and played when the current playing Sound completes.
-         * @param sounds Array of Sounds that will be selected from.
-         * @param weights Array of number values for selection weights; length must equal sounds, values will be normalized to 1
-         */
-        constructor(loop: boolean, sounds: Sound[], weights: number[]) {
-            if (sounds.length !== weights.length) {
-                throw new Error('Sounds length does not equal weights length');
-            }
-
-            this.loop = loop;
-            this._weights = weights;
-            // Normalize the weights
-            let weightSum = 0;
-            for (const weight of weights) {
-                weightSum += weight;
-            }
-            const invWeightSum = weightSum > 0 ? 1 / weightSum : 0;
-            for (let i = 0; i < this._weights.length; i++) {
-                this._weights[i] *= invWeightSum;
-            }
-            this._sounds = sounds;
-            for (let sound of this._sounds) {
-                sound.onEndedObservable.add(() => { this._onended(); });
-            }
+    constructor(loop: boolean, sounds: Sound[], weights: number[]) {
+        if (sounds.length !== weights.length) {
+            throw new Error('Sounds length does not equal weights length');
         }
 
-        /**
-         * The size of cone in degrees for a directional sound in which there will be no attenuation.
-         */
-        public get directionalConeInnerAngle(): number {
-            return this._coneInnerAngle;
+        this.loop = loop;
+        this._weights = weights;
+        // Normalize the weights
+        let weightSum = 0;
+        for (const weight of weights) {
+            weightSum += weight;
         }
+        const invWeightSum = weightSum > 0 ? 1 / weightSum : 0;
+        for (let i = 0; i < this._weights.length; i++) {
+            this._weights[i] *= invWeightSum;
+        }
+        this._sounds = sounds;
+        for (let sound of this._sounds) {
+            sound.onEndedObservable.add(() => { this._onended(); });
+        }
+    }
 
-        /**
-         * The size of cone in degress for a directional sound in which there will be no attenuation.
-         */
-        public set directionalConeInnerAngle(value: number) {
-            if (value !== this._coneInnerAngle) {
-                if (this._coneOuterAngle < value) {
-                    Logger.Error("directionalConeInnerAngle: outer angle of the cone must be superior or equal to the inner angle.");
-                    return;
-                }
+    /**
+     * The size of cone in degrees for a directional sound in which there will be no attenuation.
+     */
+    public get directionalConeInnerAngle(): number {
+        return this._coneInnerAngle;
+    }
 
-                this._coneInnerAngle = value;
-                for (let sound of this._sounds) {
-                    sound.directionalConeInnerAngle = value;
-                }
+    /**
+     * The size of cone in degress for a directional sound in which there will be no attenuation.
+     */
+    public set directionalConeInnerAngle(value: number) {
+        if (value !== this._coneInnerAngle) {
+            if (this._coneOuterAngle < value) {
+                Logger.Error("directionalConeInnerAngle: outer angle of the cone must be superior or equal to the inner angle.");
+                return;
             }
-        }
 
-        /**
-         * Size of cone in degrees for a directional sound outside of which there will be no sound.
-         * Listener angles between innerAngle and outerAngle will falloff linearly.
-         */
-        public get directionalConeOuterAngle(): number {
-            return this._coneOuterAngle;
+            this._coneInnerAngle = value;
+            for (let sound of this._sounds) {
+                sound.directionalConeInnerAngle = value;
+            }
         }
+    }
 
-        /**
-         * Size of cone in degrees for a directional sound outside of which there will be no sound.
-         * Listener angles between innerAngle and outerAngle will falloff linearly.
-         */
-        public set directionalConeOuterAngle(value: number) {
-            if (value !== this._coneOuterAngle) {
-                if (value < this._coneInnerAngle) {
-                    Logger.Error("directionalConeOuterAngle: outer angle of the cone must be superior or equal to the inner angle.");
-                    return;
-                }
+    /**
+     * Size of cone in degrees for a directional sound outside of which there will be no sound.
+     * Listener angles between innerAngle and outerAngle will falloff linearly.
+     */
+    public get directionalConeOuterAngle(): number {
+        return this._coneOuterAngle;
+    }
 
-                this._coneOuterAngle = value;
-                for (let sound of this._sounds) {
-                    sound.directionalConeOuterAngle = value;
-                }
+    /**
+     * Size of cone in degrees for a directional sound outside of which there will be no sound.
+     * Listener angles between innerAngle and outerAngle will falloff linearly.
+     */
+    public set directionalConeOuterAngle(value: number) {
+        if (value !== this._coneOuterAngle) {
+            if (value < this._coneInnerAngle) {
+                Logger.Error("directionalConeOuterAngle: outer angle of the cone must be superior or equal to the inner angle.");
+                return;
             }
-        }
 
-        /**
-         * Playback volume.
-         */
-        public get volume(): number {
-            return this._volume;
+            this._coneOuterAngle = value;
+            for (let sound of this._sounds) {
+                sound.directionalConeOuterAngle = value;
+            }
         }
+    }
 
-        /**
-         * Playback volume.
-         */
-        public set volume(value: number) {
-            if (value !== this._volume) {
-                for (let sound of this._sounds) {
-                    sound.setVolume(value);
-                }
+    /**
+     * Playback volume.
+     */
+    public get volume(): number {
+        return this._volume;
+    }
+
+    /**
+     * Playback volume.
+     */
+    public set volume(value: number) {
+        if (value !== this._volume) {
+            for (let sound of this._sounds) {
+                sound.setVolume(value);
             }
         }
+    }
 
-        private _onended() {
-            if (this._currentIndex !== undefined) {
-                this._sounds[this._currentIndex].autoplay = false;
-            }
-            if (this.loop && this.isPlaying) {
-                this.play();
-            } else {
-                this.isPlaying = false;
-            }
+    private _onended() {
+        if (this._currentIndex !== undefined) {
+            this._sounds[this._currentIndex].autoplay = false;
+        }
+        if (this.loop && this.isPlaying) {
+            this.play();
+        } else {
+            this.isPlaying = false;
         }
+    }
 
-        /**
-         * Suspend playback
-         */
-        public pause() {
-            this.isPaused = true;
-            if (this._currentIndex !== undefined) {
-                this._sounds[this._currentIndex].pause();
-            }
+    /**
+     * Suspend playback
+     */
+    public pause() {
+        this.isPaused = true;
+        if (this._currentIndex !== undefined) {
+            this._sounds[this._currentIndex].pause();
         }
+    }
 
-        /**
-         * Stop playback
-         */
-        public stop() {
-            this.isPlaying = false;
-            if (this._currentIndex !== undefined) {
-                this._sounds[this._currentIndex].stop();
-            }
+    /**
+     * Stop playback
+     */
+    public stop() {
+        this.isPlaying = false;
+        if (this._currentIndex !== undefined) {
+            this._sounds[this._currentIndex].stop();
         }
+    }
 
-        /**
-         * Start playback.
-         * @param startOffset Position the clip head at a specific time in seconds.
-         */
-        public play(startOffset?: number) {
-            if (!this.isPaused) {
-                this.stop();
-                let randomValue = Math.random();
-                let total = 0;
-                for (let i = 0; i < this._weights.length; i++) {
-                    total += this._weights[i];
-                    if (randomValue <= total) {
-                        this._currentIndex = i;
-                        break;
-                    }
+    /**
+     * Start playback.
+     * @param startOffset Position the clip head at a specific time in seconds.
+     */
+    public play(startOffset?: number) {
+        if (!this.isPaused) {
+            this.stop();
+            let randomValue = Math.random();
+            let total = 0;
+            for (let i = 0; i < this._weights.length; i++) {
+                total += this._weights[i];
+                if (randomValue <= total) {
+                    this._currentIndex = i;
+                    break;
                 }
             }
-            const sound = this._sounds[this._currentIndex!];
-            if (sound.isReady()) {
-                sound.play(0, this.isPaused ? undefined : startOffset);
-            } else {
-                sound.autoplay = true;
-            }
-            this.isPlaying = true;
-            this.isPaused = false;
         }
+        const sound = this._sounds[this._currentIndex!];
+        if (sound.isReady()) {
+            sound.play(0, this.isPaused ? undefined : startOffset);
+        } else {
+            sound.autoplay = true;
+        }
+        this.isPlaying = true;
+        this.isPaused = false;
     }
+}

+ 171 - 171
src/Behaviors/Cameras/autoRotationBehavior.ts

@@ -6,207 +6,207 @@ import { Observer } from "../../Misc/observable";
 import { PointerInfoPre, PointerEventTypes } from "../../Events/pointerEvents";
 import { PrecisionDate } from "../../Misc/precisionDate";
 
+/**
+ * The autoRotation behavior (AutoRotationBehavior) is designed to create a smooth rotation of an ArcRotateCamera when there is no user interaction.
+ * @see http://doc.babylonjs.com/how_to/camera_behaviors#autorotation-behavior
+ */
+export class AutoRotationBehavior implements Behavior<ArcRotateCamera> {
     /**
-     * The autoRotation behavior (AutoRotationBehavior) is designed to create a smooth rotation of an ArcRotateCamera when there is no user interaction.
-     * @see http://doc.babylonjs.com/how_to/camera_behaviors#autorotation-behavior
+     * Gets the name of the behavior.
      */
-    export class AutoRotationBehavior implements Behavior<ArcRotateCamera> {
-        /**
-         * Gets the name of the behavior.
-         */
-        public get name(): string {
-            return "AutoRotation";
-        }
+    public get name(): string {
+        return "AutoRotation";
+    }
 
-        private _zoomStopsAnimation = false;
-        private _idleRotationSpeed = 0.05;
-        private _idleRotationWaitTime = 2000;
-        private _idleRotationSpinupTime = 2000;
+    private _zoomStopsAnimation = false;
+    private _idleRotationSpeed = 0.05;
+    private _idleRotationWaitTime = 2000;
+    private _idleRotationSpinupTime = 2000;
 
-        /**
-        * Sets the flag that indicates if user zooming should stop animation.
-        */
-        public set zoomStopsAnimation(flag: boolean) {
-            this._zoomStopsAnimation = flag;
-        }
+    /**
+    * Sets the flag that indicates if user zooming should stop animation.
+    */
+    public set zoomStopsAnimation(flag: boolean) {
+        this._zoomStopsAnimation = flag;
+    }
 
-        /**
-        * Gets the flag that indicates if user zooming should stop animation.
-        */
-        public get zoomStopsAnimation(): boolean {
-            return this._zoomStopsAnimation;
-        }
+    /**
+    * Gets the flag that indicates if user zooming should stop 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;
-        }
+    /**
+    * 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;
-        }
+    /**
+    * 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;
-        }
+    /**
+    * 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;
-        }
+    /**
+    * 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;
-        }
+    /**
+    * 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;
-        }
+    /**
+    * Gets the time (milliseconds) to take to spin up to the full idle rotation speed.
+    */
+    public get idleRotationSpinupTime() {
+        return this._idleRotationSpinupTime;
+    }
 
-        /**
-         * Gets a value indicating if the camera is currently rotating because of this behavior
-         */
-        public get rotationInProgress(): boolean {
-            return Math.abs(this._cameraRotationSpeed) > 0;
-        }
+    /**
+     * Gets a value indicating if the camera is currently rotating because of this behavior
+     */
+    public get rotationInProgress(): boolean {
+        return Math.abs(this._cameraRotationSpeed) > 0;
+    }
 
-        // Default behavior functions
-        private _onPrePointerObservableObserver: Nullable<Observer<PointerInfoPre>>;
-        private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
-        private _attachedCamera: Nullable<ArcRotateCamera>;
-        private _isPointerDown = false;
-        private _lastFrameTime: Nullable<number> = null;
-        private _lastInteractionTime = -Infinity;
-        private _cameraRotationSpeed: number = 0;
-
-        /**
-         * Initializes the behavior.
-         */
-        public init(): void {
-            // Do notihng
-        }
+    // Default behavior functions
+    private _onPrePointerObservableObserver: Nullable<Observer<PointerInfoPre>>;
+    private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
+    private _attachedCamera: Nullable<ArcRotateCamera>;
+    private _isPointerDown = false;
+    private _lastFrameTime: Nullable<number> = null;
+    private _lastInteractionTime = -Infinity;
+    private _cameraRotationSpeed: number = 0;
 
-        /**
-         * Attaches the behavior to its arc rotate camera.
-         * @param camera Defines the camera to attach the behavior to
-         */
-        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 = PrecisionDate.Now;
-                let dt = 0;
-                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);
-                this._cameraRotationSpeed = this._idleRotationSpeed * scale;
-
-                // Step camera rotation by rotation speed
-                if (this._attachedCamera) {
-                    this._attachedCamera.alpha -= this._cameraRotationSpeed * (dt / 1000);
-                }
-            });
-        }
+    /**
+     * Initializes the behavior.
+     */
+    public init(): void {
+        // Do notihng
+    }
+
+    /**
+     * Attaches the behavior to its arc rotate camera.
+     * @param camera Defines the camera to attach the behavior to
+     */
+    public attach(camera: ArcRotateCamera): void {
+        this._attachedCamera = camera;
+        let scene = this._attachedCamera.getScene();
 
-        /**
-         * Detaches the behavior from its current arc rotate camera.
-         */
-        public detach(): void {
-            if (!this._attachedCamera) {
+        this._onPrePointerObservableObserver = scene.onPrePointerObservable.add((pointerInfoPre) => {
+            if (pointerInfoPre.type === PointerEventTypes.POINTERDOWN) {
+                this._isPointerDown = true;
                 return;
             }
-            let scene = this._attachedCamera.getScene();
 
-            if (this._onPrePointerObservableObserver) {
-                scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
+            if (pointerInfoPre.type === PointerEventTypes.POINTERUP) {
+                this._isPointerDown = false;
             }
+        });
 
-            this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
-            this._attachedCamera = null;
-        }
+        this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
+            let now = PrecisionDate.Now;
+            let dt = 0;
+            if (this._lastFrameTime != null) {
+                dt = now - this._lastFrameTime;
+            }
+            this._lastFrameTime = now;
 
-        /**
-         * Returns true if user is scrolling.
-         * @return true if user is scrolling.
-         */
-        private _userIsZooming(): boolean {
-            if (!this._attachedCamera) {
-                return false;
+            // 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);
+            this._cameraRotationSpeed = this._idleRotationSpeed * scale;
+
+            // Step camera rotation by rotation speed
+            if (this._attachedCamera) {
+                this._attachedCamera.alpha -= this._cameraRotationSpeed * (dt / 1000);
             }
-            return this._attachedCamera.inertialRadiusOffset !== 0;
+        });
+    }
+
+    /**
+     * Detaches the behavior from its current arc rotate camera.
+     */
+    public detach(): void {
+        if (!this._attachedCamera) {
+            return;
         }
+        let scene = this._attachedCamera.getScene();
 
-        private _lastFrameRadius = 0;
-        private _shouldAnimationStopForInteraction(): boolean {
-            if (!this._attachedCamera) {
-                return false;
-            }
+        if (this._onPrePointerObservableObserver) {
+            scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
+        }
 
-            var zoomHasHitLimit = false;
-            if (this._lastFrameRadius === this._attachedCamera.radius && this._attachedCamera.inertialRadiusOffset !== 0) {
-                zoomHasHitLimit = true;
-            }
+        this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+        this._attachedCamera = null;
+    }
 
-            // 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();
+    /**
+     * Returns true if user is scrolling.
+     * @return true if user is scrolling.
+     */
+    private _userIsZooming(): boolean {
+        if (!this._attachedCamera) {
+            return false;
         }
+        return this._attachedCamera.inertialRadiusOffset !== 0;
+    }
 
-        /**
-         *  Applies any current user interaction to the camera. Takes into account maximum alpha rotation.
-         */
-        private _applyUserInteraction(): void {
-            if (this._userIsMoving() && !this._shouldAnimationStopForInteraction()) {
-                this._lastInteractionTime = PrecisionDate.Now;
-            }
+    private _lastFrameRadius = 0;
+    private _shouldAnimationStopForInteraction(): boolean {
+        if (!this._attachedCamera) {
+            return false;
         }
 
-        // Tools
-        private _userIsMoving(): boolean {
-            if (!this._attachedCamera) {
-                return false;
-            }
+        var zoomHasHitLimit = false;
+        if (this._lastFrameRadius === this._attachedCamera.radius && this._attachedCamera.inertialRadiusOffset !== 0) {
+            zoomHasHitLimit = true;
+        }
 
-            return this._attachedCamera.inertialAlphaOffset !== 0 ||
-                this._attachedCamera.inertialBetaOffset !== 0 ||
-                this._attachedCamera.inertialRadiusOffset !== 0 ||
-                this._attachedCamera.inertialPanningX !== 0 ||
-                this._attachedCamera.inertialPanningY !== 0 ||
-                this._isPointerDown;
+        // 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 = PrecisionDate.Now;
         }
     }
+
+    // Tools
+    private _userIsMoving(): boolean {
+        if (!this._attachedCamera) {
+            return false;
+        }
+
+        return this._attachedCamera.inertialAlphaOffset !== 0 ||
+            this._attachedCamera.inertialBetaOffset !== 0 ||
+            this._attachedCamera.inertialRadiusOffset !== 0 ||
+            this._attachedCamera.inertialPanningX !== 0 ||
+            this._attachedCamera.inertialPanningY !== 0 ||
+            this._isPointerDown;
+    }
+}

+ 166 - 166
src/Behaviors/Cameras/bouncingBehavior.ts

@@ -8,209 +8,209 @@ import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { Animatable } from "../../Animations/animatable";
 import { Animation } from "../../Animations/animation";
 
+/**
+ * Add a bouncing effect to an ArcRotateCamera when reaching a specified minimum and maximum radius
+ * @see http://doc.babylonjs.com/how_to/camera_behaviors#bouncing-behavior
+ */
+export class BouncingBehavior implements Behavior<ArcRotateCamera> {
     /**
-     * Add a bouncing effect to an ArcRotateCamera when reaching a specified minimum and maximum radius
-     * @see http://doc.babylonjs.com/how_to/camera_behaviors#bouncing-behavior
+     * Gets the name of the behavior.
      */
-    export class BouncingBehavior implements Behavior<ArcRotateCamera> {
-        /**
-         * Gets the name of the behavior.
-         */
-        public get name(): string {
-            return "Bouncing";
-        }
+    public get name(): string {
+        return "Bouncing";
+    }
 
-        /**
-		 * The easing function used by animations
-		 */
-        public static EasingFunction = new BackEase(0.3);
-
-        /**
-		 * The easing mode used by animations
-		 */
-        public static EasingMode = EasingFunction.EASINGMODE_EASEOUT;
-
-        /**
-         * The duration of the animation, in milliseconds
-         */
-        public transitionDuration = 450;
-
-        /**
-         * Length of the distance animated by the transition when lower radius is reached
-         */
-        public lowerRadiusTransitionRange = 2;
-
-        /**
-         * Length of the distance animated by the transition when upper radius is reached
-         */
-        public upperRadiusTransitionRange = -2;
-
-        private _autoTransitionRange = false;
-
-        /**
-		 * Gets a value indicating if the lowerRadiusTransitionRange and upperRadiusTransitionRange are defined automatically
-		 */
-        public get autoTransitionRange(): boolean {
-            return this._autoTransitionRange;
-        }
+    /**
+     * The easing function used by animations
+     */
+    public static EasingFunction = new BackEase(0.3);
 
-        /**
-		 * Sets a value indicating if the lowerRadiusTransitionRange and upperRadiusTransitionRange are defined automatically
-		 * Transition ranges will be set to 5% of the bounding box diagonal in world space
-		 */
-        public set autoTransitionRange(value: boolean) {
-            if (this._autoTransitionRange === value) {
-                return;
-            }
+    /**
+     * The easing mode used by animations
+     */
+    public static EasingMode = EasingFunction.EASINGMODE_EASEOUT;
 
-            this._autoTransitionRange = value;
+    /**
+     * The duration of the animation, in milliseconds
+     */
+    public transitionDuration = 450;
 
-            let camera = this._attachedCamera;
-            if (!camera) {
-                return;
-            }
+    /**
+     * Length of the distance animated by the transition when lower radius is reached
+     */
+    public lowerRadiusTransitionRange = 2;
 
-            if (value) {
-                this._onMeshTargetChangedObserver = camera.onMeshTargetChangedObservable.add((mesh) => {
-                    if (!mesh) {
-                        return;
-                    }
+    /**
+     * Length of the distance animated by the transition when upper radius is reached
+     */
+    public upperRadiusTransitionRange = -2;
 
-                    mesh.computeWorldMatrix(true);
-                    let diagonal = mesh.getBoundingInfo().diagonalLength;
+    private _autoTransitionRange = false;
 
-                    this.lowerRadiusTransitionRange = diagonal * 0.05;
-                    this.upperRadiusTransitionRange = diagonal * 0.05;
-                });
-            } else if (this._onMeshTargetChangedObserver) {
-                camera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
-            }
+    /**
+     * Gets a value indicating if the lowerRadiusTransitionRange and upperRadiusTransitionRange are defined automatically
+     */
+    public get autoTransitionRange(): boolean {
+        return this._autoTransitionRange;
+    }
+
+    /**
+     * Sets a value indicating if the lowerRadiusTransitionRange and upperRadiusTransitionRange are defined automatically
+     * Transition ranges will be set to 5% of the bounding box diagonal in world space
+     */
+    public set autoTransitionRange(value: boolean) {
+        if (this._autoTransitionRange === value) {
+            return;
         }
 
-        // Connection
-        private _attachedCamera: Nullable<ArcRotateCamera>;
-        private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
-        private _onMeshTargetChangedObserver: Nullable<Observer<Nullable<AbstractMesh>>>;
+        this._autoTransitionRange = value;
 
-        /**
-         * Initializes the behavior.
-         */
-        public init(): void {
-            // Do notihng
+        let camera = this._attachedCamera;
+        if (!camera) {
+            return;
         }
 
-        /**
-         * Attaches the behavior to its arc rotate camera.
-         * @param camera Defines the camera to attach the behavior to
-         */
-        public attach(camera: ArcRotateCamera): void {
-            this._attachedCamera = camera;
-            this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
-                if (!this._attachedCamera) {
+        if (value) {
+            this._onMeshTargetChangedObserver = camera.onMeshTargetChangedObservable.add((mesh) => {
+                if (!mesh) {
                     return;
                 }
 
-                // Add the bounce animation to the lower radius limit
-                if (this._isRadiusAtLimit(this._attachedCamera.lowerRadiusLimit)) {
-                    this._applyBoundRadiusAnimation(this.lowerRadiusTransitionRange);
-                }
+                mesh.computeWorldMatrix(true);
+                let diagonal = mesh.getBoundingInfo().diagonalLength;
 
-                // Add the bounce animation to the upper radius limit
-                if (this._isRadiusAtLimit(this._attachedCamera.upperRadiusLimit)) {
-                    this._applyBoundRadiusAnimation(this.upperRadiusTransitionRange);
-                }
+                this.lowerRadiusTransitionRange = diagonal * 0.05;
+                this.upperRadiusTransitionRange = diagonal * 0.05;
             });
+        } else if (this._onMeshTargetChangedObserver) {
+            camera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
         }
+    }
 
-        /**
-         * Detaches the behavior from its current arc rotate camera.
-         */
-        public detach(): void {
+    // Connection
+    private _attachedCamera: Nullable<ArcRotateCamera>;
+    private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
+    private _onMeshTargetChangedObserver: Nullable<Observer<Nullable<AbstractMesh>>>;
+
+    /**
+     * Initializes the behavior.
+     */
+    public init(): void {
+        // Do notihng
+    }
+
+    /**
+     * Attaches the behavior to its arc rotate camera.
+     * @param camera Defines the camera to attach the behavior to
+     */
+    public attach(camera: ArcRotateCamera): void {
+        this._attachedCamera = camera;
+        this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
             if (!this._attachedCamera) {
                 return;
             }
-            if (this._onAfterCheckInputsObserver) {
-                this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+
+            // Add the bounce animation to the lower radius limit
+            if (this._isRadiusAtLimit(this._attachedCamera.lowerRadiusLimit)) {
+                this._applyBoundRadiusAnimation(this.lowerRadiusTransitionRange);
             }
-            if (this._onMeshTargetChangedObserver) {
-                this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
+
+            // Add the bounce animation to the upper radius limit
+            if (this._isRadiusAtLimit(this._attachedCamera.upperRadiusLimit)) {
+                this._applyBoundRadiusAnimation(this.upperRadiusTransitionRange);
             }
-            this._attachedCamera = null;
+        });
+    }
+
+    /**
+     * Detaches the behavior from its current arc rotate camera.
+     */
+    public detach(): void {
+        if (!this._attachedCamera) {
+            return;
         }
+        if (this._onAfterCheckInputsObserver) {
+            this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+        }
+        if (this._onMeshTargetChangedObserver) {
+            this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
+        }
+        this._attachedCamera = null;
+    }
 
-        // Animations
-        private _radiusIsAnimating: boolean = false;
-        private _radiusBounceTransition: Nullable<Animation> = null;
-        private _animatables = new Array<Animatable>();
-        private _cachedWheelPrecision: number;
-
-        /**
-		 * Checks if the camera radius is at the specified limit. Takes into account animation locks.
-		 * @param radiusLimit The limit to check against.
-		 * @return Bool to indicate if at limit.
-		 */
-        private _isRadiusAtLimit(radiusLimit: Nullable<number>): boolean {
-            if (!this._attachedCamera) {
-                return false;
-            }
+    // Animations
+    private _radiusIsAnimating: boolean = false;
+    private _radiusBounceTransition: Nullable<Animation> = null;
+    private _animatables = new Array<Animatable>();
+    private _cachedWheelPrecision: number;
 
-            if (this._attachedCamera.radius === radiusLimit && !this._radiusIsAnimating) {
-                return true;
-            }
+    /**
+     * Checks if the camera radius is at the specified limit. Takes into account animation locks.
+     * @param radiusLimit The limit to check against.
+     * @return Bool to indicate if at limit.
+     */
+    private _isRadiusAtLimit(radiusLimit: Nullable<number>): boolean {
+        if (!this._attachedCamera) {
             return false;
         }
 
-        /**
-		 * Applies an animation to the radius of the camera, extending by the radiusDelta.
-		 * @param radiusDelta The delta by which to animate to. Can be negative.
-		 */
-        private _applyBoundRadiusAnimation(radiusDelta: number): void {
-            if (!this._attachedCamera) {
-                return;
-            }
+        if (this._attachedCamera.radius === radiusLimit && !this._radiusIsAnimating) {
+            return true;
+        }
+        return false;
+    }
 
-            if (!this._radiusBounceTransition) {
-                BouncingBehavior.EasingFunction.setEasingMode(BouncingBehavior.EasingMode);
-                this._radiusBounceTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, BouncingBehavior.EasingFunction);
-            }
-            // Prevent zoom until bounce has completed
-            this._cachedWheelPrecision = this._attachedCamera.wheelPrecision;
-            this._attachedCamera.wheelPrecision = Infinity;
-            this._attachedCamera.inertialRadiusOffset = 0;
-
-            // Animate to the radius limit
-            this.stopAllAnimations();
-            this._radiusIsAnimating = true;
-            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);
-            }
+    /**
+     * Applies an animation to the radius of the camera, extending by the radiusDelta.
+     * @param radiusDelta The delta by which to animate to. Can be negative.
+     */
+    private _applyBoundRadiusAnimation(radiusDelta: number): void {
+        if (!this._attachedCamera) {
+            return;
         }
 
-        /**
-		 * Removes all animation locks. Allows new animations to be added to any of the camera properties.
-		 */
-        protected _clearAnimationLocks(): void {
-            this._radiusIsAnimating = false;
+        if (!this._radiusBounceTransition) {
+            BouncingBehavior.EasingFunction.setEasingMode(BouncingBehavior.EasingMode);
+            this._radiusBounceTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, BouncingBehavior.EasingFunction);
+        }
+        // Prevent zoom until bounce has completed
+        this._cachedWheelPrecision = this._attachedCamera.wheelPrecision;
+        this._attachedCamera.wheelPrecision = Infinity;
+        this._attachedCamera.inertialRadiusOffset = 0;
+
+        // Animate to the radius limit
+        this.stopAllAnimations();
+        this._radiusIsAnimating = true;
+        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);
+        }
+    }
 
-            if (this._attachedCamera) {
-                this._attachedCamera.wheelPrecision = this._cachedWheelPrecision;
-            }
+    /**
+     * Removes all animation locks. Allows new animations to be added to any of the camera properties.
+     */
+    protected _clearAnimationLocks(): void {
+        this._radiusIsAnimating = false;
+
+        if (this._attachedCamera) {
+            this._attachedCamera.wheelPrecision = this._cachedWheelPrecision;
         }
+    }
 
-        /**
-		 * Stops and removes all animations that have been applied to the camera
-		 */
-        public stopAllAnimations(): void {
-            if (this._attachedCamera) {
-                this._attachedCamera.animations = [];
-            }
-            while (this._animatables.length) {
-                this._animatables[0].onAnimationEnd = null;
-                this._animatables[0].stop();
-                this._animatables.shift();
-            }
+    /**
+     * Stops and removes all animations that have been applied to the camera
+     */
+    public stopAllAnimations(): void {
+        if (this._attachedCamera) {
+            this._attachedCamera.animations = [];
+        }
+        while (this._animatables.length) {
+            this._animatables[0].onAnimationEnd = null;
+            this._animatables[0].stop();
+            this._animatables.shift();
         }
     }
+}

+ 451 - 451
src/Behaviors/Cameras/framingBehavior.ts

@@ -12,541 +12,541 @@ import { Vector3, Vector2 } from "../../Maths/math";
 import { Animatable } from "../../Animations/animatable";
 import { Animation } from "../../Animations/animation";
 
+/**
+ * The framing behavior (FramingBehavior) is designed to automatically position an ArcRotateCamera when its target is set to a mesh. It is also useful if you want to prevent the camera to go under a virtual horizontal plane.
+ * @see http://doc.babylonjs.com/how_to/camera_behaviors#framing-behavior
+ */
+export class FramingBehavior implements Behavior<ArcRotateCamera> {
     /**
-     * The framing behavior (FramingBehavior) is designed to automatically position an ArcRotateCamera when its target is set to a mesh. It is also useful if you want to prevent the camera to go under a virtual horizontal plane.
-     * @see http://doc.babylonjs.com/how_to/camera_behaviors#framing-behavior
+     * Gets the name of the behavior.
      */
-    export class FramingBehavior implements Behavior<ArcRotateCamera> {
-        /**
-         * Gets the name of the behavior.
-         */
-        public get name(): string {
-            return "Framing";
-        }
+    public get name(): string {
+        return "Framing";
+    }
 
-        private _mode = FramingBehavior.FitFrustumSidesMode;
-        private _radiusScale = 1.0;
-        private _positionScale = 0.5;
-        private _defaultElevation = 0.3;
-        private _elevationReturnTime = 1500;
-        private _elevationReturnWaitTime = 1000;
-        private _zoomStopsAnimation = false;
-        private _framingTime = 1500;
-
-        /**
-		 * The easing function used by animations
-		 */
-        public static EasingFunction = new ExponentialEase();
-
-        /**
-		 * The easing mode used by animations
-		 */
-        public static EasingMode = EasingFunction.EASINGMODE_EASEINOUT;
-
-        /**
-		 * Sets the current mode used by the behavior
-		 */
-        public set mode(mode: number) {
-            this._mode = mode;
-        }
+    private _mode = FramingBehavior.FitFrustumSidesMode;
+    private _radiusScale = 1.0;
+    private _positionScale = 0.5;
+    private _defaultElevation = 0.3;
+    private _elevationReturnTime = 1500;
+    private _elevationReturnWaitTime = 1000;
+    private _zoomStopsAnimation = false;
+    private _framingTime = 1500;
 
-        /**
-		 * Gets current mode used by the behavior.
-		 */
-        public get mode(): number {
-            return this._mode;
-        }
+    /**
+     * The easing function used by animations
+     */
+    public static EasingFunction = new ExponentialEase();
 
-        /**
-		 * Sets the scale applied to the radius (1 by default)
-		 */
-        public set radiusScale(radius: number) {
-            this._radiusScale = radius;
-        }
+    /**
+     * The easing mode used by animations
+     */
+    public static EasingMode = EasingFunction.EASINGMODE_EASEINOUT;
 
-        /**
-		 * Gets the scale applied to the radius
-		 */
-        public get radiusScale(): number {
-            return this._radiusScale;
-        }
+    /**
+     * Sets the current mode used by the behavior
+     */
+    public set mode(mode: number) {
+        this._mode = mode;
+    }
 
-        /**
-		 * Sets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
-		 */
-        public set positionScale(scale: number) {
-            this._positionScale = scale;
-        }
+    /**
+     * Gets current mode used by the behavior.
+     */
+    public get mode(): number {
+        return this._mode;
+    }
 
-        /**
-		 * Gets the scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
-		 */
-        public get positionScale(): number {
-            return this._positionScale;
-        }
+    /**
+     * Sets the scale applied to the radius (1 by default)
+     */
+    public set radiusScale(radius: number) {
+        this._radiusScale = radius;
+    }
 
-        /**
-		* 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 scale applied to the radius
+     */
+    public get radiusScale(): number {
+        return this._radiusScale;
+    }
 
-        /**
-		* 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 scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
+     */
+    public set positionScale(scale: number) {
+        this._positionScale = scale;
+    }
 
-        /**
-		 * 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 scale to apply on Y axis to position camera focus. 0.5 by default which means the center of the bounding box.
+     */
+    public get positionScale(): number {
+        return this._positionScale;
+    }
 
-        /**
-		 * 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 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;
+    }
 
-        /**
-		 * 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 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;
+    }
 
-        /**
-		 * Gets the delay (in milliseconds) taken before the camera returns to the default beta position.
-		 */
-        public get elevationReturnWaitTime(): number {
-            return this._elevationReturnWaitTime;
-        }
+    /**
+     * 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;
+    }
 
-        /**
-		* Sets the flag that indicates if user zooming should stop animation.
-		*/
-        public set zoomStopsAnimation(flag: boolean) {
-            this._zoomStopsAnimation = flag;
-        }
+    /**
+     * 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;
+    }
 
-        /**
-		* Gets the flag that indicates if user zooming should stop animation.
-		*/
-        public get zoomStopsAnimation(): boolean {
-            return this._zoomStopsAnimation;
-        }
+    /**
+     * Sets the delay (in milliseconds) taken before the camera returns to the default beta position.
+     */
+    public set elevationReturnWaitTime(time: number) {
+        this._elevationReturnWaitTime = time;
+    }
 
-        /**
-		 * Sets the transition time when framing the mesh, in milliseconds
-		*/
-        public set framingTime(time: number) {
-            this._framingTime = time;
-        }
+    /**
+     * Gets the delay (in milliseconds) taken before the camera returns to the default beta position.
+     */
+    public get elevationReturnWaitTime(): number {
+        return this._elevationReturnWaitTime;
+    }
 
-        /**
-         * Gets the transition time when framing the mesh, in milliseconds
-        */
-        public get framingTime() {
-            return this._framingTime;
-        }
+    /**
+    * Sets the flag that indicates if user zooming should stop animation.
+    */
+    public set zoomStopsAnimation(flag: boolean) {
+        this._zoomStopsAnimation = flag;
+    }
 
-        /**
-         * Define if the behavior should automatically change the configured
-         * camera limits and sensibilities.
-         */
-        public autoCorrectCameraLimitsAndSensibility = true;
-
-        // Default behavior functions
-        private _onPrePointerObservableObserver: Nullable<Observer<PointerInfoPre>>;
-        private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
-        private _onMeshTargetChangedObserver: Nullable<Observer<Nullable<AbstractMesh>>>;
-        private _attachedCamera: Nullable<ArcRotateCamera>;
-        private _isPointerDown = false;
-        private _lastInteractionTime = -Infinity;
-
-        /**
-         * Initializes the behavior.
-         */
-        public init(): void {
-            // Do notihng
-        }
+    /**
+    * Gets the flag that indicates if user zooming should stop animation.
+    */
+    public get zoomStopsAnimation(): boolean {
+        return this._zoomStopsAnimation;
+    }
 
-        /**
-         * Attaches the behavior to its arc rotate camera.
-         * @param camera Defines the camera to attach the behavior to
-         */
-        public attach(camera: ArcRotateCamera): void {
-            this._attachedCamera = camera;
-            let scene = this._attachedCamera.getScene();
+    /**
+     * Sets the transition time when framing the mesh, in milliseconds
+    */
+    public set framingTime(time: number) {
+        this._framingTime = time;
+    }
 
-            FramingBehavior.EasingFunction.setEasingMode(FramingBehavior.EasingMode);
+    /**
+     * Gets the transition time when framing the mesh, in milliseconds
+    */
+    public get framingTime() {
+        return this._framingTime;
+    }
 
-            this._onPrePointerObservableObserver = scene.onPrePointerObservable.add((pointerInfoPre) => {
-                if (pointerInfoPre.type === PointerEventTypes.POINTERDOWN) {
-                    this._isPointerDown = true;
-                    return;
-                }
+    /**
+     * Define if the behavior should automatically change the configured
+     * camera limits and sensibilities.
+     */
+    public autoCorrectCameraLimitsAndSensibility = true;
 
-                if (pointerInfoPre.type === PointerEventTypes.POINTERUP) {
-                    this._isPointerDown = false;
-                }
-            });
+    // Default behavior functions
+    private _onPrePointerObservableObserver: Nullable<Observer<PointerInfoPre>>;
+    private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
+    private _onMeshTargetChangedObserver: Nullable<Observer<Nullable<AbstractMesh>>>;
+    private _attachedCamera: Nullable<ArcRotateCamera>;
+    private _isPointerDown = false;
+    private _lastInteractionTime = -Infinity;
 
-            this._onMeshTargetChangedObserver = camera.onMeshTargetChangedObservable.add((mesh) => {
-                if (mesh) {
-                    this.zoomOnMesh(mesh);
-                }
-            });
+    /**
+     * Initializes the behavior.
+     */
+    public init(): void {
+        // Do notihng
+    }
 
-            this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
-                // Stop the animation if there is user interaction and the animation should stop for this interaction
-                this._applyUserInteraction();
+    /**
+     * Attaches the behavior to its arc rotate camera.
+     * @param camera Defines the camera to attach the behavior to
+     */
+    public attach(camera: ArcRotateCamera): void {
+        this._attachedCamera = camera;
+        let scene = this._attachedCamera.getScene();
 
-                // 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();
-            });
-        }
+        FramingBehavior.EasingFunction.setEasingMode(FramingBehavior.EasingMode);
 
-        /**
-         * Detaches the behavior from its current arc rotate camera.
-         */
-        public detach(): void {
-            if (!this._attachedCamera) {
+        this._onPrePointerObservableObserver = scene.onPrePointerObservable.add((pointerInfoPre) => {
+            if (pointerInfoPre.type === PointerEventTypes.POINTERDOWN) {
+                this._isPointerDown = true;
                 return;
             }
 
-            let scene = this._attachedCamera.getScene();
-
-            if (this._onPrePointerObservableObserver) {
-                scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
+            if (pointerInfoPre.type === PointerEventTypes.POINTERUP) {
+                this._isPointerDown = false;
             }
+        });
 
-            if (this._onAfterCheckInputsObserver) {
-                this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+        this._onMeshTargetChangedObserver = camera.onMeshTargetChangedObservable.add((mesh) => {
+            if (mesh) {
+                this.zoomOnMesh(mesh);
             }
+        });
 
-            if (this._onMeshTargetChangedObserver) {
-                this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
-            }
+        this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
+            // Stop the animation if there is user interaction and the animation should stop for this interaction
+            this._applyUserInteraction();
 
-            this._attachedCamera = null;
-        }
+            // 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();
+        });
+    }
 
-        // Framing control
-        private _animatables = new Array<Animatable>();
-        private _betaIsAnimating = false;
-        private _betaTransition: Animation;
-        private _radiusTransition: Animation;
-        private _vectorTransition: Animation;
-
-        /**
-		 * Targets the given mesh and updates zoom level accordingly.
-		 * @param mesh  The mesh to target.
-		 * @param radius Optional. If a cached radius position already exists, overrides default.
-		 * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
-		 * @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: Nullable<() => void> = null): void {
-            mesh.computeWorldMatrix(true);
-
-            let boundingBox = mesh.getBoundingInfo().boundingBox;
-            this.zoomOnBoundingInfo(boundingBox.minimumWorld, boundingBox.maximumWorld, focusOnOriginXZ, onAnimationEnd);
+    /**
+     * Detaches the behavior from its current arc rotate camera.
+     */
+    public detach(): void {
+        if (!this._attachedCamera) {
+            return;
         }
 
-        /**
-		 * Targets the given mesh with its children and updates zoom level accordingly.
-		 * @param mesh  The mesh to target.
-		 * @param radius Optional. If a cached radius position already exists, overrides default.
-		 * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
-		 * @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 zoomOnMeshHierarchy(mesh: AbstractMesh, focusOnOriginXZ: boolean = false, onAnimationEnd: Nullable<() => void> = null): void {
-            mesh.computeWorldMatrix(true);
-
-            let boundingBox = mesh.getHierarchyBoundingVectors(true);
-            this.zoomOnBoundingInfo(boundingBox.min, boundingBox.max, focusOnOriginXZ, onAnimationEnd);
+        let scene = this._attachedCamera.getScene();
+
+        if (this._onPrePointerObservableObserver) {
+            scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
         }
 
-        /**
-		 * Targets the given meshes with their children and updates zoom level accordingly.
-		 * @param meshes  The mesh to target.
-		 * @param radius Optional. If a cached radius position already exists, overrides default.
-		 * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
-		 * @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 zoomOnMeshesHierarchy(meshes: AbstractMesh[], focusOnOriginXZ: boolean = false, onAnimationEnd: Nullable<() => void> = null): void {
-            let min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
-            let max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
-
-            for (let i = 0; i < meshes.length; i++) {
-                let boundingInfo = meshes[i].getHierarchyBoundingVectors(true);
-                Tools.CheckExtends(boundingInfo.min, min, max);
-                Tools.CheckExtends(boundingInfo.max, min, max);
-            }
+        if (this._onAfterCheckInputsObserver) {
+            this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+        }
 
-            this.zoomOnBoundingInfo(min, max, focusOnOriginXZ, onAnimationEnd);
+        if (this._onMeshTargetChangedObserver) {
+            this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
         }
 
-        /**
-		 * Targets the bounding box info defined by its extends and updates zoom level accordingly.
-		 * @param minimumWorld Determines the smaller position of the bounding box extend
-         * @param maximumWorld Determines the bigger position of the bounding box extend
-		 * @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: Nullable<() => void> = null): void {
-            let zoomTarget: Vector3;
-
-            if (!this._attachedCamera) {
-                return;
-            }
+        this._attachedCamera = null;
+    }
 
-            // Find target by interpolating from bottom of bounding box in world-space to top via framingPositionY
-            let bottom = minimumWorld.y;
-            let top = maximumWorld.y;
-            let zoomTargetY = bottom + (top - bottom) * this._positionScale;
-            let radiusWorld = maximumWorld.subtract(minimumWorld).scale(0.5);
-
-            if (focusOnOriginXZ) {
-                zoomTarget = new Vector3(0, zoomTargetY, 0);
-            } else {
-                let centerWorld = minimumWorld.add(radiusWorld);
-                zoomTarget = new Vector3(centerWorld.x, zoomTargetY, centerWorld.z);
-            }
+    // Framing control
+    private _animatables = new Array<Animatable>();
+    private _betaIsAnimating = false;
+    private _betaTransition: Animation;
+    private _radiusTransition: Animation;
+    private _vectorTransition: Animation;
 
-            if (!this._vectorTransition) {
-                this._vectorTransition = Animation.CreateAnimation("target", Animation.ANIMATIONTYPE_VECTOR3, 60, FramingBehavior.EasingFunction);
-            }
+    /**
+     * Targets the given mesh and updates zoom level accordingly.
+     * @param mesh  The mesh to target.
+     * @param radius Optional. If a cached radius position already exists, overrides default.
+     * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
+     * @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: Nullable<() => void> = null): void {
+        mesh.computeWorldMatrix(true);
 
-            this._betaIsAnimating = true;
-            let animatable = Animation.TransitionTo("target", zoomTarget, this._attachedCamera, this._attachedCamera.getScene(), 60, this._vectorTransition, this._framingTime);
-            if (animatable) {
-                this._animatables.push(animatable);
-            }
+        let boundingBox = mesh.getBoundingInfo().boundingBox;
+        this.zoomOnBoundingInfo(boundingBox.minimumWorld, boundingBox.maximumWorld, focusOnOriginXZ, onAnimationEnd);
+    }
 
-            // sets the radius and lower radius bounds
-            // Small delta ensures camera is not always at lower zoom limit.
-            let radius = 0;
-            if (this._mode === FramingBehavior.FitFrustumSidesMode) {
-                let position = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
-                if (this.autoCorrectCameraLimitsAndSensibility) {
-                    this._attachedCamera.lowerRadiusLimit = radiusWorld.length() + this._attachedCamera.minZ;
-                }
-                radius = position;
-            } else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
-                radius = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
-                if (this.autoCorrectCameraLimitsAndSensibility && this._attachedCamera.lowerRadiusLimit === null) {
-                    this._attachedCamera.lowerRadiusLimit = this._attachedCamera.minZ;
-                }
-            }
+    /**
+     * Targets the given mesh with its children and updates zoom level accordingly.
+     * @param mesh  The mesh to target.
+     * @param radius Optional. If a cached radius position already exists, overrides default.
+     * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
+     * @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 zoomOnMeshHierarchy(mesh: AbstractMesh, focusOnOriginXZ: boolean = false, onAnimationEnd: Nullable<() => void> = null): void {
+        mesh.computeWorldMatrix(true);
 
-            // Set sensibilities
-            if (this.autoCorrectCameraLimitsAndSensibility) {
-                const extend = maximumWorld.subtract(minimumWorld).length();
-                this._attachedCamera.panningSensibility = 5000 / extend;
-                this._attachedCamera.wheelPrecision = 100 / radius;
-            }
+        let boundingBox = mesh.getHierarchyBoundingVectors(true);
+        this.zoomOnBoundingInfo(boundingBox.min, boundingBox.max, focusOnOriginXZ, onAnimationEnd);
+    }
 
-            // transition to new radius
-            if (!this._radiusTransition) {
-                this._radiusTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);
-            }
+    /**
+     * Targets the given meshes with their children and updates zoom level accordingly.
+     * @param meshes  The mesh to target.
+     * @param radius Optional. If a cached radius position already exists, overrides default.
+     * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
+     * @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 zoomOnMeshesHierarchy(meshes: AbstractMesh[], focusOnOriginXZ: boolean = false, onAnimationEnd: Nullable<() => void> = null): void {
+        let min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+        let max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+
+        for (let i = 0; i < meshes.length; i++) {
+            let boundingInfo = meshes[i].getHierarchyBoundingVectors(true);
+            Tools.CheckExtends(boundingInfo.min, min, max);
+            Tools.CheckExtends(boundingInfo.max, min, max);
+        }
 
-            animatable = Animation.TransitionTo("radius", radius, this._attachedCamera, this._attachedCamera.getScene(),
-                60, this._radiusTransition, this._framingTime, () => {
-                    this.stopAllAnimations();
-                    if (onAnimationEnd) {
-                        onAnimationEnd();
-                    }
+        this.zoomOnBoundingInfo(min, max, focusOnOriginXZ, onAnimationEnd);
+    }
 
-                    if (this._attachedCamera && this._attachedCamera.useInputToRestoreState) {
-                        this._attachedCamera.storeState();
-                    }
-                });
+    /**
+     * Targets the bounding box info defined by its extends and updates zoom level accordingly.
+     * @param minimumWorld Determines the smaller position of the bounding box extend
+     * @param maximumWorld Determines the bigger position of the bounding box extend
+     * @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: Nullable<() => void> = null): void {
+        let zoomTarget: Vector3;
 
-            if (animatable) {
-                this._animatables.push(animatable);
-            }
+        if (!this._attachedCamera) {
+            return;
         }
 
-        /**
-		 * Calculates the lowest radius for the camera based on the bounding box of the mesh.
-		 * @param mesh The mesh on which to base the calculation. mesh boundingInfo used to estimate necessary
-		 *			  frustum width.
-		 * @return The minimum distance from the primary mesh's center point at which the camera must be kept in order
-		 *		 to fully enclose the mesh in the viewing frustum.
-		 */
-        protected _calculateLowerRadiusFromModelBoundingSphere(minimumWorld: Vector3, maximumWorld: Vector3): number {
-            let size = maximumWorld.subtract(minimumWorld);
-            let boxVectorGlobalDiagonal = size.length();
-            let frustumSlope: Vector2 = this._getFrustumSlope();
-
-            // Formula for setting distance
-            // (Good explanation: http://stackoverflow.com/questions/2866350/move-camera-to-fit-3d-scene)
-            let radiusWithoutFraming = boxVectorGlobalDiagonal * 0.5;
-
-            // Horizon distance
-            let radius = radiusWithoutFraming * this._radiusScale;
-            let distanceForHorizontalFrustum = radius * Math.sqrt(1.0 + 1.0 / (frustumSlope.x * frustumSlope.x));
-            let distanceForVerticalFrustum = radius * Math.sqrt(1.0 + 1.0 / (frustumSlope.y * frustumSlope.y));
-            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;
-            }
+        // Find target by interpolating from bottom of bounding box in world-space to top via framingPositionY
+        let bottom = minimumWorld.y;
+        let top = maximumWorld.y;
+        let zoomTargetY = bottom + (top - bottom) * this._positionScale;
+        let radiusWorld = maximumWorld.subtract(minimumWorld).scale(0.5);
+
+        if (focusOnOriginXZ) {
+            zoomTarget = new Vector3(0, zoomTargetY, 0);
+        } else {
+            let centerWorld = minimumWorld.add(radiusWorld);
+            zoomTarget = new Vector3(centerWorld.x, zoomTargetY, centerWorld.z);
+        }
 
-            // Don't exceed the upper radius limit
-            if (camera.upperRadiusLimit) {
-                distance = distance > camera.upperRadiusLimit ? camera.upperRadiusLimit : distance;
-            }
+        if (!this._vectorTransition) {
+            this._vectorTransition = Animation.CreateAnimation("target", Animation.ANIMATIONTYPE_VECTOR3, 60, FramingBehavior.EasingFunction);
+        }
 
-            return distance;
+        this._betaIsAnimating = true;
+        let animatable = Animation.TransitionTo("target", zoomTarget, this._attachedCamera, this._attachedCamera.getScene(), 60, this._vectorTransition, this._framingTime);
+        if (animatable) {
+            this._animatables.push(animatable);
         }
 
-        /**
-		 * 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 {
-            if (this._elevationReturnTime < 0) {
-                return;
+        // sets the radius and lower radius bounds
+        // Small delta ensures camera is not always at lower zoom limit.
+        let radius = 0;
+        if (this._mode === FramingBehavior.FitFrustumSidesMode) {
+            let position = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+            if (this.autoCorrectCameraLimitsAndSensibility) {
+                this._attachedCamera.lowerRadiusLimit = radiusWorld.length() + this._attachedCamera.minZ;
             }
+            radius = position;
+        } else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
+            radius = this._calculateLowerRadiusFromModelBoundingSphere(minimumWorld, maximumWorld);
+            if (this.autoCorrectCameraLimitsAndSensibility && this._attachedCamera.lowerRadiusLimit === null) {
+                this._attachedCamera.lowerRadiusLimit = this._attachedCamera.minZ;
+            }
+        }
 
-            let timeSinceInteraction = PrecisionDate.Now - this._lastInteractionTime;
-            let defaultBeta = Math.PI * 0.5 - this._defaultElevation;
-            let limitBeta = Math.PI * 0.5;
+        // Set sensibilities
+        if (this.autoCorrectCameraLimitsAndSensibility) {
+            const extend = maximumWorld.subtract(minimumWorld).length();
+            this._attachedCamera.panningSensibility = 5000 / extend;
+            this._attachedCamera.wheelPrecision = 100 / radius;
+        }
 
-            // Bring the camera back up if below the ground plane
-            if (this._attachedCamera && !this._betaIsAnimating && this._attachedCamera.beta > limitBeta && timeSinceInteraction >= this._elevationReturnWaitTime) {
-                this._betaIsAnimating = true;
+        // transition to new radius
+        if (!this._radiusTransition) {
+            this._radiusTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);
+        }
 
-                //Transition to new position
+        animatable = Animation.TransitionTo("radius", radius, this._attachedCamera, this._attachedCamera.getScene(),
+            60, this._radiusTransition, this._framingTime, () => {
                 this.stopAllAnimations();
-
-                if (!this._betaTransition) {
-                    this._betaTransition = Animation.CreateAnimation("beta", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);
+                if (onAnimationEnd) {
+                    onAnimationEnd();
                 }
 
-                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);
+                if (this._attachedCamera && this._attachedCamera.useInputToRestoreState) {
+                    this._attachedCamera.storeState();
                 }
-            }
+            });
+
+        if (animatable) {
+            this._animatables.push(animatable);
         }
+    }
 
-        /**
-		 * Returns the frustum slope based on the canvas ratio and camera FOV
-		 * @returns The frustum slope represented as a Vector2 with X and Y slopes
-		 */
-        private _getFrustumSlope(): Vector2 {
-            // Calculate the viewport ratio
-            // Aspect Ratio is Height/Width.
-            let camera = this._attachedCamera;
-
-            if (!camera) {
-                return Vector2.Zero();
-            }
+    /**
+     * Calculates the lowest radius for the camera based on the bounding box of the mesh.
+     * @param mesh The mesh on which to base the calculation. mesh boundingInfo used to estimate necessary
+     *			  frustum width.
+     * @return The minimum distance from the primary mesh's center point at which the camera must be kept in order
+     *		 to fully enclose the mesh in the viewing frustum.
+     */
+    protected _calculateLowerRadiusFromModelBoundingSphere(minimumWorld: Vector3, maximumWorld: Vector3): number {
+        let size = maximumWorld.subtract(minimumWorld);
+        let boxVectorGlobalDiagonal = size.length();
+        let frustumSlope: Vector2 = this._getFrustumSlope();
+
+        // Formula for setting distance
+        // (Good explanation: http://stackoverflow.com/questions/2866350/move-camera-to-fit-3d-scene)
+        let radiusWithoutFraming = boxVectorGlobalDiagonal * 0.5;
+
+        // Horizon distance
+        let radius = radiusWithoutFraming * this._radiusScale;
+        let distanceForHorizontalFrustum = radius * Math.sqrt(1.0 + 1.0 / (frustumSlope.x * frustumSlope.x));
+        let distanceForVerticalFrustum = radius * Math.sqrt(1.0 + 1.0 / (frustumSlope.y * frustumSlope.y));
+        let distance = Math.max(distanceForHorizontalFrustum, distanceForVerticalFrustum);
+        let camera = this._attachedCamera;
+
+        if (!camera) {
+            return 0;
+        }
 
-            let engine = camera.getScene().getEngine();
-            var aspectRatio = engine.getAspectRatio(camera);
+        if (camera.lowerRadiusLimit && this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
+            // Don't exceed the requested limit
+            distance = distance < camera.lowerRadiusLimit ? camera.lowerRadiusLimit : distance;
+        }
 
-            // Camera FOV is the vertical field of view (top-bottom) in radians.
-            // Slope of the frustum top/bottom planes in view space, relative to the forward vector.
-            var frustumSlopeY = Math.tan(camera.fov / 2);
+        // Don't exceed the upper radius limit
+        if (camera.upperRadiusLimit) {
+            distance = distance > camera.upperRadiusLimit ? camera.upperRadiusLimit : distance;
+        }
 
-            // Slope of the frustum left/right planes in view space, relative to the forward vector.
-            // Provides the amount that one side (e.g. left) of the frustum gets wider for every unit
-            // along the forward vector.
-            var frustumSlopeX = frustumSlopeY * aspectRatio;
+        return distance;
+    }
 
-            return new Vector2(frustumSlopeX, frustumSlopeY);
+    /**
+     * 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 {
+        if (this._elevationReturnTime < 0) {
+            return;
         }
 
-        /**
-		 * Removes all animation locks. Allows new animations to be added to any of the arcCamera properties.
-		 */
-        private _clearAnimationLocks(): void {
-            this._betaIsAnimating = false;
-        }
+        let timeSinceInteraction = PrecisionDate.Now - this._lastInteractionTime;
+        let defaultBeta = Math.PI * 0.5 - this._defaultElevation;
+        let limitBeta = Math.PI * 0.5;
 
-        /**
-		 *  Applies any current user interaction to the camera. Takes into account maximum alpha rotation.
-		 */
-        private _applyUserInteraction(): void {
-            if (this.isUserIsMoving) {
-                this._lastInteractionTime = PrecisionDate.Now;
-                this.stopAllAnimations();
-                this._clearAnimationLocks();
-            }
-        }
+        // Bring the camera back up if below the ground plane
+        if (this._attachedCamera && !this._betaIsAnimating && this._attachedCamera.beta > limitBeta && timeSinceInteraction >= this._elevationReturnWaitTime) {
+            this._betaIsAnimating = true;
+
+            //Transition to new position
+            this.stopAllAnimations();
 
-        /**
-		 * Stops and removes all animations that have been applied to the camera
-		 */
-        public stopAllAnimations(): void {
-            if (this._attachedCamera) {
-                this._attachedCamera.animations = [];
+            if (!this._betaTransition) {
+                this._betaTransition = Animation.CreateAnimation("beta", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);
             }
 
-            while (this._animatables.length) {
-                if (this._animatables[0]) {
-                    this._animatables[0].onAnimationEnd = null;
-                    this._animatables[0].stop();
-                }
-                this._animatables.shift();
+            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);
             }
         }
+    }
 
-        /**
-		 * Gets a value indicating if the user is moving the camera
-		 */
-        public get isUserIsMoving(): boolean {
-            if (!this._attachedCamera) {
-                return false;
-            }
+    /**
+     * Returns the frustum slope based on the canvas ratio and camera FOV
+     * @returns The frustum slope represented as a Vector2 with X and Y slopes
+     */
+    private _getFrustumSlope(): Vector2 {
+        // Calculate the viewport ratio
+        // Aspect Ratio is Height/Width.
+        let camera = this._attachedCamera;
 
-            return this._attachedCamera.inertialAlphaOffset !== 0 ||
-                this._attachedCamera.inertialBetaOffset !== 0 ||
-                this._attachedCamera.inertialRadiusOffset !== 0 ||
-                this._attachedCamera.inertialPanningX !== 0 ||
-                this._attachedCamera.inertialPanningY !== 0 ||
-                this._isPointerDown;
+        if (!camera) {
+            return Vector2.Zero();
         }
 
-        // Statics
+        let engine = camera.getScene().getEngine();
+        var aspectRatio = engine.getAspectRatio(camera);
+
+        // Camera FOV is the vertical field of view (top-bottom) in radians.
+        // Slope of the frustum top/bottom planes in view space, relative to the forward vector.
+        var frustumSlopeY = Math.tan(camera.fov / 2);
+
+        // Slope of the frustum left/right planes in view space, relative to the forward vector.
+        // Provides the amount that one side (e.g. left) of the frustum gets wider for every unit
+        // along the forward vector.
+        var frustumSlopeX = frustumSlopeY * aspectRatio;
 
-        /**
-         * The camera can move all the way towards the mesh.
-         */
-        public static IgnoreBoundsSizeMode = 0;
+        return new Vector2(frustumSlopeX, frustumSlopeY);
+    }
 
-        /**
-         * The camera is not allowed to zoom closer to the mesh than the point at which the adjusted bounding sphere touches the frustum sides
-         */
-        public static FitFrustumSidesMode = 1;
+    /**
+     * Removes all animation locks. Allows new animations to be added to any of the arcCamera properties.
+     */
+    private _clearAnimationLocks(): void {
+        this._betaIsAnimating = false;
     }
+
+    /**
+     *  Applies any current user interaction to the camera. Takes into account maximum alpha rotation.
+     */
+    private _applyUserInteraction(): void {
+        if (this.isUserIsMoving) {
+            this._lastInteractionTime = PrecisionDate.Now;
+            this.stopAllAnimations();
+            this._clearAnimationLocks();
+        }
+    }
+
+    /**
+     * Stops and removes all animations that have been applied to the camera
+     */
+    public stopAllAnimations(): void {
+        if (this._attachedCamera) {
+            this._attachedCamera.animations = [];
+        }
+
+        while (this._animatables.length) {
+            if (this._animatables[0]) {
+                this._animatables[0].onAnimationEnd = null;
+                this._animatables[0].stop();
+            }
+            this._animatables.shift();
+        }
+    }
+
+    /**
+     * 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 ||
+            this._attachedCamera.inertialPanningX !== 0 ||
+            this._attachedCamera.inertialPanningY !== 0 ||
+            this._isPointerDown;
+    }
+
+    // Statics
+
+    /**
+     * The camera can move all the way towards the mesh.
+     */
+    public static IgnoreBoundsSizeMode = 0;
+
+    /**
+     * The camera is not allowed to zoom closer to the mesh than the point at which the adjusted bounding sphere touches the frustum sides
+     */
+    public static FitFrustumSidesMode = 1;
+}

+ 144 - 144
src/Behaviors/Meshes/attachToBoxBehavior.ts

@@ -6,167 +6,167 @@ import { Nullable } from "../../types";
 import { Observer } from "../../Misc/observable";
 import { Behavior } from "../../Behaviors/behavior";
 
+/**
+ * @hidden
+ */
+class FaceDirectionInfo {
+    constructor(public direction: Vector3, public rotatedDirection = new Vector3(), public diff = 0, public ignore = false) { }
+}
+
+/**
+ * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+ */
+export class AttachToBoxBehavior implements Behavior<Mesh> {
+    /**
+     *  The name of the behavior
+     */
+    public name = "AttachToBoxBehavior";
+    /**
+     * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
+     */
+    public distanceAwayFromFace = 0.15;
     /**
-     * @hidden
+     * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
      */
-    class FaceDirectionInfo {
-        constructor(public direction: Vector3, public rotatedDirection = new Vector3(), public diff = 0, public ignore = false) {}
+    public distanceAwayFromBottomOfFace = 0.15;
+    private _faceVectors = [new FaceDirectionInfo(Vector3.Up()), new FaceDirectionInfo(Vector3.Down()), new FaceDirectionInfo(Vector3.Left()), new FaceDirectionInfo(Vector3.Right()), new FaceDirectionInfo(Vector3.Forward()), new FaceDirectionInfo(Vector3.Forward().scaleInPlace(-1))];
+    private _target: Mesh;
+    private _scene: Scene;
+    private _onRenderObserver: Nullable<Observer<Scene>>;
+    private _tmpMatrix = new Matrix();
+    private _tmpVector = new Vector3();
+
+    /**
+     * Creates the AttachToBoxBehavior, used to attach UI to the closest face of the box to a camera
+     * @param ui The transform node that should be attched to the mesh
+     */
+    constructor(private ui: TransformNode) {
+        /* Does nothing */
     }
 
     /**
-     * A behavior that when attached to a mesh will will place a specified node on the meshes face pointing towards the camera
+     *  Initializes the behavior
      */
-    export class AttachToBoxBehavior implements Behavior<Mesh> {
-        /**
-         *  The name of the behavior
-         */
-        public name = "AttachToBoxBehavior";
-        /**
-         * The distance away from the face of the mesh that the UI should be attached to (default: 0.15)
-         */
-        public distanceAwayFromFace = 0.15;
-        /**
-         * The distance from the bottom of the face that the UI should be attached to (default: 0.15)
-         */
-        public distanceAwayFromBottomOfFace = 0.15;
-        private _faceVectors = [new FaceDirectionInfo(Vector3.Up()), new FaceDirectionInfo(Vector3.Down()), new FaceDirectionInfo(Vector3.Left()), new FaceDirectionInfo(Vector3.Right()), new FaceDirectionInfo(Vector3.Forward()), new FaceDirectionInfo(Vector3.Forward().scaleInPlace(-1))];
-        private _target: Mesh;
-        private _scene: Scene;
-        private _onRenderObserver: Nullable<Observer<Scene>>;
-        private _tmpMatrix = new Matrix();
-        private _tmpVector = new Vector3();
+    public init() {
+        /* Does nothing */
+    }
 
-        /**
-         * Creates the AttachToBoxBehavior, used to attach UI to the closest face of the box to a camera
-         * @param ui The transform node that should be attched to the mesh
-         */
-        constructor(private ui: TransformNode) {
-            /* Does nothing */
-        }
+    private _closestFace(targetDirection: Vector3) {
+        // Go over each face and calculate the angle between the face's normal and targetDirection
+        this._faceVectors.forEach((v) => {
+            if (!this._target.rotationQuaternion) {
+                this._target.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._target.rotation.y, this._target.rotation.x, this._target.rotation.z);
+            }
+            this._target.rotationQuaternion.toRotationMatrix(this._tmpMatrix);
+            Vector3.TransformCoordinatesToRef(v.direction, this._tmpMatrix, v.rotatedDirection);
+            v.diff = Vector3.GetAngleBetweenVectors(v.rotatedDirection, targetDirection, Vector3.Cross(v.rotatedDirection, targetDirection));
+        });
+        // Return the face information of the one with the normal closeset to target direction
+        return this._faceVectors.reduce((min, p) => {
+            if (min.ignore) {
+                return p;
+            } else if (p.ignore) {
+                return min;
+            } else {
+                return min.diff < p.diff ? min : p;
+            }
+        }, this._faceVectors[0]);
+    }
 
-        /**
-         *  Initializes the behavior
-         */
-        public init() {
-            /* Does nothing */
-        }
+    private _zeroVector = Vector3.Zero();
+    private _lookAtTmpMatrix = new Matrix();
+    private _lookAtToRef(pos: Vector3, up = new Vector3(0, 1, 0), ref: Quaternion) {
+        Matrix.LookAtLHToRef(this._zeroVector, pos, up, this._lookAtTmpMatrix);
+        this._lookAtTmpMatrix.invert();
+        Quaternion.FromRotationMatrixToRef(this._lookAtTmpMatrix, ref);
+    }
 
-        private _closestFace(targetDirection: Vector3) {
-            // Go over each face and calculate the angle between the face's normal and targetDirection
-            this._faceVectors.forEach((v) => {
-                if (!this._target.rotationQuaternion) {
-                    this._target.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._target.rotation.y, this._target.rotation.x, this._target.rotation.z);
-                }
-                this._target.rotationQuaternion.toRotationMatrix(this._tmpMatrix);
-                Vector3.TransformCoordinatesToRef(v.direction, this._tmpMatrix, v.rotatedDirection);
-                v.diff = Vector3.GetAngleBetweenVectors(v.rotatedDirection, targetDirection, Vector3.Cross(v.rotatedDirection, targetDirection));
-            });
-            // Return the face information of the one with the normal closeset to target direction
-            return this._faceVectors.reduce((min, p) => {
-                if (min.ignore) {
-                    return p;
-                }else if (p.ignore) {
-                    return min;
-                }else {
-                    return min.diff < p.diff ? min : p;
-                }
-            }, this._faceVectors[0]);
-        }
+    /**
+     * Attaches the AttachToBoxBehavior to the passed in mesh
+     * @param target The mesh that the specified node will be attached to
+     */
+    attach(target: Mesh) {
+        this._target = target;
+        this._scene = this._target.getScene();
 
-        private _zeroVector = Vector3.Zero();
-        private _lookAtTmpMatrix = new Matrix();
-        private _lookAtToRef(pos: Vector3, up = new Vector3(0, 1, 0), ref: Quaternion) {
-            Matrix.LookAtLHToRef(this._zeroVector, pos, up, this._lookAtTmpMatrix);
-            this._lookAtTmpMatrix.invert();
-            Quaternion.FromRotationMatrixToRef(this._lookAtTmpMatrix, ref);
-        }
+        // Every frame, update the app bars position
+        this._onRenderObserver = this._scene.onBeforeRenderObservable.add(() => {
+            if (!this._scene.activeCamera) {
+                return;
+            }
 
-        /**
-         * Attaches the AttachToBoxBehavior to the passed in mesh
-         * @param target The mesh that the specified node will be attached to
-         */
-        attach(target: Mesh) {
-            this._target = target;
-            this._scene = this._target.getScene();
+            // Find the face closest to the cameras position
+            var cameraPos = this._scene.activeCamera.position;
+            if ((<any>this._scene.activeCamera).devicePosition) {
+                cameraPos = (<any>this._scene.activeCamera).devicePosition;
+            }
+            var facing = this._closestFace(cameraPos.subtract(target.position));
+            if (this._scene.activeCamera.leftCamera) {
+                this._scene.activeCamera.leftCamera.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
+            } else {
+                this._scene.activeCamera.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
+            }
 
-            // Every frame, update the app bars position
-            this._onRenderObserver = this._scene.onBeforeRenderObservable.add(() => {
-                if (!this._scene.activeCamera) {
-                    return;
+            // Get camera up direction
+            Vector3.TransformCoordinatesToRef(Vector3.Up(), this._tmpMatrix, this._tmpVector);
+            // Ignore faces to not select a parrelel face for the up vector of the UI
+            this._faceVectors.forEach((v) => {
+                if (facing.direction.x && v.direction.x) {
+                    v.ignore = true;
                 }
-
-                // Find the face closest to the cameras position
-                var cameraPos = this._scene.activeCamera.position;
-                if ((<any>this._scene.activeCamera).devicePosition) {
-                    cameraPos = (<any>this._scene.activeCamera).devicePosition;
+                if (facing.direction.y && v.direction.y) {
+                    v.ignore = true;
                 }
-                var facing = this._closestFace(cameraPos.subtract(target.position));
-                if (this._scene.activeCamera.leftCamera) {
-                    this._scene.activeCamera.leftCamera.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
-                }else {
-                    this._scene.activeCamera.computeWorldMatrix().getRotationMatrixToRef(this._tmpMatrix);
+                if (facing.direction.z && v.direction.z) {
+                    v.ignore = true;
                 }
+            });
+            var facingUp = this._closestFace(this._tmpVector);
+            // Unignore faces
+            this._faceVectors.forEach((v) => {
+                v.ignore = false;
+            });
 
-                // Get camera up direction
-                Vector3.TransformCoordinatesToRef(Vector3.Up(), this._tmpMatrix, this._tmpVector);
-                // Ignore faces to not select a parrelel face for the up vector of the UI
-                this._faceVectors.forEach((v) => {
-                    if (facing.direction.x && v.direction.x) {
-                        v.ignore = true;
-                    }
-                    if (facing.direction.y && v.direction.y) {
-                        v.ignore = true;
-                    }
-                    if (facing.direction.z && v.direction.z) {
-                        v.ignore = true;
-                    }
-                });
-                var facingUp = this._closestFace(this._tmpVector);
-                // Unignore faces
-                this._faceVectors.forEach((v) => {
-                    v.ignore = false;
-                });
+            // Position the app bar on that face
+            this.ui.position.copyFrom(target.position);
+            if (facing.direction.x) {
+                facing.rotatedDirection.scaleToRef((target.scaling.x / 2) + this.distanceAwayFromFace, this._tmpVector);
+                this.ui.position.addInPlace(this._tmpVector);
+            }
+            if (facing.direction.y) {
+                facing.rotatedDirection.scaleToRef((target.scaling.y / 2) + this.distanceAwayFromFace, this._tmpVector);
+                this.ui.position.addInPlace(this._tmpVector);
+            }
+            if (facing.direction.z) {
+                facing.rotatedDirection.scaleToRef((target.scaling.z / 2) + this.distanceAwayFromFace, this._tmpVector);
+                this.ui.position.addInPlace(this._tmpVector);
+            }
 
-                // Position the app bar on that face
-                this.ui.position.copyFrom(target.position);
-                if (facing.direction.x) {
-                    facing.rotatedDirection.scaleToRef((target.scaling.x / 2) + this.distanceAwayFromFace, this._tmpVector);
-                    this.ui.position.addInPlace(this._tmpVector);
-                }
-                if (facing.direction.y) {
-                    facing.rotatedDirection.scaleToRef((target.scaling.y / 2) + this.distanceAwayFromFace, this._tmpVector);
-                    this.ui.position.addInPlace(this._tmpVector);
-                }
-                if (facing.direction.z) {
-                    facing.rotatedDirection.scaleToRef((target.scaling.z / 2) + this.distanceAwayFromFace, this._tmpVector);
-                    this.ui.position.addInPlace(this._tmpVector);
-                }
+            // Rotate to be oriented properly to the camera
+            if (!this.ui.rotationQuaternion) {
+                this.ui.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.ui.rotation.y, this.ui.rotation.x, this.ui.rotation.z);
+            }
+            facing.rotatedDirection.scaleToRef(-1, this._tmpVector);
+            this._lookAtToRef(this._tmpVector, facingUp.rotatedDirection, this.ui.rotationQuaternion);
 
-                // Rotate to be oriented properly to the camera
-                if (!this.ui.rotationQuaternion) {
-                    this.ui.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.ui.rotation.y, this.ui.rotation.x, this.ui.rotation.z);
-                }
-                facing.rotatedDirection.scaleToRef(-1, this._tmpVector);
-                this._lookAtToRef(this._tmpVector, facingUp.rotatedDirection, this.ui.rotationQuaternion);
-
-                // Place ui the correct distance from the bottom of the mesh
-                if (facingUp.direction.x) {
-                    this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace - target.scaling.x / 2, this._tmpVector);
-                }
-                if (facingUp.direction.y) {
-                    this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace - target.scaling.y / 2, this._tmpVector);
-                }
-                if (facingUp.direction.z) {
-                    this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace - target.scaling.z / 2, this._tmpVector);
-                }
-                this.ui.position.addInPlace(this._tmpVector);
-            });
-        }
+            // Place ui the correct distance from the bottom of the mesh
+            if (facingUp.direction.x) {
+                this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace - target.scaling.x / 2, this._tmpVector);
+            }
+            if (facingUp.direction.y) {
+                this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace - target.scaling.y / 2, this._tmpVector);
+            }
+            if (facingUp.direction.z) {
+                this.ui.up.scaleToRef(this.distanceAwayFromBottomOfFace - target.scaling.z / 2, this._tmpVector);
+            }
+            this.ui.position.addInPlace(this._tmpVector);
+        });
+    }
 
-        /**
-         *  Detaches the behavior from the mesh
-         */
-        detach() {
-            this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
-        }
+    /**
+     *  Detaches the behavior from the mesh
+     */
+    detach() {
+        this._scene.onBeforeRenderObservable.remove(this._onRenderObserver);
     }
+}

+ 73 - 73
src/Behaviors/Meshes/fadeInOutBehavior.ts

@@ -3,93 +3,93 @@ import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { Mesh } from "../../Meshes/mesh";
 import { Nullable } from "../../types";
 
+/**
+ * A behavior that when attached to a mesh will allow the mesh to fade in and out
+ */
+export class FadeInOutBehavior implements Behavior<Mesh> {
     /**
-     * A behavior that when attached to a mesh will allow the mesh to fade in and out
+     * Time in milliseconds to delay before fading in (Default: 0)
      */
-    export class FadeInOutBehavior implements Behavior<Mesh> {
-        /**
-         * Time in milliseconds to delay before fading in (Default: 0)
-         */
-        public delay = 0;
-        /**
-         * Time in milliseconds for the mesh to fade in (Default: 300)
-         */
-        public fadeInTime = 300;
+    public delay = 0;
+    /**
+     * Time in milliseconds for the mesh to fade in (Default: 300)
+     */
+    public fadeInTime = 300;
 
-        private _millisecondsPerFrame = 1000 / 60;
-        private _hovered = false;
-        private _hoverValue = 0;
-        private _ownerNode: Nullable<Mesh> = null;
+    private _millisecondsPerFrame = 1000 / 60;
+    private _hovered = false;
+    private _hoverValue = 0;
+    private _ownerNode: Nullable<Mesh> = null;
 
-        /**
-         * Instatiates the FadeInOutBehavior
-         */
-        constructor() {
-        }
+    /**
+     * Instatiates the FadeInOutBehavior
+     */
+    constructor() {
+    }
 
-        /**
-         *  The name of the behavior
-         */
-        public get name(): string {
-            return "FadeInOut";
-        }
+    /**
+     *  The name of the behavior
+     */
+    public get name(): string {
+        return "FadeInOut";
+    }
 
-        /**
-         *  Initializes the behavior
-         */
-        public init() {
-        }
+    /**
+     *  Initializes the behavior
+     */
+    public init() {
+    }
 
-        /**
-         * Attaches the fade behavior on the passed in mesh
-         * @param ownerNode The mesh that will be faded in/out once attached
-         */
-        public attach(ownerNode: Mesh): void {
-            this._ownerNode = ownerNode;
-            this._setAllVisibility(this._ownerNode, 0);
-        }
-        /**
-         *  Detaches the behavior from the mesh
-         */
-        public detach(): void {
-            this._ownerNode = null;
-        }
+    /**
+     * Attaches the fade behavior on the passed in mesh
+     * @param ownerNode The mesh that will be faded in/out once attached
+     */
+    public attach(ownerNode: Mesh): void {
+        this._ownerNode = ownerNode;
+        this._setAllVisibility(this._ownerNode, 0);
+    }
+    /**
+     *  Detaches the behavior from the mesh
+     */
+    public detach(): void {
+        this._ownerNode = null;
+    }
 
-        /**
-         * Triggers the mesh to begin fading in or out
-         * @param value if the object should fade in or out (true to fade in)
-         */
-        public fadeIn(value: boolean) {
-            this._hovered = value;
-            this._update();
-        }
+    /**
+     * Triggers the mesh to begin fading in or out
+     * @param value if the object should fade in or out (true to fade in)
+     */
+    public fadeIn(value: boolean) {
+        this._hovered = value;
+        this._update();
+    }
 
-        private _update = () => {
-            if (this._ownerNode) {
-                this._hoverValue += this._hovered ? this._millisecondsPerFrame : -this._millisecondsPerFrame;
+    private _update = () => {
+        if (this._ownerNode) {
+            this._hoverValue += this._hovered ? this._millisecondsPerFrame : -this._millisecondsPerFrame;
 
-                this._setAllVisibility(this._ownerNode, (this._hoverValue - this.delay) / this.fadeInTime);
+            this._setAllVisibility(this._ownerNode, (this._hoverValue - this.delay) / this.fadeInTime);
 
-                if (this._ownerNode.visibility > 1) {
-                    this._setAllVisibility(this._ownerNode, 1);
-                    this._hoverValue = this.fadeInTime + this.delay;
+            if (this._ownerNode.visibility > 1) {
+                this._setAllVisibility(this._ownerNode, 1);
+                this._hoverValue = this.fadeInTime + this.delay;
+                return;
+            } else if (this._ownerNode.visibility < 0) {
+                this._setAllVisibility(this._ownerNode, 0);
+                if (this._hoverValue < 0) {
+                    this._hoverValue = 0;
                     return;
-                }else if (this._ownerNode.visibility < 0) {
-                    this._setAllVisibility(this._ownerNode, 0);
-                    if (this._hoverValue < 0) {
-                        this._hoverValue = 0;
-                        return;
-                    }
                 }
-                setTimeout(this._update, this._millisecondsPerFrame);
             }
+            setTimeout(this._update, this._millisecondsPerFrame);
         }
+    }
 
-        private _setAllVisibility(mesh: AbstractMesh, value: number) {
-            mesh.visibility = value;
-            mesh.getChildMeshes().forEach((c) => {
-                this._setAllVisibility(c, value);
-            });
-        }
-
+    private _setAllVisibility(mesh: AbstractMesh, value: number) {
+        mesh.visibility = value;
+        mesh.getChildMeshes().forEach((c) => {
+            this._setAllVisibility(c, value);
+        });
     }
+
+}

+ 87 - 87
src/Behaviors/Meshes/multiPointerScaleBehavior.ts

@@ -6,105 +6,105 @@ import { Nullable } from "../../types";
 import { Observer } from "../../Misc/observable";
 import { Scene } from "../../scene";
 
+/**
+ * A behavior that when attached to a mesh will allow the mesh to be scaled
+ */
+export class MultiPointerScaleBehavior implements Behavior<Mesh> {
+    private _dragBehaviorA: PointerDragBehavior;
+    private _dragBehaviorB: PointerDragBehavior;
+    private _startDistance = 0;
+    private _initialScale = new Vector3(0, 0, 0);
+    private _targetScale = new Vector3(0, 0, 0);
+    private _ownerNode: Mesh;
+    private _sceneRenderObserver: Nullable<Observer<Scene>> = null;
+
     /**
-     * A behavior that when attached to a mesh will allow the mesh to be scaled
+     * Instantiate a new behavior that when attached to a mesh will allow the mesh to be scaled
      */
-    export class MultiPointerScaleBehavior implements Behavior<Mesh> {
-        private _dragBehaviorA: PointerDragBehavior;
-        private _dragBehaviorB: PointerDragBehavior;
-        private _startDistance = 0;
-        private _initialScale = new Vector3(0, 0, 0);
-        private _targetScale = new Vector3(0, 0, 0);
-        private _ownerNode: Mesh;
-        private _sceneRenderObserver: Nullable<Observer<Scene>> = null;
-
-        /**
-         * Instantiate a new behavior that when attached to a mesh will allow the mesh to be scaled
-         */
-        constructor() {
-            this._dragBehaviorA = new PointerDragBehavior({});
-            this._dragBehaviorA.moveAttached = false;
-            this._dragBehaviorB = new PointerDragBehavior({});
-            this._dragBehaviorB.moveAttached = false;
-        }
+    constructor() {
+        this._dragBehaviorA = new PointerDragBehavior({});
+        this._dragBehaviorA.moveAttached = false;
+        this._dragBehaviorB = new PointerDragBehavior({});
+        this._dragBehaviorB.moveAttached = false;
+    }
 
-        /**
-         *  The name of the behavior
-         */
-        public get name(): string {
-            return "MultiPointerScale";
-        }
+    /**
+     *  The name of the behavior
+     */
+    public get name(): string {
+        return "MultiPointerScale";
+    }
 
-        /**
-         *  Initializes the behavior
-         */
-        public init() {}
+    /**
+     *  Initializes the behavior
+     */
+    public init() { }
 
-        private _getCurrentDistance() {
-            return this._dragBehaviorA.lastDragPosition.subtract(this._dragBehaviorB.lastDragPosition).length();
-        }
+    private _getCurrentDistance() {
+        return this._dragBehaviorA.lastDragPosition.subtract(this._dragBehaviorB.lastDragPosition).length();
+    }
 
-        /**
-         * Attaches the scale behavior the passed in mesh
-         * @param ownerNode The mesh that will be scaled around once attached
-         */
-        public attach(ownerNode: Mesh): void {
-            this._ownerNode = ownerNode;
+    /**
+     * Attaches the scale behavior the passed in mesh
+     * @param ownerNode The mesh that will be scaled around once attached
+     */
+    public attach(ownerNode: Mesh): void {
+        this._ownerNode = ownerNode;
 
-            // Create 2 drag behaviors such that each will only be triggered by a separate pointer
-            this._dragBehaviorA.onDragStartObservable.add((e) => {
-                if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
-                    if (this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID) {
-                        this._dragBehaviorA.releaseDrag();
-                    }else {
-                        this._initialScale.copyFrom(ownerNode.scaling);
-                        this._startDistance = this._getCurrentDistance();
-                    }
+        // Create 2 drag behaviors such that each will only be triggered by a separate pointer
+        this._dragBehaviorA.onDragStartObservable.add((e) => {
+            if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
+                if (this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID) {
+                    this._dragBehaviorA.releaseDrag();
+                } else {
+                    this._initialScale.copyFrom(ownerNode.scaling);
+                    this._startDistance = this._getCurrentDistance();
                 }
-            });
-            this._dragBehaviorB.onDragStartObservable.add((e) => {
-                if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
-                    if (this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID) {
-                        this._dragBehaviorB.releaseDrag();
-                    }else {
-                        this._initialScale.copyFrom(ownerNode.scaling);
-                        this._startDistance = this._getCurrentDistance();
-                    }
+            }
+        });
+        this._dragBehaviorB.onDragStartObservable.add((e) => {
+            if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
+                if (this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID) {
+                    this._dragBehaviorB.releaseDrag();
+                } else {
+                    this._initialScale.copyFrom(ownerNode.scaling);
+                    this._startDistance = this._getCurrentDistance();
                 }
-            });
+            }
+        });
 
-            // Once both drag behaviors are active scale based on the distance between the two pointers
-            [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior) => {
-                behavior.onDragObservable.add(() => {
-                    if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
-                        var ratio = this._getCurrentDistance() / this._startDistance;
-                        this._initialScale.scaleToRef(ratio, this._targetScale);
-                    }
-                });
+        // Once both drag behaviors are active scale based on the distance between the two pointers
+        [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior) => {
+            behavior.onDragObservable.add(() => {
+                if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
+                    var ratio = this._getCurrentDistance() / this._startDistance;
+                    this._initialScale.scaleToRef(ratio, this._targetScale);
+                }
             });
+        });
 
-            ownerNode.addBehavior(this._dragBehaviorA);
-            ownerNode.addBehavior(this._dragBehaviorB);
+        ownerNode.addBehavior(this._dragBehaviorA);
+        ownerNode.addBehavior(this._dragBehaviorB);
 
-            // On every frame move towards target scaling to avoid jitter caused by vr controllers
-            this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(() => {
-                if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
-                    var change = this._targetScale.subtract(ownerNode.scaling).scaleInPlace(0.1);
-                    if (change.length() > 0.01) {
-                        ownerNode.scaling.addInPlace(change);
-                    }
+        // On every frame move towards target scaling to avoid jitter caused by vr controllers
+        this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(() => {
+            if (this._dragBehaviorA.dragging && this._dragBehaviorB.dragging) {
+                var change = this._targetScale.subtract(ownerNode.scaling).scaleInPlace(0.1);
+                if (change.length() > 0.01) {
+                    ownerNode.scaling.addInPlace(change);
                 }
-            });
-        }
-        /**
-         *  Detaches the behavior from the mesh
-         */
-        public detach(): void {
-            this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
-            [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior) => {
-                behavior.onDragStartObservable.clear();
-                behavior.onDragObservable.clear();
-                this._ownerNode.removeBehavior(behavior);
-            });
-        }
+            }
+        });
+    }
+    /**
+     *  Detaches the behavior from the mesh
+     */
+    public detach(): void {
+        this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
+        [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior) => {
+            behavior.onDragStartObservable.clear();
+            behavior.onDragObservable.clear();
+            this._ownerNode.removeBehavior(behavior);
+        });
     }
+}

+ 359 - 359
src/Behaviors/Meshes/pointerDragBehavior.ts

@@ -9,409 +9,409 @@ import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
 import { Ray } from "../../Culling/ray";
 import { PivotTools } from '../../Misc/pivotTools';
 
+/**
+ * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
+ */
+export class PointerDragBehavior implements Behavior<AbstractMesh> {
+    private static _AnyMouseID = -2;
+    private _attachedNode: AbstractMesh;
+    private _dragPlane: Mesh;
+    private _scene: Scene;
+    private _pointerObserver: Nullable<Observer<PointerInfo>>;
+    private _beforeRenderObserver: Nullable<Observer<Scene>>;
+    private static _planeScene: Scene;
     /**
-     * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
+     * The maximum tolerated angle between the drag plane and dragging pointer rays to trigger pointer events. Set to 0 to allow any angle (default: 0)
      */
-    export class PointerDragBehavior implements Behavior<AbstractMesh> {
-        private static _AnyMouseID = -2;
-        private _attachedNode: AbstractMesh;
-        private _dragPlane: Mesh;
-        private _scene: Scene;
-        private _pointerObserver: Nullable<Observer<PointerInfo>>;
-        private _beforeRenderObserver: Nullable<Observer<Scene>>;
-        private static _planeScene: Scene;
-        /**
-         * The maximum tolerated angle between the drag plane and dragging pointer rays to trigger pointer events. Set to 0 to allow any angle (default: 0)
-         */
-        public maxDragAngle = 0;
-        /**
-         * @hidden
-         */
-        public _useAlternatePickedPointAboveMaxDragAngle = false;
-        /**
-         * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
-         */
-        public currentDraggingPointerID = -1;
-        /**
-         * The last position where the pointer hit the drag plane in world space
-         */
-        public lastDragPosition: Vector3;
-        /**
-         * If the behavior is currently in a dragging state
-         */
-        public dragging = false;
-        /**
-         * The distance towards the target drag position to move each frame. This can be useful to avoid jitter. Set this to 1 for no delay. (Default: 0.2)
-         */
-        public dragDeltaRatio = 0.2;
-        /**
-         * If the drag plane orientation should be updated during the dragging (Default: true)
-         */
-        public updateDragPlane = true;
-        // Debug mode will display drag planes to help visualize behavior
-        private _debugMode = false;
-        private _moving = false;
-        /**
-         *  Fires each time the attached mesh is dragged with the pointer
-         *  * delta between last drag position and current drag position in world space
-         *  * dragDistance along the drag axis
-         *  * dragPlaneNormal normal of the current drag plane used during the drag
-         *  * dragPlanePoint in world space where the drag intersects the drag plane
-         */
-        public onDragObservable = new Observable<{ delta: Vector3, dragPlanePoint: Vector3, dragPlaneNormal: Vector3, dragDistance: number, pointerId: number }>();
-        /**
-         *  Fires each time a drag begins (eg. mouse down on mesh)
-         */
-        public onDragStartObservable = new Observable<{ dragPlanePoint: Vector3, pointerId: number }>();
-        /**
-         *  Fires each time a drag ends (eg. mouse release after drag)
-         */
-        public onDragEndObservable = new Observable<{ dragPlanePoint: Vector3, pointerId: number }>();
-        /**
-         *  If the attached mesh should be moved when dragged
-         */
-        public moveAttached = true;
-
-        /**
-         *  If the drag behavior will react to drag events (Default: true)
-         */
-        public enabled = true;
-        /**
-         * If camera controls should be detached during the drag
-         */
-        public detachCameraControls = true;
-
-        /**
-         * If set, the drag plane/axis will be rotated based on the attached mesh's world rotation (Default: true)
-         */
-        public useObjectOrienationForDragging = true;
-
-        private _options: { dragAxis?: Vector3, dragPlaneNormal?: Vector3 };
-        /**
-         * Creates a pointer drag behavior that can be attached to a mesh
-         * @param options The drag axis or normal of the plane that will be dragged across. If no options are specified the drag plane will always face the ray's origin (eg. camera)
-         */
-        constructor(options?: { dragAxis?: Vector3, dragPlaneNormal?: Vector3 }) {
-            this._options = options ? options : {};
-
-            var optionCount = 0;
-            if (this._options.dragAxis) {
-                optionCount++;
-            }
-            if (this._options.dragPlaneNormal) {
-                optionCount++;
-            }
-            if (optionCount > 1) {
-                throw "Multiple drag modes specified in dragBehavior options. Only one expected";
-            }
-        }
+    public maxDragAngle = 0;
+    /**
+     * @hidden
+     */
+    public _useAlternatePickedPointAboveMaxDragAngle = false;
+    /**
+     * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
+     */
+    public currentDraggingPointerID = -1;
+    /**
+     * The last position where the pointer hit the drag plane in world space
+     */
+    public lastDragPosition: Vector3;
+    /**
+     * If the behavior is currently in a dragging state
+     */
+    public dragging = false;
+    /**
+     * The distance towards the target drag position to move each frame. This can be useful to avoid jitter. Set this to 1 for no delay. (Default: 0.2)
+     */
+    public dragDeltaRatio = 0.2;
+    /**
+     * If the drag plane orientation should be updated during the dragging (Default: true)
+     */
+    public updateDragPlane = true;
+    // Debug mode will display drag planes to help visualize behavior
+    private _debugMode = false;
+    private _moving = false;
+    /**
+     *  Fires each time the attached mesh is dragged with the pointer
+     *  * delta between last drag position and current drag position in world space
+     *  * dragDistance along the drag axis
+     *  * dragPlaneNormal normal of the current drag plane used during the drag
+     *  * dragPlanePoint in world space where the drag intersects the drag plane
+     */
+    public onDragObservable = new Observable<{ delta: Vector3, dragPlanePoint: Vector3, dragPlaneNormal: Vector3, dragDistance: number, pointerId: number }>();
+    /**
+     *  Fires each time a drag begins (eg. mouse down on mesh)
+     */
+    public onDragStartObservable = new Observable<{ dragPlanePoint: Vector3, pointerId: number }>();
+    /**
+     *  Fires each time a drag ends (eg. mouse release after drag)
+     */
+    public onDragEndObservable = new Observable<{ dragPlanePoint: Vector3, pointerId: number }>();
+    /**
+     *  If the attached mesh should be moved when dragged
+     */
+    public moveAttached = true;
 
-        /**
-         * Predicate to determine if it is valid to move the object to a new position when it is moved
-         */
-        public validateDrag = (targetPosition: Vector3) => {return true; };
+    /**
+     *  If the drag behavior will react to drag events (Default: true)
+     */
+    public enabled = true;
+    /**
+     * If camera controls should be detached during the drag
+     */
+    public detachCameraControls = true;
 
-        /**
-         *  The name of the behavior
-         */
-        public get name(): string {
-            return "PointerDrag";
+    /**
+     * If set, the drag plane/axis will be rotated based on the attached mesh's world rotation (Default: true)
+     */
+    public useObjectOrienationForDragging = true;
+
+    private _options: { dragAxis?: Vector3, dragPlaneNormal?: Vector3 };
+    /**
+     * Creates a pointer drag behavior that can be attached to a mesh
+     * @param options The drag axis or normal of the plane that will be dragged across. If no options are specified the drag plane will always face the ray's origin (eg. camera)
+     */
+    constructor(options?: { dragAxis?: Vector3, dragPlaneNormal?: Vector3 }) {
+        this._options = options ? options : {};
+
+        var optionCount = 0;
+        if (this._options.dragAxis) {
+            optionCount++;
+        }
+        if (this._options.dragPlaneNormal) {
+            optionCount++;
         }
+        if (optionCount > 1) {
+            throw "Multiple drag modes specified in dragBehavior options. Only one expected";
+        }
+    }
 
-        /**
-         *  Initializes the behavior
-         */
-        public init() { }
-
-        private _tmpVector = new Vector3(0, 0, 0);
-        private _alternatePickedPoint = new Vector3(0, 0, 0);
-        private _worldDragAxis = new Vector3(0, 0, 0);
-        private _targetPosition = new Vector3(0, 0, 0);
-        private _attachedElement: Nullable<HTMLElement> = null;
-        /**
-         * Attaches the drag behavior the passed in mesh
-         * @param ownerNode The mesh that will be dragged around once attached
-         */
-        public attach(ownerNode: AbstractMesh): void {
-            this._scene = ownerNode.getScene();
-            this._attachedNode = ownerNode;
-
-            // Initialize drag plane to not interfere with existing scene
-            if (!PointerDragBehavior._planeScene) {
-                if (this._debugMode) {
-                    PointerDragBehavior._planeScene = this._scene;
-                } else {
-                    PointerDragBehavior._planeScene = new Scene(this._scene.getEngine());
-                    PointerDragBehavior._planeScene.detachControl();
-                    this._scene.getEngine().scenes.pop();
-                    this._scene.onDisposeObservable.addOnce(() => {
-                        PointerDragBehavior._planeScene.dispose();
-                        (<any>PointerDragBehavior._planeScene) = null;
-                    });
-                }
-            }
-            this._dragPlane = Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, Mesh.DOUBLESIDE);
+    /**
+     * Predicate to determine if it is valid to move the object to a new position when it is moved
+     */
+    public validateDrag = (targetPosition: Vector3) => { return true; };
 
-            // State of the drag
-            this.lastDragPosition = new Vector3(0, 0, 0);
+    /**
+     *  The name of the behavior
+     */
+    public get name(): string {
+        return "PointerDrag";
+    }
 
-            var pickPredicate = (m: AbstractMesh) => {
-                return this._attachedNode == m || m.isDescendantOf(this._attachedNode);
-            };
+    /**
+     *  Initializes the behavior
+     */
+    public init() { }
 
-            this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState) => {
-                if (!this.enabled) {
-                    return;
-                }
+    private _tmpVector = new Vector3(0, 0, 0);
+    private _alternatePickedPoint = new Vector3(0, 0, 0);
+    private _worldDragAxis = new Vector3(0, 0, 0);
+    private _targetPosition = new Vector3(0, 0, 0);
+    private _attachedElement: Nullable<HTMLElement> = null;
+    /**
+     * Attaches the drag behavior the passed in mesh
+     * @param ownerNode The mesh that will be dragged around once attached
+     */
+    public attach(ownerNode: AbstractMesh): void {
+        this._scene = ownerNode.getScene();
+        this._attachedNode = ownerNode;
+
+        // Initialize drag plane to not interfere with existing scene
+        if (!PointerDragBehavior._planeScene) {
+            if (this._debugMode) {
+                PointerDragBehavior._planeScene = this._scene;
+            } else {
+                PointerDragBehavior._planeScene = new Scene(this._scene.getEngine());
+                PointerDragBehavior._planeScene.detachControl();
+                this._scene.getEngine().scenes.pop();
+                this._scene.onDisposeObservable.addOnce(() => {
+                    PointerDragBehavior._planeScene.dispose();
+                    (<any>PointerDragBehavior._planeScene) = null;
+                });
+            }
+        }
+        this._dragPlane = Mesh.CreatePlane("pointerDragPlane", this._debugMode ? 1 : 10000, PointerDragBehavior._planeScene, false, Mesh.DOUBLESIDE);
 
-                if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
+        // State of the drag
+        this.lastDragPosition = new Vector3(0, 0, 0);
 
-                    if (!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.pickedPoint && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
-                        this._startDrag((<PointerEvent>pointerInfo.event).pointerId, pointerInfo.pickInfo.ray, pointerInfo.pickInfo.pickedPoint);
-                    }
-                } else if (pointerInfo.type == PointerEventTypes.POINTERUP) {
-                    if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId) {
-                        this.releaseDrag();
-                    }
-                } else if (pointerInfo.type == PointerEventTypes.POINTERMOVE) {
-                    var pointerId = (<PointerEvent>pointerInfo.event).pointerId;
-
-                    // If drag was started with anyMouseID specified, set pointerID to the next mouse that moved
-                    if (this.currentDraggingPointerID === PointerDragBehavior._AnyMouseID && pointerId !== PointerDragBehavior._AnyMouseID && (<PointerEvent>pointerInfo.event).pointerType == "mouse") {
-                        if (this._lastPointerRay[this.currentDraggingPointerID]) {
-                            this._lastPointerRay[pointerId] = this._lastPointerRay[this.currentDraggingPointerID];
-                            delete this._lastPointerRay[this.currentDraggingPointerID];
-                        }
-                        this.currentDraggingPointerID = pointerId;
-                    }
+        var pickPredicate = (m: AbstractMesh) => {
+            return this._attachedNode == m || m.isDescendantOf(this._attachedNode);
+        };
 
-                    // Keep track of last pointer ray, this is used simulating the start of a drag in startDrag()
-                    if (!this._lastPointerRay[pointerId]) {
-                        this._lastPointerRay[pointerId] = new Ray(new Vector3(), new Vector3());
-                    }
-                    if (pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
-                        this._lastPointerRay[pointerId].origin.copyFrom(pointerInfo.pickInfo.ray.origin);
-                        this._lastPointerRay[pointerId].direction.copyFrom(pointerInfo.pickInfo.ray.direction);
+        this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState) => {
+            if (!this.enabled) {
+                return;
+            }
 
-                        if (this.currentDraggingPointerID == pointerId && this.dragging) {
-                            this._moveDrag(pointerInfo.pickInfo.ray);
-                        }
-                    }
+            if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
+
+                if (!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.pickedPoint && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
+                    this._startDrag((<PointerEvent>pointerInfo.event).pointerId, pointerInfo.pickInfo.ray, pointerInfo.pickInfo.pickedPoint);
+                }
+            } else if (pointerInfo.type == PointerEventTypes.POINTERUP) {
+                if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId) {
+                    this.releaseDrag();
                 }
-            });
-
-            this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add(() => {
-                if (this._moving && this.moveAttached) {
-                    PivotTools._RemoveAndStorePivotPoint(this._attachedNode);
-                    // Slowly move mesh to avoid jitter
-                    this._targetPosition.subtractToRef((this._attachedNode).absolutePosition, this._tmpVector);
-                    this._tmpVector.scaleInPlace(this.dragDeltaRatio);
-                    (this._attachedNode).getAbsolutePosition().addToRef(this._tmpVector, this._tmpVector);
-                    if (this.validateDrag(this._tmpVector)) {
-                        (this._attachedNode).setAbsolutePosition(this._tmpVector);
+            } else if (pointerInfo.type == PointerEventTypes.POINTERMOVE) {
+                var pointerId = (<PointerEvent>pointerInfo.event).pointerId;
+
+                // If drag was started with anyMouseID specified, set pointerID to the next mouse that moved
+                if (this.currentDraggingPointerID === PointerDragBehavior._AnyMouseID && pointerId !== PointerDragBehavior._AnyMouseID && (<PointerEvent>pointerInfo.event).pointerType == "mouse") {
+                    if (this._lastPointerRay[this.currentDraggingPointerID]) {
+                        this._lastPointerRay[pointerId] = this._lastPointerRay[this.currentDraggingPointerID];
+                        delete this._lastPointerRay[this.currentDraggingPointerID];
                     }
-                    PivotTools._RestorePivotPoint(this._attachedNode);
+                    this.currentDraggingPointerID = pointerId;
                 }
-            });
-        }
 
-        /**
-         * Force relase the drag action by code.
-         */
-        public releaseDrag() {
-            this.dragging = false;
-            this.onDragEndObservable.notifyObservers({ dragPlanePoint: this.lastDragPosition, pointerId: this.currentDraggingPointerID });
-            this.currentDraggingPointerID = -1;
-            this._moving = false;
-
-            // Reattach camera controls
-            if (this.detachCameraControls && this._attachedElement && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
-                this._scene.activeCamera.attachControl(this._attachedElement, true);
-            }
-        }
+                // Keep track of last pointer ray, this is used simulating the start of a drag in startDrag()
+                if (!this._lastPointerRay[pointerId]) {
+                    this._lastPointerRay[pointerId] = new Ray(new Vector3(), new Vector3());
+                }
+                if (pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                    this._lastPointerRay[pointerId].origin.copyFrom(pointerInfo.pickInfo.ray.origin);
+                    this._lastPointerRay[pointerId].direction.copyFrom(pointerInfo.pickInfo.ray.direction);
 
-        private _startDragRay = new Ray(new Vector3(), new Vector3());
-        private _lastPointerRay: { [key: number]: Ray } = {};
-        /**
-         * Simulates the start of a pointer drag event on the behavior
-         * @param pointerId pointerID of the pointer that should be simulated (Default: Any mouse pointer ID)
-         * @param fromRay initial ray of the pointer to be simulated (Default: Ray from camera to attached mesh)
-         * @param startPickedPoint picked point of the pointer to be simulated (Default: attached mesh position)
-         */
-        public startDrag(pointerId: number = PointerDragBehavior._AnyMouseID, fromRay?: Ray, startPickedPoint?: Vector3) {
-            this._startDrag(pointerId, fromRay, startPickedPoint);
-
-            var lastRay = this._lastPointerRay[pointerId];
-            if (pointerId === PointerDragBehavior._AnyMouseID) {
-                lastRay = this._lastPointerRay[<any>Object.keys(this._lastPointerRay)[0]];
+                    if (this.currentDraggingPointerID == pointerId && this.dragging) {
+                        this._moveDrag(pointerInfo.pickInfo.ray);
+                    }
+                }
             }
-
-            if (lastRay) {
-                // if there was a last pointer ray drag the object there
-                this._moveDrag(lastRay);
+        });
+
+        this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add(() => {
+            if (this._moving && this.moveAttached) {
+                PivotTools._RemoveAndStorePivotPoint(this._attachedNode);
+                // Slowly move mesh to avoid jitter
+                this._targetPosition.subtractToRef((this._attachedNode).absolutePosition, this._tmpVector);
+                this._tmpVector.scaleInPlace(this.dragDeltaRatio);
+                (this._attachedNode).getAbsolutePosition().addToRef(this._tmpVector, this._tmpVector);
+                if (this.validateDrag(this._tmpVector)) {
+                    (this._attachedNode).setAbsolutePosition(this._tmpVector);
+                }
+                PivotTools._RestorePivotPoint(this._attachedNode);
             }
-        }
+        });
+    }
 
-        private _startDrag(pointerId: number, fromRay?: Ray, startPickedPoint?: Vector3) {
-            if (!this._scene.activeCamera || this.dragging || !this._attachedNode) {
-                return;
-            }
+    /**
+     * Force relase the drag action by code.
+     */
+    public releaseDrag() {
+        this.dragging = false;
+        this.onDragEndObservable.notifyObservers({ dragPlanePoint: this.lastDragPosition, pointerId: this.currentDraggingPointerID });
+        this.currentDraggingPointerID = -1;
+        this._moving = false;
+
+        // Reattach camera controls
+        if (this.detachCameraControls && this._attachedElement && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
+            this._scene.activeCamera.attachControl(this._attachedElement, true);
+        }
+    }
 
-            PivotTools._RemoveAndStorePivotPoint(this._attachedNode);
-            // Create start ray from the camera to the object
-            if (fromRay) {
-                this._startDragRay.direction.copyFrom(fromRay.direction);
-                this._startDragRay.origin.copyFrom(fromRay.origin);
-            } else {
-                this._startDragRay.origin.copyFrom(this._scene.activeCamera.position);
-                this._attachedNode.getWorldMatrix().getTranslationToRef(this._tmpVector);
-                this._tmpVector.subtractToRef(this._scene.activeCamera.position, this._startDragRay.direction);
-            }
+    private _startDragRay = new Ray(new Vector3(), new Vector3());
+    private _lastPointerRay: { [key: number]: Ray } = {};
+    /**
+     * Simulates the start of a pointer drag event on the behavior
+     * @param pointerId pointerID of the pointer that should be simulated (Default: Any mouse pointer ID)
+     * @param fromRay initial ray of the pointer to be simulated (Default: Ray from camera to attached mesh)
+     * @param startPickedPoint picked point of the pointer to be simulated (Default: attached mesh position)
+     */
+    public startDrag(pointerId: number = PointerDragBehavior._AnyMouseID, fromRay?: Ray, startPickedPoint?: Vector3) {
+        this._startDrag(pointerId, fromRay, startPickedPoint);
 
-            this._updateDragPlanePosition(this._startDragRay, startPickedPoint ? startPickedPoint : this._tmpVector);
-
-            var pickedPoint = this._pickWithRayOnDragPlane(this._startDragRay);
-            if (pickedPoint) {
-                this.dragging = true;
-                this.currentDraggingPointerID = pointerId;
-                this.lastDragPosition.copyFrom(pickedPoint);
-                this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint, pointerId: this.currentDraggingPointerID });
-                this._targetPosition.copyFrom((this._attachedNode).absolutePosition);
-
-                // Detatch camera controls
-                if (this.detachCameraControls && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
-                    if (this._scene.activeCamera.inputs.attachedElement) {
-                        this._attachedElement = this._scene.activeCamera.inputs.attachedElement;
-                        this._scene.activeCamera.detachControl(this._scene.activeCamera.inputs.attachedElement);
-                    } else {
-                        this._attachedElement = null;
-                    }
-                }
-            }
-            PivotTools._RestorePivotPoint(this._attachedNode);
+        var lastRay = this._lastPointerRay[pointerId];
+        if (pointerId === PointerDragBehavior._AnyMouseID) {
+            lastRay = this._lastPointerRay[<any>Object.keys(this._lastPointerRay)[0]];
         }
 
-        private _dragDelta = new Vector3();
-        private _moveDrag(ray: Ray) {
-            this._moving = true;
-            var pickedPoint = this._pickWithRayOnDragPlane(ray);
+        if (lastRay) {
+            // if there was a last pointer ray drag the object there
+            this._moveDrag(lastRay);
+        }
+    }
 
-            if (pickedPoint) {
-                if (this.updateDragPlane) {
-                    this._updateDragPlanePosition(ray, pickedPoint);
-                }
+    private _startDrag(pointerId: number, fromRay?: Ray, startPickedPoint?: Vector3) {
+        if (!this._scene.activeCamera || this.dragging || !this._attachedNode) {
+            return;
+        }
 
-                var dragLength = 0;
-                // depending on the drag mode option drag accordingly
-                if (this._options.dragAxis) {
-                    // Convert local drag axis to world
-                    Vector3.TransformCoordinatesToRef(this._options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._worldDragAxis);
+        PivotTools._RemoveAndStorePivotPoint(this._attachedNode);
+        // Create start ray from the camera to the object
+        if (fromRay) {
+            this._startDragRay.direction.copyFrom(fromRay.direction);
+            this._startDragRay.origin.copyFrom(fromRay.origin);
+        } else {
+            this._startDragRay.origin.copyFrom(this._scene.activeCamera.position);
+            this._attachedNode.getWorldMatrix().getTranslationToRef(this._tmpVector);
+            this._tmpVector.subtractToRef(this._scene.activeCamera.position, this._startDragRay.direction);
+        }
 
-                    // Project delta drag from the drag plane onto the drag axis
-                    pickedPoint.subtractToRef(this.lastDragPosition, this._tmpVector);
-                    dragLength = Vector3.Dot(this._tmpVector, this._worldDragAxis);
-                    this._worldDragAxis.scaleToRef(dragLength, this._dragDelta);
+        this._updateDragPlanePosition(this._startDragRay, startPickedPoint ? startPickedPoint : this._tmpVector);
+
+        var pickedPoint = this._pickWithRayOnDragPlane(this._startDragRay);
+        if (pickedPoint) {
+            this.dragging = true;
+            this.currentDraggingPointerID = pointerId;
+            this.lastDragPosition.copyFrom(pickedPoint);
+            this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint, pointerId: this.currentDraggingPointerID });
+            this._targetPosition.copyFrom((this._attachedNode).absolutePosition);
+
+            // Detatch camera controls
+            if (this.detachCameraControls && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
+                if (this._scene.activeCamera.inputs.attachedElement) {
+                    this._attachedElement = this._scene.activeCamera.inputs.attachedElement;
+                    this._scene.activeCamera.detachControl(this._scene.activeCamera.inputs.attachedElement);
                 } else {
-                    dragLength = this._dragDelta.length();
-                    pickedPoint.subtractToRef(this.lastDragPosition, this._dragDelta);
+                    this._attachedElement = null;
                 }
-                this._targetPosition.addInPlace(this._dragDelta);
-                this.onDragObservable.notifyObservers({ dragDistance: dragLength, delta: this._dragDelta, dragPlanePoint: pickedPoint, dragPlaneNormal: this._dragPlane.forward, pointerId: this.currentDraggingPointerID });
-                this.lastDragPosition.copyFrom(pickedPoint);
             }
         }
+        PivotTools._RestorePivotPoint(this._attachedNode);
+    }
 
-        private _pickWithRayOnDragPlane(ray: Nullable<Ray>) {
-            if (!ray) {
-                return null;
-            }
+    private _dragDelta = new Vector3();
+    private _moveDrag(ray: Ray) {
+        this._moving = true;
+        var pickedPoint = this._pickWithRayOnDragPlane(ray);
 
-            // Calculate angle between plane normal and ray
-            var angle = Math.acos(Vector3.Dot(this._dragPlane.forward, ray.direction));
-            // Correct if ray is casted from oposite side
-            if (angle > Math.PI / 2) {
-                angle = Math.PI - angle;
+        if (pickedPoint) {
+            if (this.updateDragPlane) {
+                this._updateDragPlanePosition(ray, pickedPoint);
             }
 
-            // If the angle is too perpendicular to the plane pick another point on the plane where it is looking
-            if (this.maxDragAngle > 0 && angle > this.maxDragAngle) {
-                if (this._useAlternatePickedPointAboveMaxDragAngle) {
-                    // Invert ray direction along the towards object axis
-                    this._tmpVector.copyFrom(ray.direction);
-                    (this._attachedNode).absolutePosition.subtractToRef(ray.origin, this._alternatePickedPoint);
-                    this._alternatePickedPoint.normalize();
-                    this._alternatePickedPoint.scaleInPlace(-2 * Vector3.Dot(this._alternatePickedPoint, this._tmpVector));
-                    this._tmpVector.addInPlace(this._alternatePickedPoint);
-
-                    // Project resulting vector onto the drag plane and add it to the attached nodes absolute position to get a picked point
-                    var dot = Vector3.Dot(this._dragPlane.forward, this._tmpVector);
-                    this._dragPlane.forward.scaleToRef(-dot, this._alternatePickedPoint);
-                    this._alternatePickedPoint.addInPlace(this._tmpVector);
-                    this._alternatePickedPoint.addInPlace((this._attachedNode).absolutePosition);
-                    return this._alternatePickedPoint;
-                } else {
-                    return null;
-                }
-            }
+            var dragLength = 0;
+            // depending on the drag mode option drag accordingly
+            if (this._options.dragAxis) {
+                // Convert local drag axis to world
+                Vector3.TransformCoordinatesToRef(this._options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._worldDragAxis);
 
-            var pickResult = PointerDragBehavior._planeScene.pickWithRay(ray, (m) => { return m == this._dragPlane; });
-            if (pickResult && pickResult.hit && pickResult.pickedMesh && pickResult.pickedPoint) {
-                return pickResult.pickedPoint;
+                // Project delta drag from the drag plane onto the drag axis
+                pickedPoint.subtractToRef(this.lastDragPosition, this._tmpVector);
+                dragLength = Vector3.Dot(this._tmpVector, this._worldDragAxis);
+                this._worldDragAxis.scaleToRef(dragLength, this._dragDelta);
             } else {
-                return null;
+                dragLength = this._dragDelta.length();
+                pickedPoint.subtractToRef(this.lastDragPosition, this._dragDelta);
             }
+            this._targetPosition.addInPlace(this._dragDelta);
+            this.onDragObservable.notifyObservers({ dragDistance: dragLength, delta: this._dragDelta, dragPlanePoint: pickedPoint, dragPlaneNormal: this._dragPlane.forward, pointerId: this.currentDraggingPointerID });
+            this.lastDragPosition.copyFrom(pickedPoint);
         }
+    }
 
-        // Variables to avoid instantiation in the below method
-        private _pointA = new Vector3(0, 0, 0);
-        private _pointB = new Vector3(0, 0, 0);
-        private _pointC = new Vector3(0, 0, 0);
-        private _lineA = new Vector3(0, 0, 0);
-        private _lineB = new Vector3(0, 0, 0);
-        private _localAxis = new Vector3(0, 0, 0);
-        private _lookAt = new Vector3(0, 0, 0);
-        // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
-        private _updateDragPlanePosition(ray: Ray, dragPlanePosition: Vector3) {
-            this._pointA.copyFrom(dragPlanePosition);
-            if (this._options.dragAxis) {
-                this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this._options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._localAxis) : this._localAxis.copyFrom(this._options.dragAxis);
-
-                // Calculate plane normal in direction of camera but perpendicular to drag axis
-                this._pointA.addToRef(this._localAxis, this._pointB); // towards drag axis
-                ray.origin.subtractToRef(this._pointA, this._pointC);
-                this._pointA.addToRef(this._pointC.normalize(), this._pointC); // towards camera
-                // Get perpendicular line from direction to camera and drag axis
-                this._pointB.subtractToRef(this._pointA, this._lineA);
-                this._pointC.subtractToRef(this._pointA, this._lineB);
-                Vector3.CrossToRef(this._lineA, this._lineB, this._lookAt);
-                // Get perpendicular line from previous result and drag axis to adjust lineB to be perpendiculat to camera
-                Vector3.CrossToRef(this._lineA, this._lookAt, this._lookAt);
-                this._lookAt.normalize();
-
-                this._dragPlane.position.copyFrom(this._pointA);
-                this._pointA.addToRef(this._lookAt, this._lookAt);
-                this._dragPlane.lookAt(this._lookAt);
-            } else if (this._options.dragPlaneNormal) {
-                this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this._options.dragPlaneNormal, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._localAxis) : this._localAxis.copyFrom(this._options.dragPlaneNormal);
-                this._dragPlane.position.copyFrom(this._pointA);
-                this._pointA.addToRef(this._localAxis, this._lookAt);
-                this._dragPlane.lookAt(this._lookAt);
+    private _pickWithRayOnDragPlane(ray: Nullable<Ray>) {
+        if (!ray) {
+            return null;
+        }
+
+        // Calculate angle between plane normal and ray
+        var angle = Math.acos(Vector3.Dot(this._dragPlane.forward, ray.direction));
+        // Correct if ray is casted from oposite side
+        if (angle > Math.PI / 2) {
+            angle = Math.PI - angle;
+        }
+
+        // If the angle is too perpendicular to the plane pick another point on the plane where it is looking
+        if (this.maxDragAngle > 0 && angle > this.maxDragAngle) {
+            if (this._useAlternatePickedPointAboveMaxDragAngle) {
+                // Invert ray direction along the towards object axis
+                this._tmpVector.copyFrom(ray.direction);
+                (this._attachedNode).absolutePosition.subtractToRef(ray.origin, this._alternatePickedPoint);
+                this._alternatePickedPoint.normalize();
+                this._alternatePickedPoint.scaleInPlace(-2 * Vector3.Dot(this._alternatePickedPoint, this._tmpVector));
+                this._tmpVector.addInPlace(this._alternatePickedPoint);
+
+                // Project resulting vector onto the drag plane and add it to the attached nodes absolute position to get a picked point
+                var dot = Vector3.Dot(this._dragPlane.forward, this._tmpVector);
+                this._dragPlane.forward.scaleToRef(-dot, this._alternatePickedPoint);
+                this._alternatePickedPoint.addInPlace(this._tmpVector);
+                this._alternatePickedPoint.addInPlace((this._attachedNode).absolutePosition);
+                return this._alternatePickedPoint;
             } else {
-                this._dragPlane.position.copyFrom(this._pointA);
-                this._dragPlane.lookAt(ray.origin);
+                return null;
             }
-            this._dragPlane.computeWorldMatrix(true);
         }
 
-        /**
-         *  Detaches the behavior from the mesh
-         */
-        public detach(): void {
-            if (this._pointerObserver) {
-                this._scene.onPointerObservable.remove(this._pointerObserver);
-            }
-            if (this._beforeRenderObserver) {
-                this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
-            }
+        var pickResult = PointerDragBehavior._planeScene.pickWithRay(ray, (m) => { return m == this._dragPlane; });
+        if (pickResult && pickResult.hit && pickResult.pickedMesh && pickResult.pickedPoint) {
+            return pickResult.pickedPoint;
+        } else {
+            return null;
+        }
+    }
+
+    // Variables to avoid instantiation in the below method
+    private _pointA = new Vector3(0, 0, 0);
+    private _pointB = new Vector3(0, 0, 0);
+    private _pointC = new Vector3(0, 0, 0);
+    private _lineA = new Vector3(0, 0, 0);
+    private _lineB = new Vector3(0, 0, 0);
+    private _localAxis = new Vector3(0, 0, 0);
+    private _lookAt = new Vector3(0, 0, 0);
+    // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
+    private _updateDragPlanePosition(ray: Ray, dragPlanePosition: Vector3) {
+        this._pointA.copyFrom(dragPlanePosition);
+        if (this._options.dragAxis) {
+            this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this._options.dragAxis, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._localAxis) : this._localAxis.copyFrom(this._options.dragAxis);
+
+            // Calculate plane normal in direction of camera but perpendicular to drag axis
+            this._pointA.addToRef(this._localAxis, this._pointB); // towards drag axis
+            ray.origin.subtractToRef(this._pointA, this._pointC);
+            this._pointA.addToRef(this._pointC.normalize(), this._pointC); // towards camera
+            // Get perpendicular line from direction to camera and drag axis
+            this._pointB.subtractToRef(this._pointA, this._lineA);
+            this._pointC.subtractToRef(this._pointA, this._lineB);
+            Vector3.CrossToRef(this._lineA, this._lineB, this._lookAt);
+            // Get perpendicular line from previous result and drag axis to adjust lineB to be perpendiculat to camera
+            Vector3.CrossToRef(this._lineA, this._lookAt, this._lookAt);
+            this._lookAt.normalize();
+
+            this._dragPlane.position.copyFrom(this._pointA);
+            this._pointA.addToRef(this._lookAt, this._lookAt);
+            this._dragPlane.lookAt(this._lookAt);
+        } else if (this._options.dragPlaneNormal) {
+            this.useObjectOrienationForDragging ? Vector3.TransformCoordinatesToRef(this._options.dragPlaneNormal, this._attachedNode.getWorldMatrix().getRotationMatrix(), this._localAxis) : this._localAxis.copyFrom(this._options.dragPlaneNormal);
+            this._dragPlane.position.copyFrom(this._pointA);
+            this._pointA.addToRef(this._localAxis, this._lookAt);
+            this._dragPlane.lookAt(this._lookAt);
+        } else {
+            this._dragPlane.position.copyFrom(this._pointA);
+            this._dragPlane.lookAt(ray.origin);
+        }
+        this._dragPlane.computeWorldMatrix(true);
+    }
+
+    /**
+     *  Detaches the behavior from the mesh
+     */
+    public detach(): void {
+        if (this._pointerObserver) {
+            this._scene.onPointerObservable.remove(this._pointerObserver);
+        }
+        if (this._beforeRenderObserver) {
+            this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
         }
     }
+}

+ 217 - 217
src/Behaviors/Meshes/sixDofDragBehavior.ts

@@ -8,244 +8,244 @@ import { Vector3, Quaternion, Matrix } from "../../Maths/math";
 import { Observer, Observable } from "../../Misc/observable";
 import { Camera } from "../../Cameras/camera";
 import { PivotTools } from "../../Misc/pivotTools";
+/**
+ * A behavior that when attached to a mesh will allow the mesh to be dragged around based on directions and origin of the pointer's ray
+ */
+export class SixDofDragBehavior implements Behavior<Mesh> {
+    private static _virtualScene: Scene;
+    private _ownerNode: Mesh;
+    private _sceneRenderObserver: Nullable<Observer<Scene>> = null;
+    private _scene: Scene;
+    private _targetPosition = new Vector3(0, 0, 0);
+    private _virtualOriginMesh: AbstractMesh;
+    private _virtualDragMesh: AbstractMesh;
+    private _pointerObserver: Nullable<Observer<PointerInfo>>;
+    private _moving = false;
+    private _startingOrientation = new Quaternion();
     /**
-     * A behavior that when attached to a mesh will allow the mesh to be dragged around based on directions and origin of the pointer's ray
+     * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 3)
      */
-    export class SixDofDragBehavior implements Behavior<Mesh> {
-        private static _virtualScene: Scene;
-        private _ownerNode: Mesh;
-        private _sceneRenderObserver: Nullable<Observer<Scene>> = null;
-        private _scene: Scene;
-        private _targetPosition = new Vector3(0, 0, 0);
-        private _virtualOriginMesh: AbstractMesh;
-        private _virtualDragMesh: AbstractMesh;
-        private _pointerObserver: Nullable<Observer<PointerInfo>>;
-        private _moving = false;
-        private _startingOrientation = new Quaternion();
-        /**
-         * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 3)
-         */
-        private zDragFactor = 3;
-        /**
-         * If the object should rotate to face the drag origin
-         */
-        public rotateDraggedObject = true;
-        /**
-         * If the behavior is currently in a dragging state
-         */
-        public dragging = false;
-        /**
-         * The distance towards the target drag position to move each frame. This can be useful to avoid jitter. Set this to 1 for no delay. (Default: 0.2)
-         */
-        public dragDeltaRatio = 0.2;
-        /**
-         * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
-         */
-        public currentDraggingPointerID = -1;
-        /**
-         * If camera controls should be detached during the drag
-         */
-        public detachCameraControls = true;
-        /**
-         * Fires each time a drag starts
-         */
-        public onDragStartObservable = new Observable<{}>();
-        /**
-         *  Fires each time a drag ends (eg. mouse release after drag)
-         */
-        public onDragEndObservable = new Observable<{}>();
-
-        /**
-         * Instantiates a behavior that when attached to a mesh will allow the mesh to be dragged around based on directions and origin of the pointer's ray
-         */
-        constructor() {
-        }
+    private zDragFactor = 3;
+    /**
+     * If the object should rotate to face the drag origin
+     */
+    public rotateDraggedObject = true;
+    /**
+     * If the behavior is currently in a dragging state
+     */
+    public dragging = false;
+    /**
+     * The distance towards the target drag position to move each frame. This can be useful to avoid jitter. Set this to 1 for no delay. (Default: 0.2)
+     */
+    public dragDeltaRatio = 0.2;
+    /**
+     * The id of the pointer that is currently interacting with the behavior (-1 when no pointer is active)
+     */
+    public currentDraggingPointerID = -1;
+    /**
+     * If camera controls should be detached during the drag
+     */
+    public detachCameraControls = true;
+    /**
+     * Fires each time a drag starts
+     */
+    public onDragStartObservable = new Observable<{}>();
+    /**
+     *  Fires each time a drag ends (eg. mouse release after drag)
+     */
+    public onDragEndObservable = new Observable<{}>();
 
-        /**
-         *  The name of the behavior
-         */
-        public get name(): string {
-            return "SixDofDrag";
-        }
+    /**
+     * Instantiates a behavior that when attached to a mesh will allow the mesh to be dragged around based on directions and origin of the pointer's ray
+     */
+    constructor() {
+    }
 
-        /**
-         *  Initializes the behavior
-         */
-        public init() { }
-
-        /**
-         * Attaches the scale behavior the passed in mesh
-         * @param ownerNode The mesh that will be scaled around once attached
-         */
-        public attach(ownerNode: Mesh): void {
-            this._ownerNode = ownerNode;
-            this._scene = this._ownerNode.getScene();
-            if (!SixDofDragBehavior._virtualScene) {
-                SixDofDragBehavior._virtualScene = new Scene(this._scene.getEngine());
-                SixDofDragBehavior._virtualScene.detachControl();
-                this._scene.getEngine().scenes.pop();
-            }
+    /**
+     *  The name of the behavior
+     */
+    public get name(): string {
+        return "SixDofDrag";
+    }
 
-            var pickedMesh: Nullable<AbstractMesh> = null;
-            var lastSixDofOriginPosition = new Vector3(0, 0, 0);
-
-            // Setup virtual meshes to be used for dragging without dirtying the existing scene
-            this._virtualOriginMesh = new AbstractMesh("", SixDofDragBehavior._virtualScene);
-            this._virtualOriginMesh.rotationQuaternion = new Quaternion();
-            this._virtualDragMesh = new AbstractMesh("", SixDofDragBehavior._virtualScene);
-            this._virtualDragMesh.rotationQuaternion = new Quaternion();
-
-            var pickPredicate = (m: AbstractMesh) => {
-                return this._ownerNode == m || m.isDescendantOf(this._ownerNode);
-            };
-            var attachedElement: Nullable<HTMLElement> = null;
-            this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState) => {
-                if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
-                    if (!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
-                        if (this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.globalPosition);
-                        }
+    /**
+     *  Initializes the behavior
+     */
+    public init() { }
 
-                        pickedMesh = this._ownerNode;
-                        PivotTools._RemoveAndStorePivotPoint(pickedMesh);
-                        lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
+    /**
+     * Attaches the scale behavior the passed in mesh
+     * @param ownerNode The mesh that will be scaled around once attached
+     */
+    public attach(ownerNode: Mesh): void {
+        this._ownerNode = ownerNode;
+        this._scene = this._ownerNode.getScene();
+        if (!SixDofDragBehavior._virtualScene) {
+            SixDofDragBehavior._virtualScene = new Scene(this._scene.getEngine());
+            SixDofDragBehavior._virtualScene.detachControl();
+            this._scene.getEngine().scenes.pop();
+        }
 
-                        // Set position and orientation of the controller
-                        this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
-                        this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.add(pointerInfo.pickInfo.ray.direction));
+        var pickedMesh: Nullable<AbstractMesh> = null;
+        var lastSixDofOriginPosition = new Vector3(0, 0, 0);
+
+        // Setup virtual meshes to be used for dragging without dirtying the existing scene
+        this._virtualOriginMesh = new AbstractMesh("", SixDofDragBehavior._virtualScene);
+        this._virtualOriginMesh.rotationQuaternion = new Quaternion();
+        this._virtualDragMesh = new AbstractMesh("", SixDofDragBehavior._virtualScene);
+        this._virtualDragMesh.rotationQuaternion = new Quaternion();
+
+        var pickPredicate = (m: AbstractMesh) => {
+            return this._ownerNode == m || m.isDescendantOf(this._ownerNode);
+        };
+        var attachedElement: Nullable<HTMLElement> = null;
+        this._pointerObserver = this._scene.onPointerObservable.add((pointerInfo, eventState) => {
+            if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
+                if (!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
+                    if (this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE) {
+                        pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.globalPosition);
+                    }
 
-                        // Attach the virtual drag mesh to the virtual origin mesh so it can be dragged
-                        this._virtualOriginMesh.removeChild(this._virtualDragMesh);
-                        pickedMesh.computeWorldMatrix();
-                        this._virtualDragMesh.position.copyFrom(pickedMesh.absolutePosition);
-                        if (!pickedMesh.rotationQuaternion) {
-                            pickedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(pickedMesh.rotation.y, pickedMesh.rotation.x, pickedMesh.rotation.z);
-                        }
-                        var oldParent = pickedMesh.parent;
-                        pickedMesh.setParent(null);
-                        this._virtualDragMesh.rotationQuaternion!.copyFrom(pickedMesh.rotationQuaternion);
-                        pickedMesh.setParent(oldParent);
-                        this._virtualOriginMesh.addChild(this._virtualDragMesh);
-
-                        // Update state
-                        this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
-                        this.dragging = true;
-                        this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
-
-                        // Detatch camera controls
-                        if (this.detachCameraControls && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
-                            if (this._scene.activeCamera.inputs.attachedElement) {
-                                attachedElement = this._scene.activeCamera.inputs.attachedElement;
-                                this._scene.activeCamera.detachControl(this._scene.activeCamera.inputs.attachedElement);
-                            } else {
-                                attachedElement = null;
-                            }
-                        }
-                        PivotTools._RestorePivotPoint(pickedMesh);
-                        this.onDragStartObservable.notifyObservers({});
+                    pickedMesh = this._ownerNode;
+                    PivotTools._RemoveAndStorePivotPoint(pickedMesh);
+                    lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
+
+                    // Set position and orientation of the controller
+                    this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
+                    this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.add(pointerInfo.pickInfo.ray.direction));
+
+                    // Attach the virtual drag mesh to the virtual origin mesh so it can be dragged
+                    this._virtualOriginMesh.removeChild(this._virtualDragMesh);
+                    pickedMesh.computeWorldMatrix();
+                    this._virtualDragMesh.position.copyFrom(pickedMesh.absolutePosition);
+                    if (!pickedMesh.rotationQuaternion) {
+                        pickedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(pickedMesh.rotation.y, pickedMesh.rotation.x, pickedMesh.rotation.z);
                     }
-                } else if (pointerInfo.type == PointerEventTypes.POINTERUP) {
-                    if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId) {
-                        this.dragging = false;
-                        this._moving = false;
-                        this.currentDraggingPointerID = -1;
-                        pickedMesh = null;
-                        this._virtualOriginMesh.removeChild(this._virtualDragMesh);
-
-                        // Reattach camera controls
-                        if (this.detachCameraControls && attachedElement && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
-                            this._scene.activeCamera.attachControl(attachedElement, true);
+                    var oldParent = pickedMesh.parent;
+                    pickedMesh.setParent(null);
+                    this._virtualDragMesh.rotationQuaternion!.copyFrom(pickedMesh.rotationQuaternion);
+                    pickedMesh.setParent(oldParent);
+                    this._virtualOriginMesh.addChild(this._virtualDragMesh);
+
+                    // Update state
+                    this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
+                    this.dragging = true;
+                    this.currentDraggingPointerID = (<PointerEvent>pointerInfo.event).pointerId;
+
+                    // Detatch camera controls
+                    if (this.detachCameraControls && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
+                        if (this._scene.activeCamera.inputs.attachedElement) {
+                            attachedElement = this._scene.activeCamera.inputs.attachedElement;
+                            this._scene.activeCamera.detachControl(this._scene.activeCamera.inputs.attachedElement);
+                        } else {
+                            attachedElement = null;
                         }
-                        this.onDragEndObservable.notifyObservers({});
                     }
-                } else if (pointerInfo.type == PointerEventTypes.POINTERMOVE) {
-                    if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh) {
-                        var zDragFactor = this.zDragFactor;
-                        if (this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.globalPosition);
-                            zDragFactor = 0;
-                        }
+                    PivotTools._RestorePivotPoint(pickedMesh);
+                    this.onDragStartObservable.notifyObservers({});
+                }
+            } else if (pointerInfo.type == PointerEventTypes.POINTERUP) {
+                if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId) {
+                    this.dragging = false;
+                    this._moving = false;
+                    this.currentDraggingPointerID = -1;
+                    pickedMesh = null;
+                    this._virtualOriginMesh.removeChild(this._virtualDragMesh);
+
+                    // Reattach camera controls
+                    if (this.detachCameraControls && attachedElement && this._scene.activeCamera && !this._scene.activeCamera.leftCamera) {
+                        this._scene.activeCamera.attachControl(attachedElement, true);
+                    }
+                    this.onDragEndObservable.notifyObservers({});
+                }
+            } else if (pointerInfo.type == PointerEventTypes.POINTERMOVE) {
+                if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh) {
+                    var zDragFactor = this.zDragFactor;
+                    if (this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE) {
+                        pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.globalPosition);
+                        zDragFactor = 0;
+                    }
 
-                        // Calculate controller drag distance in controller space
-                        var originDragDifference = pointerInfo.pickInfo.ray.origin.subtract(lastSixDofOriginPosition);
-                        lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
-                        var localOriginDragDifference = -Vector3.Dot(originDragDifference, pointerInfo.pickInfo.ray.direction);
+                    // Calculate controller drag distance in controller space
+                    var originDragDifference = pointerInfo.pickInfo.ray.origin.subtract(lastSixDofOriginPosition);
+                    lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
+                    var localOriginDragDifference = -Vector3.Dot(originDragDifference, pointerInfo.pickInfo.ray.direction);
 
-                        this._virtualOriginMesh.addChild(this._virtualDragMesh);
-                        // Determine how much the controller moved to/away towards the dragged object and use this to move the object further when its further away
-                        this._virtualDragMesh.position.z -= this._virtualDragMesh.position.z < 1 ? localOriginDragDifference * this.zDragFactor : localOriginDragDifference * zDragFactor * this._virtualDragMesh.position.z;
-                        if (this._virtualDragMesh.position.z < 0) {
-                            this._virtualDragMesh.position.z = 0;
-                        }
+                    this._virtualOriginMesh.addChild(this._virtualDragMesh);
+                    // Determine how much the controller moved to/away towards the dragged object and use this to move the object further when its further away
+                    this._virtualDragMesh.position.z -= this._virtualDragMesh.position.z < 1 ? localOriginDragDifference * this.zDragFactor : localOriginDragDifference * zDragFactor * this._virtualDragMesh.position.z;
+                    if (this._virtualDragMesh.position.z < 0) {
+                        this._virtualDragMesh.position.z = 0;
+                    }
 
-                        // Update the controller position
-                        this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
-                        this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.add(pointerInfo.pickInfo.ray.direction));
-                        this._virtualOriginMesh.removeChild(this._virtualDragMesh);
+                    // Update the controller position
+                    this._virtualOriginMesh.position.copyFrom(pointerInfo.pickInfo.ray.origin);
+                    this._virtualOriginMesh.lookAt(pointerInfo.pickInfo.ray.origin.add(pointerInfo.pickInfo.ray.direction));
+                    this._virtualOriginMesh.removeChild(this._virtualDragMesh);
 
-                        // Move the virtualObjectsPosition into the picked mesh's space if needed
-                        this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
-                        if (pickedMesh.parent) {
-                            Vector3.TransformCoordinatesToRef(this._targetPosition, Matrix.Invert(pickedMesh.parent.getWorldMatrix()), this._targetPosition);
-                        }
+                    // Move the virtualObjectsPosition into the picked mesh's space if needed
+                    this._targetPosition.copyFrom(this._virtualDragMesh.absolutePosition);
+                    if (pickedMesh.parent) {
+                        Vector3.TransformCoordinatesToRef(this._targetPosition, Matrix.Invert(pickedMesh.parent.getWorldMatrix()), this._targetPosition);
+                    }
 
-                        if (!this._moving) {
-                            this._startingOrientation.copyFrom(this._virtualDragMesh.rotationQuaternion!);
-                        }
-                        this._moving = true;
+                    if (!this._moving) {
+                        this._startingOrientation.copyFrom(this._virtualDragMesh.rotationQuaternion!);
                     }
+                    this._moving = true;
                 }
-            });
-
-            var tmpQuaternion = new Quaternion();
-            // On every frame move towards target scaling to avoid jitter caused by vr controllers
-            this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(() => {
-                if (this.dragging && this._moving && pickedMesh) {
-                    PivotTools._RemoveAndStorePivotPoint(pickedMesh);
+            }
+        });
+
+        var tmpQuaternion = new Quaternion();
+        // On every frame move towards target scaling to avoid jitter caused by vr controllers
+        this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(() => {
+            if (this.dragging && this._moving && pickedMesh) {
+                PivotTools._RemoveAndStorePivotPoint(pickedMesh);
+                // Slowly move mesh to avoid jitter
+                pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(this.dragDeltaRatio));
+
+                if (this.rotateDraggedObject) {
+                    // Get change in rotation
+                    tmpQuaternion.copyFrom(this._startingOrientation);
+                    tmpQuaternion.x = -tmpQuaternion.x;
+                    tmpQuaternion.y = -tmpQuaternion.y;
+                    tmpQuaternion.z = -tmpQuaternion.z;
+                    this._virtualDragMesh.rotationQuaternion!.multiplyToRef(tmpQuaternion, tmpQuaternion);
+                    // Convert change in rotation to only y axis rotation
+                    Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
+                    tmpQuaternion.multiplyToRef(this._startingOrientation, tmpQuaternion);
                     // Slowly move mesh to avoid jitter
-                    pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(this.dragDeltaRatio));
-
-                    if (this.rotateDraggedObject) {
-                        // Get change in rotation
-                        tmpQuaternion.copyFrom(this._startingOrientation);
-                        tmpQuaternion.x = -tmpQuaternion.x;
-                        tmpQuaternion.y = -tmpQuaternion.y;
-                        tmpQuaternion.z = -tmpQuaternion.z;
-                        this._virtualDragMesh.rotationQuaternion!.multiplyToRef(tmpQuaternion, tmpQuaternion);
-                        // Convert change in rotation to only y axis rotation
-                        Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
-                        tmpQuaternion.multiplyToRef(this._startingOrientation, tmpQuaternion);
-                        // Slowly move mesh to avoid jitter
-                        var oldParent = pickedMesh.parent;
-
-                        // Only rotate the mesh if it's parent has uniform scaling
-                        if (!oldParent || ((oldParent as Mesh).scaling && !(oldParent as Mesh).scaling.isNonUniformWithinEpsilon(0.001))) {
-                            pickedMesh.setParent(null);
-                            Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
-                            pickedMesh.setParent(oldParent);
-                        }
+                    var oldParent = pickedMesh.parent;
+
+                    // Only rotate the mesh if it's parent has uniform scaling
+                    if (!oldParent || ((oldParent as Mesh).scaling && !(oldParent as Mesh).scaling.isNonUniformWithinEpsilon(0.001))) {
+                        pickedMesh.setParent(null);
+                        Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
+                        pickedMesh.setParent(oldParent);
                     }
-                    PivotTools._RestorePivotPoint(pickedMesh);
                 }
-            });
-        }
-        /**
-         *  Detaches the behavior from the mesh
-         */
-        public detach(): void {
-            if (this._scene) {
-                this._scene.onPointerObservable.remove(this._pointerObserver);
-            }
-            if (this._ownerNode) {
-                this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
+                PivotTools._RestorePivotPoint(pickedMesh);
             }
-            if (this._virtualOriginMesh) {
-                this._virtualOriginMesh.dispose();
-            }
-            if (this._virtualDragMesh) {
-                this._virtualDragMesh.dispose();
-            }
-            this.onDragEndObservable.clear();
-            this.onDragStartObservable.clear();
+        });
+    }
+    /**
+     *  Detaches the behavior from the mesh
+     */
+    public detach(): void {
+        if (this._scene) {
+            this._scene.onPointerObservable.remove(this._pointerObserver);
+        }
+        if (this._ownerNode) {
+            this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
+        }
+        if (this._virtualOriginMesh) {
+            this._virtualOriginMesh.dispose();
+        }
+        if (this._virtualDragMesh) {
+            this._virtualDragMesh.dispose();
         }
+        this.onDragEndObservable.clear();
+        this.onDragStartObservable.clear();
     }
+}

+ 40 - 40
src/Behaviors/behavior.ts

@@ -1,47 +1,47 @@
 import { Nullable } from "../types";
 
+/**
+ * Interface used to define a behavior
+ */
+export interface Behavior<T> {
+    /** gets or sets behavior's name */
+    name: string;
+
     /**
-     * Interface used to define a behavior
+     * Function called when the behavior needs to be initialized (after attaching it to a target)
      */
-    export interface Behavior<T> {
-        /** gets or sets behavior's name */
-        name: string;
-
-        /**
-         * Function called when the behavior needs to be initialized (after attaching it to a target)
-         */
-        init(): void;
-        /**
-         * Called when the behavior is attached to a target
-         * @param target defines the target where the behavior is attached to
-         */
-        attach(target: T): void;
-        /**
-         * Called when the behavior is detached from its target
-         */
-        detach(): void;
-    }
+    init(): void;
+    /**
+     * Called when the behavior is attached to a target
+     * @param target defines the target where the behavior is attached to
+     */
+    attach(target: T): void;
+    /**
+     * Called when the behavior is detached from its target
+     */
+    detach(): void;
+}
 
+/**
+ * Interface implemented by classes supporting behaviors
+ */
+export interface IBehaviorAware<T> {
+    /**
+     * Attach a behavior
+     * @param behavior defines the behavior to attach
+     * @returns the current host
+     */
+    addBehavior(behavior: Behavior<T>): T;
+    /**
+     * Remove a behavior from the current object
+     * @param behavior defines the behavior to detach
+     * @returns the current host
+     */
+    removeBehavior(behavior: Behavior<T>): T;
     /**
-     * Interface implemented by classes supporting behaviors
+     * Gets a behavior using its name to search
+     * @param name defines the name to search
+     * @returns the behavior or null if not found
      */
-    export interface IBehaviorAware<T> {
-        /**
-         * Attach a behavior
-         * @param behavior defines the behavior to attach
-         * @returns the current host
-         */
-        addBehavior(behavior: Behavior<T>): T;
-        /**
-         * Remove a behavior from the current object
-         * @param behavior defines the behavior to detach
-         * @returns the current host
-         */
-        removeBehavior(behavior: Behavior<T>): T;
-        /**
-         * Gets a behavior using its name to search
-         * @param name defines the name to search
-         * @returns the behavior or null if not found
-         */
-        getBehaviorByName(name: string): Nullable<Behavior<T>>;
-    }
+    getBehaviorByName(name: string): Nullable<Behavior<T>>;
+}

文件差異過大導致無法顯示
+ 893 - 893
src/Bones/bone.ts


+ 263 - 263
src/Bones/boneIKController.ts

@@ -4,345 +4,345 @@ import { Vector3, Quaternion, Matrix, Space } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Nullable } from "../types";
 
+/**
+ * Class used to apply inverse kinematics to bones
+ * @see http://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons#boneikcontroller
+ */
+export class BoneIKController {
+
+    private static _tmpVecs: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
+    private static _tmpQuat = Quaternion.Identity();
+    private static _tmpMats: Matrix[] = [Matrix.Identity(), Matrix.Identity()];
+
     /**
-     * Class used to apply inverse kinematics to bones
-     * @see http://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons#boneikcontroller
+     * Gets or sets the target mesh
      */
-    export class BoneIKController {
-
-        private static _tmpVecs: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
-        private static _tmpQuat = Quaternion.Identity();
-        private static _tmpMats: Matrix[] = [Matrix.Identity(), Matrix.Identity()];
-
-        /**
-         * Gets or sets the target mesh
-         */
-        public targetMesh: AbstractMesh;
-
-        /** Gets or sets the mesh used as pole */
-        public poleTargetMesh: AbstractMesh;
-
-        /**
-         * Gets or sets the bone used as pole
-         */
-        public poleTargetBone: Nullable<Bone>;
-
-        /**
-         * Gets or sets the target position
-         */
-        public targetPosition = Vector3.Zero();
-
-        /**
-         * Gets or sets the pole target position
-         */
-        public poleTargetPosition = Vector3.Zero();
-
-        /**
-         * Gets or sets the pole target local offset
-         */
-        public poleTargetLocalOffset = Vector3.Zero();
-
-        /**
-         * Gets or sets the pole angle
-         */
-        public poleAngle = 0;
-
-        /**
-         * Gets or sets the mesh associated with the controller
-         */
-        public mesh: AbstractMesh;
-
-        /**
-         * The amount to slerp (spherical linear interpolation) to the target.  Set this to a value between 0 and 1 (a value of 1 disables slerp)
-         */
-        public slerpAmount = 1;
-
-        private _bone1Quat = Quaternion.Identity();
-        private _bone1Mat = Matrix.Identity();
-        private _bone2Ang = Math.PI;
-
-        private _bone1: Nullable<Bone>;
-        private _bone2: Bone;
-        private _bone1Length: number;
-        private _bone2Length: number;
-        private _maxAngle = Math.PI;
-        private _maxReach: number;
-
-        private _rightHandedSystem = false;
-
-        private _bendAxis = Vector3.Right();
-        private _slerping = false;
-
-        private _adjustRoll = 0;
-
-        /**
-         * Gets or sets maximum allowed angle
-         */
-        public get maxAngle(): number {
-            return this._maxAngle;
-        }
+    public targetMesh: AbstractMesh;
 
-        public set maxAngle(value: number) {
-            this._setMaxAngle(value);
-        }
+    /** Gets or sets the mesh used as pole */
+    public poleTargetMesh: AbstractMesh;
 
-        /**
-         * Creates a new BoneIKController
-         * @param mesh defines the mesh to control
-         * @param bone defines the bone to control
-         * @param options defines options to set up the controller
-         */
-        constructor(mesh: AbstractMesh,
-                    bone: Bone,
-                    options?: {
-                        targetMesh?: AbstractMesh,
-                        poleTargetMesh?: AbstractMesh,
-                        poleTargetBone?: Bone,
-                        poleTargetLocalOffset?: Vector3,
-                        poleAngle?: number,
-                        bendAxis?: Vector3,
-                        maxAngle?: number,
-                        slerpAmount?: number
-                    }) {
-
-            this._bone2 = bone;
-            this._bone1 = bone.getParent();
-
-            if (!this._bone1) {
-                return;
-            }
+    /**
+     * Gets or sets the bone used as pole
+     */
+    public poleTargetBone: Nullable<Bone>;
 
-            this.mesh = mesh;
+    /**
+     * Gets or sets the target position
+     */
+    public targetPosition = Vector3.Zero();
 
-            var bonePos = bone.getPosition();
+    /**
+     * Gets or sets the pole target position
+     */
+    public poleTargetPosition = Vector3.Zero();
 
-             if (bone.getAbsoluteTransform().determinant() > 0) {
-                this._rightHandedSystem = true;
-                this._bendAxis.x = 0;
-                this._bendAxis.y = 0;
-                this._bendAxis.z = -1;
+    /**
+     * Gets or sets the pole target local offset
+     */
+    public poleTargetLocalOffset = Vector3.Zero();
 
-                if (bonePos.x > bonePos.y && bonePos.x > bonePos.z) {
-                    this._adjustRoll = Math.PI * .5;
-                    this._bendAxis.z = 1;
-                }
-            }
+    /**
+     * Gets or sets the pole angle
+     */
+    public poleAngle = 0;
 
-            if (this._bone1.length) {
+    /**
+     * Gets or sets the mesh associated with the controller
+     */
+    public mesh: AbstractMesh;
 
-                var boneScale1 = this._bone1.getScale();
-                var boneScale2 = this._bone2.getScale();
+    /**
+     * The amount to slerp (spherical linear interpolation) to the target.  Set this to a value between 0 and 1 (a value of 1 disables slerp)
+     */
+    public slerpAmount = 1;
 
-                this._bone1Length = this._bone1.length * boneScale1.y * this.mesh.scaling.y;
-                this._bone2Length = this._bone2.length * boneScale2.y * this.mesh.scaling.y;
+    private _bone1Quat = Quaternion.Identity();
+    private _bone1Mat = Matrix.Identity();
+    private _bone2Ang = Math.PI;
 
-            } else if (this._bone1.children[0]) {
+    private _bone1: Nullable<Bone>;
+    private _bone2: Bone;
+    private _bone1Length: number;
+    private _bone2Length: number;
+    private _maxAngle = Math.PI;
+    private _maxReach: number;
 
-                mesh.computeWorldMatrix(true);
+    private _rightHandedSystem = false;
 
-                var pos1 = this._bone2.children[0].getAbsolutePosition(mesh);
-                var pos2 = this._bone2.getAbsolutePosition(mesh);
-                var pos3 = this._bone1.getAbsolutePosition(mesh);
+    private _bendAxis = Vector3.Right();
+    private _slerping = false;
 
-                this._bone1Length = Vector3.Distance(pos1, pos2);
-                this._bone2Length = Vector3.Distance(pos2, pos3);
+    private _adjustRoll = 0;
+
+    /**
+     * Gets or sets maximum allowed angle
+     */
+    public get maxAngle(): number {
+        return this._maxAngle;
+    }
+
+    public set maxAngle(value: number) {
+        this._setMaxAngle(value);
+    }
+
+    /**
+     * Creates a new BoneIKController
+     * @param mesh defines the mesh to control
+     * @param bone defines the bone to control
+     * @param options defines options to set up the controller
+     */
+    constructor(mesh: AbstractMesh,
+        bone: Bone,
+        options?: {
+            targetMesh?: AbstractMesh,
+            poleTargetMesh?: AbstractMesh,
+            poleTargetBone?: Bone,
+            poleTargetLocalOffset?: Vector3,
+            poleAngle?: number,
+            bendAxis?: Vector3,
+            maxAngle?: number,
+            slerpAmount?: number
+        }) {
+
+        this._bone2 = bone;
+        this._bone1 = bone.getParent();
+
+        if (!this._bone1) {
+            return;
+        }
+
+        this.mesh = mesh;
+
+        var bonePos = bone.getPosition();
+
+        if (bone.getAbsoluteTransform().determinant() > 0) {
+            this._rightHandedSystem = true;
+            this._bendAxis.x = 0;
+            this._bendAxis.y = 0;
+            this._bendAxis.z = -1;
+
+            if (bonePos.x > bonePos.y && bonePos.x > bonePos.z) {
+                this._adjustRoll = Math.PI * .5;
+                this._bendAxis.z = 1;
             }
+        }
 
-            this._bone1.getRotationMatrixToRef(Space.WORLD, mesh, this._bone1Mat);
-            this.maxAngle = Math.PI;
+        if (this._bone1.length) {
 
-            if (options) {
+            var boneScale1 = this._bone1.getScale();
+            var boneScale2 = this._bone2.getScale();
 
-                if (options.targetMesh) {
-                    this.targetMesh = options.targetMesh;
-                    this.targetMesh.computeWorldMatrix(true);
-                }
+            this._bone1Length = this._bone1.length * boneScale1.y * this.mesh.scaling.y;
+            this._bone2Length = this._bone2.length * boneScale2.y * this.mesh.scaling.y;
 
-                if (options.poleTargetMesh) {
+        } else if (this._bone1.children[0]) {
 
-                    this.poleTargetMesh = options.poleTargetMesh;
-                    this.poleTargetMesh.computeWorldMatrix(true);
+            mesh.computeWorldMatrix(true);
 
-                } else if (options.poleTargetBone) {
-                    this.poleTargetBone = options.poleTargetBone;
-                } else if (this._bone1.getParent()) {
-                    this.poleTargetBone = this._bone1.getParent();
-                }
+            var pos1 = this._bone2.children[0].getAbsolutePosition(mesh);
+            var pos2 = this._bone2.getAbsolutePosition(mesh);
+            var pos3 = this._bone1.getAbsolutePosition(mesh);
 
-                if (options.poleTargetLocalOffset) {
-                    this.poleTargetLocalOffset.copyFrom(options.poleTargetLocalOffset);
-                }
+            this._bone1Length = Vector3.Distance(pos1, pos2);
+            this._bone2Length = Vector3.Distance(pos2, pos3);
+        }
 
-                if (options.poleAngle) {
-                    this.poleAngle = options.poleAngle;
-                }
+        this._bone1.getRotationMatrixToRef(Space.WORLD, mesh, this._bone1Mat);
+        this.maxAngle = Math.PI;
 
-                if (options.bendAxis) {
-                    this._bendAxis.copyFrom(options.bendAxis);
-                }
+        if (options) {
 
-                if (options.maxAngle) {
-                    this.maxAngle = options.maxAngle;
-                }
+            if (options.targetMesh) {
+                this.targetMesh = options.targetMesh;
+                this.targetMesh.computeWorldMatrix(true);
+            }
 
-                if (options.slerpAmount) {
-                    this.slerpAmount = options.slerpAmount;
-                }
+            if (options.poleTargetMesh) {
 
+                this.poleTargetMesh = options.poleTargetMesh;
+                this.poleTargetMesh.computeWorldMatrix(true);
+
+            } else if (options.poleTargetBone) {
+                this.poleTargetBone = options.poleTargetBone;
+            } else if (this._bone1.getParent()) {
+                this.poleTargetBone = this._bone1.getParent();
             }
 
-        }
+            if (options.poleTargetLocalOffset) {
+                this.poleTargetLocalOffset.copyFrom(options.poleTargetLocalOffset);
+            }
 
-        private _setMaxAngle(ang: number): void {
+            if (options.poleAngle) {
+                this.poleAngle = options.poleAngle;
+            }
 
-            if (ang < 0) {
-                ang = 0;
+            if (options.bendAxis) {
+                this._bendAxis.copyFrom(options.bendAxis);
             }
 
-            if (ang > Math.PI || ang == undefined) {
-                ang = Math.PI;
+            if (options.maxAngle) {
+                this.maxAngle = options.maxAngle;
             }
 
-            this._maxAngle = ang;
+            if (options.slerpAmount) {
+                this.slerpAmount = options.slerpAmount;
+            }
 
-            var a = this._bone1Length;
-            var b = this._bone2Length;
+        }
 
-            this._maxReach = Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(ang));
+    }
+
+    private _setMaxAngle(ang: number): void {
 
+        if (ang < 0) {
+            ang = 0;
         }
 
-        /**
-         * Force the controller to update the bones
-         */
-        public update(): void {
-            var bone1 = this._bone1;
+        if (ang > Math.PI || ang == undefined) {
+            ang = Math.PI;
+        }
 
-            if (!bone1) {
-                return;
-            }
+        this._maxAngle = ang;
 
-            var target = this.targetPosition;
-            var poleTarget = this.poleTargetPosition;
+        var a = this._bone1Length;
+        var b = this._bone2Length;
 
-            var mat1 = BoneIKController._tmpMats[0];
-            var mat2 = BoneIKController._tmpMats[1];
+        this._maxReach = Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(ang));
 
-            if (this.targetMesh) {
-                target.copyFrom(this.targetMesh.getAbsolutePosition());
-            }
+    }
 
-            if (this.poleTargetBone) {
-                this.poleTargetBone.getAbsolutePositionFromLocalToRef(this.poleTargetLocalOffset, this.mesh, poleTarget);
-            }else if (this.poleTargetMesh) {
-                Vector3.TransformCoordinatesToRef(this.poleTargetLocalOffset, this.poleTargetMesh.getWorldMatrix(), poleTarget);
-            }
+    /**
+     * Force the controller to update the bones
+     */
+    public update(): void {
+        var bone1 = this._bone1;
 
-            var bonePos = BoneIKController._tmpVecs[0];
-            var zaxis = BoneIKController._tmpVecs[1];
-            var xaxis = BoneIKController._tmpVecs[2];
-            var yaxis = BoneIKController._tmpVecs[3];
-            var upAxis = BoneIKController._tmpVecs[4];
+        if (!bone1) {
+            return;
+        }
 
-            var _tmpQuat = BoneIKController._tmpQuat;
+        var target = this.targetPosition;
+        var poleTarget = this.poleTargetPosition;
 
-            bone1.getAbsolutePositionToRef(this.mesh, bonePos);
+        var mat1 = BoneIKController._tmpMats[0];
+        var mat2 = BoneIKController._tmpMats[1];
 
-            poleTarget.subtractToRef(bonePos, upAxis);
+        if (this.targetMesh) {
+            target.copyFrom(this.targetMesh.getAbsolutePosition());
+        }
 
-            if (upAxis.x == 0 && upAxis.y == 0 && upAxis.z == 0) {
-                upAxis.y = 1;
-            } else {
-                upAxis.normalize();
-            }
+        if (this.poleTargetBone) {
+            this.poleTargetBone.getAbsolutePositionFromLocalToRef(this.poleTargetLocalOffset, this.mesh, poleTarget);
+        } else if (this.poleTargetMesh) {
+            Vector3.TransformCoordinatesToRef(this.poleTargetLocalOffset, this.poleTargetMesh.getWorldMatrix(), poleTarget);
+        }
 
-            target.subtractToRef(bonePos, yaxis);
-            yaxis.normalize();
+        var bonePos = BoneIKController._tmpVecs[0];
+        var zaxis = BoneIKController._tmpVecs[1];
+        var xaxis = BoneIKController._tmpVecs[2];
+        var yaxis = BoneIKController._tmpVecs[3];
+        var upAxis = BoneIKController._tmpVecs[4];
 
-            Vector3.CrossToRef(yaxis, upAxis, zaxis);
-            zaxis.normalize();
+        var _tmpQuat = BoneIKController._tmpQuat;
 
-            Vector3.CrossToRef(yaxis, zaxis, xaxis);
-            xaxis.normalize();
+        bone1.getAbsolutePositionToRef(this.mesh, bonePos);
 
-            Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
+        poleTarget.subtractToRef(bonePos, upAxis);
 
-            var a = this._bone1Length;
-            var b = this._bone2Length;
+        if (upAxis.x == 0 && upAxis.y == 0 && upAxis.z == 0) {
+            upAxis.y = 1;
+        } else {
+            upAxis.normalize();
+        }
 
-            var c = Vector3.Distance(bonePos, target);
+        target.subtractToRef(bonePos, yaxis);
+        yaxis.normalize();
 
-            if (this._maxReach > 0) {
-                c = Math.min(this._maxReach, c);
-            }
+        Vector3.CrossToRef(yaxis, upAxis, zaxis);
+        zaxis.normalize();
 
-            var acosa = (b * b + c * c - a * a) / (2 * b * c);
-            var acosb = (c * c + a * a - b * b) / (2 * c * a);
+        Vector3.CrossToRef(yaxis, zaxis, xaxis);
+        xaxis.normalize();
 
-            if (acosa > 1) {
-                acosa = 1;
-            }
+        Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
 
-            if (acosb > 1) {
-                acosb = 1;
-            }
+        var a = this._bone1Length;
+        var b = this._bone2Length;
 
-            if (acosa < -1) {
-                acosa = -1;
-            }
+        var c = Vector3.Distance(bonePos, target);
 
-            if (acosb < -1) {
-                acosb = -1;
-            }
+        if (this._maxReach > 0) {
+            c = Math.min(this._maxReach, c);
+        }
 
-            var angA = Math.acos(acosa);
-            var angB = Math.acos(acosb);
+        var acosa = (b * b + c * c - a * a) / (2 * b * c);
+        var acosb = (c * c + a * a - b * b) / (2 * c * a);
 
-            var angC = -angA - angB;
+        if (acosa > 1) {
+            acosa = 1;
+        }
 
-            if (this._rightHandedSystem) {
+        if (acosb > 1) {
+            acosb = 1;
+        }
 
-                Matrix.RotationYawPitchRollToRef(0, 0, this._adjustRoll, mat2);
-                mat2.multiplyToRef(mat1, mat1);
+        if (acosa < -1) {
+            acosa = -1;
+        }
 
-                Matrix.RotationAxisToRef(this._bendAxis, angB, mat2);
-                mat2.multiplyToRef(mat1, mat1);
+        if (acosb < -1) {
+            acosb = -1;
+        }
 
-            } else {
+        var angA = Math.acos(acosa);
+        var angB = Math.acos(acosb);
 
-                var _tmpVec = BoneIKController._tmpVecs[5];
+        var angC = -angA - angB;
 
-                _tmpVec.copyFrom(this._bendAxis);
-                _tmpVec.x *= -1;
+        if (this._rightHandedSystem) {
 
-                Matrix.RotationAxisToRef(_tmpVec, -angB, mat2);
-                mat2.multiplyToRef(mat1, mat1);
+            Matrix.RotationYawPitchRollToRef(0, 0, this._adjustRoll, mat2);
+            mat2.multiplyToRef(mat1, mat1);
 
-            }
+            Matrix.RotationAxisToRef(this._bendAxis, angB, mat2);
+            mat2.multiplyToRef(mat1, mat1);
 
-            if (this.poleAngle) {
-                Matrix.RotationAxisToRef(yaxis, this.poleAngle, mat2);
-                mat1.multiplyToRef(mat2, mat1);
-            }
+        } else {
+
+            var _tmpVec = BoneIKController._tmpVecs[5];
+
+            _tmpVec.copyFrom(this._bendAxis);
+            _tmpVec.x *= -1;
+
+            Matrix.RotationAxisToRef(_tmpVec, -angB, mat2);
+            mat2.multiplyToRef(mat1, mat1);
+
+        }
 
-            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;
+        if (this.poleAngle) {
+            Matrix.RotationAxisToRef(yaxis, this.poleAngle, mat2);
+            mat1.multiplyToRef(mat2, mat1);
+        }
+
+        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;
             }
-
-            this._bone2.setAxisAngle(this._bendAxis, angC, Space.LOCAL);
-            this._bone2Ang = angC;
         }
+
+        this._bone2.setAxisAngle(this._bendAxis, angC, Space.LOCAL);
+        this._bone2Ang = angC;
     }
+}

文件差異過大導致無法顯示
+ 453 - 453
src/Bones/boneLookController.ts


文件差異過大導致無法顯示
+ 570 - 570
src/Bones/skeleton.ts


+ 101 - 101
src/Cameras/Inputs/arcRotateCameraGamepadInput.ts

@@ -4,124 +4,124 @@ import { Observer } from "../../Misc/observable";
 import { ArcRotateCamera } from "../../Cameras/arcRotateCamera";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
 import { Gamepad } from "../../Gamepads/gamepad";
+/**
+ * Manage the gamepad inputs to control an arc rotate camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class ArcRotateCameraGamepadInput implements ICameraInput<ArcRotateCamera> {
     /**
-     * Manage the gamepad inputs to control an arc rotate camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class ArcRotateCameraGamepadInput implements ICameraInput<ArcRotateCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: ArcRotateCamera;
-
-        /**
-         * Defines the gamepad the input is gathering event from.
-         */
-        public gamepad: Nullable<Gamepad>;
-
-        /**
-         * Defines the gamepad rotation sensiblity.
-         * This is the threshold from when rotation starts to be accounted for to prevent jittering.
-         */
-        @serialize()
-        public gamepadRotationSensibility = 80;
-
-        /**
-         * Defines the gamepad move sensiblity.
-         * This is the threshold from when moving starts to be accounted for for to prevent jittering.
-         */
-        @serialize()
-        public gamepadMoveSensibility = 40;
-
-        private _onGamepadConnectedObserver : Nullable<Observer<Gamepad>>;
-        private _onGamepadDisconnectedObserver : Nullable<Observer<Gamepad>>;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            let manager = this.camera.getScene().gamepadManager;
-            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
-                if (gamepad.type !== Gamepad.POSE_ENABLED) {
-                    // prioritize XBOX gamepads.
-                    if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
-                        this.gamepad = gamepad;
-                    }
-                }
-            });
+    public camera: ArcRotateCamera;
+
+    /**
+     * Defines the gamepad the input is gathering event from.
+     */
+    public gamepad: Nullable<Gamepad>;
+
+    /**
+     * Defines the gamepad rotation sensiblity.
+     * This is the threshold from when rotation starts to be accounted for to prevent jittering.
+     */
+    @serialize()
+    public gamepadRotationSensibility = 80;
+
+    /**
+     * Defines the gamepad move sensiblity.
+     * This is the threshold from when moving starts to be accounted for for to prevent jittering.
+     */
+    @serialize()
+    public gamepadMoveSensibility = 40;
+
+    private _onGamepadConnectedObserver: Nullable<Observer<Gamepad>>;
+    private _onGamepadDisconnectedObserver: Nullable<Observer<Gamepad>>;
 
-            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad) => {
-                if (this.gamepad === gamepad) {
-                    this.gamepad = null;
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        let manager = this.camera.getScene().gamepadManager;
+        this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
+            if (gamepad.type !== Gamepad.POSE_ENABLED) {
+                // prioritize XBOX gamepads.
+                if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
+                    this.gamepad = gamepad;
                 }
-            });
+            }
+        });
 
-            this.gamepad = manager.getGamepadByType(Gamepad.XBOX);
-        }
+        this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad) => {
+            if (this.gamepad === gamepad) {
+                this.gamepad = null;
+            }
+        });
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
-            this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
-            this.gamepad = null;
-        }
+        this.gamepad = manager.getGamepadByType(Gamepad.XBOX);
+    }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this.gamepad) {
-                var camera = this.camera;
-                var RSValues = this.gamepad.rightStick;
-
-                if (RSValues) {
-                    if (RSValues.x != 0) {
-                        var normalizedRX = RSValues.x / this.gamepadRotationSensibility;
-                        if (normalizedRX != 0 && Math.abs(normalizedRX) > 0.005) {
-                            camera.inertialAlphaOffset += normalizedRX;
-                        }
-                    }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
+        this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
+        this.gamepad = null;
+    }
 
-                    if (RSValues.y != 0) {
-                        var normalizedRY = RSValues.y / this.gamepadRotationSensibility;
-                        if (normalizedRY != 0 && Math.abs(normalizedRY) > 0.005) {
-                            camera.inertialBetaOffset += normalizedRY;
-                        }
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this.gamepad) {
+            var camera = this.camera;
+            var RSValues = this.gamepad.rightStick;
+
+            if (RSValues) {
+                if (RSValues.x != 0) {
+                    var normalizedRX = RSValues.x / this.gamepadRotationSensibility;
+                    if (normalizedRX != 0 && Math.abs(normalizedRX) > 0.005) {
+                        camera.inertialAlphaOffset += normalizedRX;
                     }
                 }
 
-                var LSValues = this.gamepad.leftStick;
-                if (LSValues && LSValues.y != 0) {
-                    var normalizedLY = LSValues.y / this.gamepadMoveSensibility;
-                    if (normalizedLY != 0 && Math.abs(normalizedLY) > 0.005) {
-                        this.camera.inertialRadiusOffset -= normalizedLY;
+                if (RSValues.y != 0) {
+                    var normalizedRY = RSValues.y / this.gamepadRotationSensibility;
+                    if (normalizedRY != 0 && Math.abs(normalizedRY) > 0.005) {
+                        camera.inertialBetaOffset += normalizedRY;
                     }
                 }
+            }
 
+            var LSValues = this.gamepad.leftStick;
+            if (LSValues && LSValues.y != 0) {
+                var normalizedLY = LSValues.y / this.gamepadMoveSensibility;
+                if (normalizedLY != 0 && Math.abs(normalizedLY) > 0.005) {
+                    this.camera.inertialRadiusOffset -= normalizedLY;
+                }
             }
-        }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "ArcRotateCameraGamepadInput";
         }
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "gamepad";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "ArcRotateCameraGamepadInput";
+    }
+
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "gamepad";
     }
+}
 
-    (<any>CameraInputTypes)["ArcRotateCameraGamepadInput"] = ArcRotateCameraGamepadInput;
+(<any>CameraInputTypes)["ArcRotateCameraGamepadInput"] = ArcRotateCameraGamepadInput;

+ 205 - 205
src/Cameras/Inputs/arcRotateCameraKeyboardMoveInput.ts

@@ -7,232 +7,232 @@ import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManage
 import { Engine } from "../../Engines/engine";
 import { KeyboardInfo, KeyboardEventTypes } from "../../Events/keyboardEvents";
 
+/**
+ * Manage the keyboard inputs to control the movement of an arc rotate camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class ArcRotateCameraKeyboardMoveInput implements ICameraInput<ArcRotateCamera> {
     /**
-     * Manage the keyboard inputs to control the movement of an arc rotate camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class ArcRotateCameraKeyboardMoveInput implements ICameraInput<ArcRotateCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: ArcRotateCamera;
-
-        /**
-         * Defines the list of key codes associated with the up action (increase alpha)
-         */
-        @serialize()
-        public keysUp = [38];
-
-        /**
-         * Defines the list of key codes associated with the down action (decrease alpha)
-         */
-        @serialize()
-        public keysDown = [40];
-
-        /**
-         * Defines the list of key codes associated with the left action (increase beta)
-         */
-        @serialize()
-        public keysLeft = [37];
-
-        /**
-         * Defines the list of key codes associated with the right action (decrease beta)
-         */
-        @serialize()
-        public keysRight = [39];
-
-        /**
-         * Defines the list of key codes associated with the reset action.
-         * Those keys reset the camera to its last stored state (with the method camera.storeState())
-         */
-        @serialize()
-        public keysReset = [220];
-
-        /**
-         * Defines the panning sensibility of the inputs.
-         * (How fast is the camera paning)
-         */
-        @serialize()
-        public panningSensibility: number = 50.0;
-
-        /**
-         * Defines the zooming sensibility of the inputs.
-         * (How fast is the camera zooming)
-         */
-        @serialize()
-        public zoomingSensibility: number = 25.0;
-
-        /**
-         * Defines wether maintaining the alt key down switch the movement mode from
-         * orientation to zoom.
-         */
-        @serialize()
-        public useAltToZoom: boolean = true;
-
-        /**
-         * Rotation speed of the camera
-         */
-        @serialize()
-        public angularSpeed = 0.01;
-
-        private _keys = new Array<number>();
-        private _ctrlPressed: boolean;
-        private _altPressed: boolean;
-        private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
-        private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
-        private _engine: Engine;
-        private _scene: Scene;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            if (this._onCanvasBlurObserver) {
-                return;
-            }
+    public camera: ArcRotateCamera;
 
-            this._scene = this.camera.getScene();
-            this._engine = this._scene.getEngine();
-
-            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
-                this._keys = [];
-            });
-
-            this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
-                let evt = info.event;
-                if (!evt.metaKey) {
-                    if (info.type === KeyboardEventTypes.KEYDOWN) {
-                        this._ctrlPressed = evt.ctrlKey;
-                        this._altPressed = evt.altKey;
-
-                        if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                            this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                            this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                            this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                            this.keysReset.indexOf(evt.keyCode) !== -1) {
-                            var index = this._keys.indexOf(evt.keyCode);
-
-                            if (index === -1) {
-                                this._keys.push(evt.keyCode);
-                            }
+    /**
+     * Defines the list of key codes associated with the up action (increase alpha)
+     */
+    @serialize()
+    public keysUp = [38];
 
-                            if (evt.preventDefault) {
-                                if (!noPreventDefault) {
-                                    evt.preventDefault();
-                                }
-                            }
+    /**
+     * Defines the list of key codes associated with the down action (decrease alpha)
+     */
+    @serialize()
+    public keysDown = [40];
+
+    /**
+     * Defines the list of key codes associated with the left action (increase beta)
+     */
+    @serialize()
+    public keysLeft = [37];
+
+    /**
+     * Defines the list of key codes associated with the right action (decrease beta)
+     */
+    @serialize()
+    public keysRight = [39];
+
+    /**
+     * Defines the list of key codes associated with the reset action.
+     * Those keys reset the camera to its last stored state (with the method camera.storeState())
+     */
+    @serialize()
+    public keysReset = [220];
+
+    /**
+     * Defines the panning sensibility of the inputs.
+     * (How fast is the camera paning)
+     */
+    @serialize()
+    public panningSensibility: number = 50.0;
+
+    /**
+     * Defines the zooming sensibility of the inputs.
+     * (How fast is the camera zooming)
+     */
+    @serialize()
+    public zoomingSensibility: number = 25.0;
+
+    /**
+     * Defines wether maintaining the alt key down switch the movement mode from
+     * orientation to zoom.
+     */
+    @serialize()
+    public useAltToZoom: boolean = true;
+
+    /**
+     * Rotation speed of the camera
+     */
+    @serialize()
+    public angularSpeed = 0.01;
+
+    private _keys = new Array<number>();
+    private _ctrlPressed: boolean;
+    private _altPressed: boolean;
+    private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
+    private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
+    private _engine: Engine;
+    private _scene: Scene;
+
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        if (this._onCanvasBlurObserver) {
+            return;
+        }
+
+        this._scene = this.camera.getScene();
+        this._engine = this._scene.getEngine();
+
+        this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
+            this._keys = [];
+        });
+
+        this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
+            let evt = info.event;
+            if (!evt.metaKey) {
+                if (info.type === KeyboardEventTypes.KEYDOWN) {
+                    this._ctrlPressed = evt.ctrlKey;
+                    this._altPressed = evt.altKey;
+
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index === -1) {
+                            this._keys.push(evt.keyCode);
                         }
-                    }
-                    else {
-                        if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                            this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                            this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                            this.keysRight.indexOf(evt.keyCode) !== -1 ||
-                            this.keysReset.indexOf(evt.keyCode) !== -1) {
-                            var index = this._keys.indexOf(evt.keyCode);
-
-                            if (index >= 0) {
-                                this._keys.splice(index, 1);
-                            }
 
-                            if (evt.preventDefault) {
-                                if (!noPreventDefault) {
-                                    evt.preventDefault();
-                                }
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
                             }
                         }
                     }
                 }
-            });
-        }
+                else {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                        this.keysReset.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index >= 0) {
+                            this._keys.splice(index, 1);
+                        }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>) {
-            if (this._scene) {
-                if (this._onKeyboardObserver) {
-                    this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
-                }
-                if (this._onCanvasBlurObserver) {
-                    this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
+                        }
+                    }
                 }
-                this._onKeyboardObserver = null;
-                this._onCanvasBlurObserver = null;
             }
+        });
+    }
 
-            this._keys = [];
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>) {
+        if (this._scene) {
+            if (this._onKeyboardObserver) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+            }
+            if (this._onCanvasBlurObserver) {
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            }
+            this._onKeyboardObserver = null;
+            this._onCanvasBlurObserver = null;
         }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this._onKeyboardObserver) {
-                var camera = this.camera;
-
-                for (var index = 0; index < this._keys.length; index++) {
-                    var keyCode = this._keys[index];
-                    if (this.keysLeft.indexOf(keyCode) !== -1) {
-                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
-                            camera.inertialPanningX -= 1 / this.panningSensibility;
-                        } else {
-                            camera.inertialAlphaOffset -= this.angularSpeed;
-                        }
-                    } else if (this.keysUp.indexOf(keyCode) !== -1) {
-                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
-                            camera.inertialPanningY += 1 / this.panningSensibility;
-                        }
-                        else if (this._altPressed && this.useAltToZoom) {
-                            camera.inertialRadiusOffset += 1 / this.zoomingSensibility;
-                        }
-                        else {
-                            camera.inertialBetaOffset -= this.angularSpeed;
-                        }
-                    } else if (this.keysRight.indexOf(keyCode) !== -1) {
-                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
-                            camera.inertialPanningX += 1 / this.panningSensibility;
-                        } else {
-                            camera.inertialAlphaOffset += this.angularSpeed;
-                        }
-                    } else if (this.keysDown.indexOf(keyCode) !== -1) {
-                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
-                            camera.inertialPanningY -= 1 / this.panningSensibility;
-                        }
-                        else if (this._altPressed && this.useAltToZoom) {
-                            camera.inertialRadiusOffset -= 1 / this.zoomingSensibility;
-                        }
-                        else {
-                            camera.inertialBetaOffset += this.angularSpeed;
-                        }
-                    } else if (this.keysReset.indexOf(keyCode) !== -1) {
-                        if (camera.useInputToRestoreState) {
-                            camera.restoreState();
-                        }
+        this._keys = [];
+    }
+
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this._onKeyboardObserver) {
+            var camera = this.camera;
+
+            for (var index = 0; index < this._keys.length; index++) {
+                var keyCode = this._keys[index];
+                if (this.keysLeft.indexOf(keyCode) !== -1) {
+                    if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                        camera.inertialPanningX -= 1 / this.panningSensibility;
+                    } else {
+                        camera.inertialAlphaOffset -= this.angularSpeed;
+                    }
+                } else if (this.keysUp.indexOf(keyCode) !== -1) {
+                    if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                        camera.inertialPanningY += 1 / this.panningSensibility;
+                    }
+                    else if (this._altPressed && this.useAltToZoom) {
+                        camera.inertialRadiusOffset += 1 / this.zoomingSensibility;
+                    }
+                    else {
+                        camera.inertialBetaOffset -= this.angularSpeed;
+                    }
+                } else if (this.keysRight.indexOf(keyCode) !== -1) {
+                    if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                        camera.inertialPanningX += 1 / this.panningSensibility;
+                    } else {
+                        camera.inertialAlphaOffset += this.angularSpeed;
+                    }
+                } else if (this.keysDown.indexOf(keyCode) !== -1) {
+                    if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                        camera.inertialPanningY -= 1 / this.panningSensibility;
+                    }
+                    else if (this._altPressed && this.useAltToZoom) {
+                        camera.inertialRadiusOffset -= 1 / this.zoomingSensibility;
+                    }
+                    else {
+                        camera.inertialBetaOffset += this.angularSpeed;
+                    }
+                } else if (this.keysReset.indexOf(keyCode) !== -1) {
+                    if (camera.useInputToRestoreState) {
+                        camera.restoreState();
                     }
                 }
             }
         }
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "ArcRotateCameraKeyboardMoveInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "ArcRotateCameraKeyboardMoveInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "keyboard";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "keyboard";
     }
+}
 
-    (<any>CameraInputTypes)["ArcRotateCameraKeyboardMoveInput"] = ArcRotateCameraKeyboardMoveInput;
+(<any>CameraInputTypes)["ArcRotateCameraKeyboardMoveInput"] = ArcRotateCameraKeyboardMoveInput;

+ 78 - 78
src/Cameras/Inputs/arcRotateCameraMouseWheelInput.ts

@@ -5,102 +5,102 @@ import { ArcRotateCamera } from "../../Cameras/arcRotateCamera";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
 import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
 
+/**
+ * Manage the mouse wheel inputs to control an arc rotate camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class ArcRotateCameraMouseWheelInput implements ICameraInput<ArcRotateCamera> {
     /**
-     * Manage the mouse wheel inputs to control an arc rotate camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class ArcRotateCameraMouseWheelInput implements ICameraInput<ArcRotateCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: ArcRotateCamera;
+    public camera: ArcRotateCamera;
 
-        /**
-         * Gets or Set the mouse wheel precision or how fast is the camera zooming.
-         */
-        @serialize()
-        public wheelPrecision = 3.0;
+    /**
+     * Gets or Set the mouse wheel precision or how fast is the camera zooming.
+     */
+    @serialize()
+    public wheelPrecision = 3.0;
 
-        /**
-         * wheelDeltaPercentage will be used instead of wheelPrecision if different from 0.
-         * It defines the percentage of current camera.radius to use as delta when wheel is used.
-         */
-        @serialize()
-        public wheelDeltaPercentage = 0;
+    /**
+     * wheelDeltaPercentage will be used instead of wheelPrecision if different from 0.
+     * It defines the percentage of current camera.radius to use as delta when wheel is used.
+     */
+    @serialize()
+    public wheelDeltaPercentage = 0;
 
-        private _wheel: Nullable<(p: PointerInfo, s: EventState) => void>;
-        private _observer: Nullable<Observer<PointerInfo>>;
+    private _wheel: Nullable<(p: PointerInfo, s: EventState) => void>;
+    private _observer: Nullable<Observer<PointerInfo>>;
 
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            this._wheel = (p, s) => {
-                //sanity check - this should be a PointerWheel event.
-                if (p.type !== PointerEventTypes.POINTERWHEEL) { return; }
-                var event = <MouseWheelEvent>p.event;
-                var delta = 0;
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        this._wheel = (p, s) => {
+            //sanity check - this should be a PointerWheel event.
+            if (p.type !== PointerEventTypes.POINTERWHEEL) { return; }
+            var event = <MouseWheelEvent>p.event;
+            var delta = 0;
 
-                let mouseWheelLegacyEvent = event as any;
-                if (mouseWheelLegacyEvent.wheelDelta) {
-                    if (this.wheelDeltaPercentage) {
-                        var wheelDelta = (mouseWheelLegacyEvent.wheelDelta * 0.01 * this.wheelDeltaPercentage) * this.camera.radius;
-                        if (mouseWheelLegacyEvent.wheelDelta > 0) {
-                            delta = wheelDelta / (1.0 + this.wheelDeltaPercentage);
-                        } else {
-                            delta = wheelDelta * (1.0 + this.wheelDeltaPercentage);
-                        }
+            let mouseWheelLegacyEvent = event as any;
+            if (mouseWheelLegacyEvent.wheelDelta) {
+                if (this.wheelDeltaPercentage) {
+                    var wheelDelta = (mouseWheelLegacyEvent.wheelDelta * 0.01 * this.wheelDeltaPercentage) * this.camera.radius;
+                    if (mouseWheelLegacyEvent.wheelDelta > 0) {
+                        delta = wheelDelta / (1.0 + this.wheelDeltaPercentage);
                     } else {
-                        delta = mouseWheelLegacyEvent.wheelDelta / (this.wheelPrecision * 40);
+                        delta = wheelDelta * (1.0 + this.wheelDeltaPercentage);
                     }
                 } else {
-                    let deltaValue = event.deltaY || event.detail;
-                    delta = -deltaValue / this.wheelPrecision;
+                    delta = mouseWheelLegacyEvent.wheelDelta / (this.wheelPrecision * 40);
                 }
+            } else {
+                let deltaValue = event.deltaY || event.detail;
+                delta = -deltaValue / this.wheelPrecision;
+            }
 
-                if (delta) {
-                    this.camera.inertialRadiusOffset += delta;
-                }
+            if (delta) {
+                this.camera.inertialRadiusOffset += delta;
+            }
 
-                if (event.preventDefault) {
-                    if (!noPreventDefault) {
-                        event.preventDefault();
-                    }
+            if (event.preventDefault) {
+                if (!noPreventDefault) {
+                    event.preventDefault();
                 }
-            };
+            }
+        };
 
-            this._observer = this.camera.getScene().onPointerObservable.add(this._wheel, PointerEventTypes.POINTERWHEEL);
-        }
+        this._observer = this.camera.getScene().onPointerObservable.add(this._wheel, PointerEventTypes.POINTERWHEEL);
+    }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._observer && element) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
-                this._observer = null;
-                this._wheel = null;
-            }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._observer && element) {
+            this.camera.getScene().onPointerObservable.remove(this._observer);
+            this._observer = null;
+            this._wheel = null;
         }
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "ArcRotateCameraMouseWheelInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "ArcRotateCameraMouseWheelInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "mousewheel";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "mousewheel";
     }
+}
 
-    (<any>CameraInputTypes)["ArcRotateCameraMouseWheelInput"] = ArcRotateCameraMouseWheelInput;
+(<any>CameraInputTypes)["ArcRotateCameraMouseWheelInput"] = ArcRotateCameraMouseWheelInput;

+ 341 - 341
src/Cameras/Inputs/arcRotateCameraPointersInput.ts

@@ -6,225 +6,251 @@ import { ArcRotateCamera } from "../../Cameras/arcRotateCamera";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
 import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
 
+/**
+ * Manage the pointers inputs to control an arc rotate camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class ArcRotateCameraPointersInput implements ICameraInput<ArcRotateCamera> {
     /**
-     * Manage the pointers inputs to control an arc rotate camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class ArcRotateCameraPointersInput implements ICameraInput<ArcRotateCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: ArcRotateCamera;
-
-        /**
-         * Defines the buttons associated with the input to handle camera move.
-         */
-        @serialize()
-        public buttons = [0, 1, 2];
-
-        /**
-         * Defines the pointer angular sensibility  along the X axis or how fast is the camera rotating.
-         */
-        @serialize()
-        public angularSensibilityX = 1000.0;
-
-        /**
-         * Defines the pointer angular sensibility along the Y axis or how fast is the camera rotating.
-         */
-        @serialize()
-        public angularSensibilityY = 1000.0;
-
-        /**
-         * Defines the pointer pinch precision or how fast is the camera zooming.
-         */
-        @serialize()
-        public pinchPrecision = 12.0;
-
-        /**
-         * pinchDeltaPercentage will be used instead of pinchPrecision if different from 0.
-         * It defines the percentage of current camera.radius to use as delta when pinch zoom is used.
-         */
-        @serialize()
-        public pinchDeltaPercentage = 0;
-
-        /**
-         * Defines the pointer panning sensibility or how fast is the camera moving.
-         */
-        @serialize()
-        public panningSensibility: number = 1000.0;
-
-        /**
-         * Defines whether panning (2 fingers swipe) is enabled through multitouch.
-         */
-        @serialize()
-        public multiTouchPanning: boolean = true;
-
-        /**
-         * Defines whether panning is enabled for both pan (2 fingers swipe) and zoom (pinch) through multitouch.
-         */
-        @serialize()
-        public multiTouchPanAndZoom: boolean = true;
-
-        /**
-         * Revers pinch action direction.
-         */
-        public pinchInwards = true;
-
-        private _isPanClick: boolean = false;
-        private _pointerInput: (p: PointerInfo, s: EventState) => 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: Event) => void>;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            var engine = this.camera.getEngine();
-            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;
-            var previousMultiTouchPanPosition: { x: number, y: number, isPaning: boolean, isPinching: boolean } = { x: 0, y: 0, isPaning: false, isPinching: false };
-
-            this._pointerInput = (p, s) => {
-                var evt = <PointerEvent>p.event;
-                let isTouch = (<any>p.event).pointerType === "touch";
-
-                if (engine.isInVRExclusivePointerMode) {
-                    return;
-                }
+    public camera: ArcRotateCamera;
 
-                if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
-                    return;
-                }
+    /**
+     * Defines the buttons associated with the input to handle camera move.
+     */
+    @serialize()
+    public buttons = [0, 1, 2];
 
-                let srcElement = <HTMLElement>(evt.srcElement || evt.target);
+    /**
+     * Defines the pointer angular sensibility  along the X axis or how fast is the camera rotating.
+     */
+    @serialize()
+    public angularSensibilityX = 1000.0;
 
-                if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
-                    try {
-                        srcElement.setPointerCapture(evt.pointerId);
-                    } catch (e) {
-                        //Nothing to do with the error. Execution will continue.
-                    }
+    /**
+     * Defines the pointer angular sensibility along the Y axis or how fast is the camera rotating.
+     */
+    @serialize()
+    public angularSensibilityY = 1000.0;
 
-                    // Manage panning with pan button click
-                    this._isPanClick = evt.button === this.camera._panningMouseButton;
+    /**
+     * Defines the pointer pinch precision or how fast is the camera zooming.
+     */
+    @serialize()
+    public pinchPrecision = 12.0;
 
-                    // manage pointers
-                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
-                    if (pointA === null) {
-                        pointA = cacheSoloPointer;
-                    }
-                    else if (pointB === null) {
-                        pointB = cacheSoloPointer;
-                    }
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                        element.focus();
-                    }
+    /**
+     * pinchDeltaPercentage will be used instead of pinchPrecision if different from 0.
+     * It defines the percentage of current camera.radius to use as delta when pinch zoom is used.
+     */
+    @serialize()
+    public pinchDeltaPercentage = 0;
+
+    /**
+     * Defines the pointer panning sensibility or how fast is the camera moving.
+     */
+    @serialize()
+    public panningSensibility: number = 1000.0;
+
+    /**
+     * Defines whether panning (2 fingers swipe) is enabled through multitouch.
+     */
+    @serialize()
+    public multiTouchPanning: boolean = true;
+
+    /**
+     * Defines whether panning is enabled for both pan (2 fingers swipe) and zoom (pinch) through multitouch.
+     */
+    @serialize()
+    public multiTouchPanAndZoom: boolean = true;
+
+    /**
+     * Revers pinch action direction.
+     */
+    public pinchInwards = true;
+
+    private _isPanClick: boolean = false;
+    private _pointerInput: (p: PointerInfo, s: EventState) => 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: Event) => void>;
+
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        var engine = this.camera.getEngine();
+        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;
+        var previousMultiTouchPanPosition: { x: number, y: number, isPaning: boolean, isPinching: boolean } = { x: 0, y: 0, isPaning: false, isPinching: false };
+
+        this._pointerInput = (p, s) => {
+            var evt = <PointerEvent>p.event;
+            let isTouch = (<any>p.event).pointerType === "touch";
+
+            if (engine.isInVRExclusivePointerMode) {
+                return;
+            }
+
+            if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
+                return;
+            }
+
+            let srcElement = <HTMLElement>(evt.srcElement || evt.target);
+
+            if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
+                try {
+                    srcElement.setPointerCapture(evt.pointerId);
+                } catch (e) {
+                    //Nothing to do with the error. Execution will continue.
                 }
-                else if (p.type === PointerEventTypes.POINTERDOUBLETAP) {
-                    if (this.camera.useInputToRestoreState) {
-                        this.camera.restoreState();
-                    }
+
+                // Manage panning with pan button click
+                this._isPanClick = evt.button === this.camera._panningMouseButton;
+
+                // manage pointers
+                cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                if (pointA === null) {
+                    pointA = cacheSoloPointer;
                 }
-                else if (p.type === PointerEventTypes.POINTERUP && srcElement) {
-                    try {
-                        srcElement.releasePointerCapture(evt.pointerId);
-                    } catch (e) {
-                        //Nothing to do with the error.
-                    }
+                else if (pointB === null) {
+                    pointB = cacheSoloPointer;
+                }
+                if (!noPreventDefault) {
+                    evt.preventDefault();
+                    element.focus();
+                }
+            }
+            else if (p.type === PointerEventTypes.POINTERDOUBLETAP) {
+                if (this.camera.useInputToRestoreState) {
+                    this.camera.restoreState();
+                }
+            }
+            else if (p.type === PointerEventTypes.POINTERUP && srcElement) {
+                try {
+                    srcElement.releasePointerCapture(evt.pointerId);
+                } catch (e) {
+                    //Nothing to do with the error.
+                }
+
+                cacheSoloPointer = null;
+                previousPinchSquaredDistance = 0;
+                previousMultiTouchPanPosition.isPaning = false;
+                previousMultiTouchPanPosition.isPinching = false;
+                twoFingerActivityCount = 0;
+                initialDistance = 0;
 
-                    cacheSoloPointer = null;
-                    previousPinchSquaredDistance = 0;
-                    previousMultiTouchPanPosition.isPaning = false;
-                    previousMultiTouchPanPosition.isPinching = false;
-                    twoFingerActivityCount = 0;
-                    initialDistance = 0;
+                if (!isTouch) {
+                    pointB = null; // Mouse and pen are mono pointer
+                }
 
-                    if (!isTouch) {
-                        pointB = null; // Mouse and pen are mono pointer
+                //would be better to use pointers.remove(evt.pointerId) for multitouch gestures,
+                //but emptying completly pointers collection is required to fix a bug on iPhone :
+                //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 = 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 = null;
+                        cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
                     }
-
-                    //would be better to use pointers.remove(evt.pointerId) for multitouch gestures,
-                    //but emptying completly pointers collection is required to fix a bug on iPhone :
-                    //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 = null;
+                    else if (pointA && pointB && pointB.pointerId == evt.pointerId) {
+                        pointB = null;
+                        cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
                     }
                     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 = null;
-                            cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
-                        }
-                        else if (pointA && pointB && pointB.pointerId == evt.pointerId) {
-                            pointB = null;
-                            cacheSoloPointer = { x: pointA.x, y: pointA.y, pointerId: pointA.pointerId, type: evt.pointerType };
-                        }
-                        else {
-                            pointA = pointB = null;
-                        }
+                        pointA = pointB = null;
                     }
+                }
+
+                if (!noPreventDefault) {
+                    evt.preventDefault();
+                }
+            } else if (p.type === PointerEventTypes.POINTERMOVE) {
+                if (!noPreventDefault) {
+                    evt.preventDefault();
+                }
 
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
+                // One button down
+                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;
+                        this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
+                    } else {
+                        var offsetX = evt.clientX - cacheSoloPointer.x;
+                        var offsetY = evt.clientY - cacheSoloPointer.y;
+                        this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
+                        this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
                     }
-                } else if (p.type === PointerEventTypes.POINTERMOVE) {
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
+
+                    cacheSoloPointer.x = evt.clientX;
+                    cacheSoloPointer.y = evt.clientY;
+                }
+
+                // Two buttons down: pinch/pan
+                else if (pointA && pointB) {
+                    //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be useful to force preventDefault to avoid html page scroll/zoom in some mobile browsers
+                    var ed = (pointA.pointerId === evt.pointerId) ? pointA : pointB;
+                    ed.x = evt.clientX;
+                    ed.y = evt.clientY;
+                    var direction = this.pinchInwards ? 1 : -1;
+                    var distX = pointA.x - pointB.x;
+                    var distY = pointA.y - pointB.y;
+                    var pinchSquaredDistance = (distX * distX) + (distY * distY);
+                    var pinchDistance = Math.sqrt(pinchSquaredDistance);
+
+                    if (previousPinchSquaredDistance === 0) {
+                        initialDistance = pinchDistance;
+                        previousPinchSquaredDistance = pinchSquaredDistance;
+                        previousMultiTouchPanPosition.x = (pointA.x + pointB.x) / 2;
+                        previousMultiTouchPanPosition.y = (pointA.y + pointB.y) / 2;
+                        return;
                     }
 
-                    // One button down
-                    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;
-                            this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
+                    if (this.multiTouchPanAndZoom) {
+                        if (this.pinchDeltaPercentage) {
+                            this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * this.camera.radius * this.pinchDeltaPercentage;
                         } else {
-                            var offsetX = evt.clientX - cacheSoloPointer.x;
-                            var offsetY = evt.clientY - cacheSoloPointer.y;
-                            this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
-                            this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
+                            this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                (this.pinchPrecision *
+                                    ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
+                                    direction);
                         }
 
-                        cacheSoloPointer.x = evt.clientX;
-                        cacheSoloPointer.y = evt.clientY;
-                    }
+                        if (this.panningSensibility !== 0) {
+                            var pointersCenterX = (pointA.x + pointB.x) / 2;
+                            var pointersCenterY = (pointA.y + pointB.y) / 2;
+                            var pointersCenterDistX = pointersCenterX - previousMultiTouchPanPosition.x;
+                            var pointersCenterDistY = pointersCenterY - previousMultiTouchPanPosition.y;
+
+                            previousMultiTouchPanPosition.x = pointersCenterX;
+                            previousMultiTouchPanPosition.y = pointersCenterY;
 
-                    // Two buttons down: pinch/pan
-                    else if (pointA && pointB) {
-                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be useful to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                        var ed = (pointA.pointerId === evt.pointerId) ? pointA : pointB;
-                        ed.x = evt.clientX;
-                        ed.y = evt.clientY;
-                        var direction = this.pinchInwards ? 1 : -1;
-                        var distX = pointA.x - pointB.x;
-                        var distY = pointA.y - pointB.y;
-                        var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                        var pinchDistance = Math.sqrt(pinchSquaredDistance);
-
-                        if (previousPinchSquaredDistance === 0) {
-                            initialDistance = pinchDistance;
-                            previousPinchSquaredDistance = pinchSquaredDistance;
-                            previousMultiTouchPanPosition.x = (pointA.x + pointB.x) / 2;
-                            previousMultiTouchPanPosition.y = (pointA.y + pointB.y) / 2;
-                            return;
+                            this.camera.inertialPanningX += -(pointersCenterDistX) / (this.panningSensibility);
+                            this.camera.inertialPanningY += (pointersCenterDistY) / (this.panningSensibility);
                         }
+                    }
+                    else {
+                        twoFingerActivityCount++;
 
-                        if (this.multiTouchPanAndZoom) {
+                        if (previousMultiTouchPanPosition.isPinching || (twoFingerActivityCount < 20 && Math.abs(pinchDistance - initialDistance) > this.camera.pinchToPanMaxDistance)) {
                             if (this.pinchDeltaPercentage) {
                                 this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * this.camera.radius * this.pinchDeltaPercentage;
                             } else {
@@ -233,189 +259,163 @@ import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
                                         ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
                                         direction);
                             }
-
-                            if (this.panningSensibility !== 0) {
-                                var pointersCenterX = (pointA.x + pointB.x) / 2;
-                                var pointersCenterY = (pointA.y + pointB.y) / 2;
-                                var pointersCenterDistX = pointersCenterX - previousMultiTouchPanPosition.x;
-                                var pointersCenterDistY = pointersCenterY - previousMultiTouchPanPosition.y;
-
-                                previousMultiTouchPanPosition.x = pointersCenterX;
-                                previousMultiTouchPanPosition.y = pointersCenterY;
-
-                                this.camera.inertialPanningX += -(pointersCenterDistX) / (this.panningSensibility);
-                                this.camera.inertialPanningY += (pointersCenterDistY) / (this.panningSensibility);
-                            }
+                            previousMultiTouchPanPosition.isPaning = false;
+                            previousMultiTouchPanPosition.isPinching = true;
                         }
                         else {
-                            twoFingerActivityCount++;
-
-                            if (previousMultiTouchPanPosition.isPinching || (twoFingerActivityCount < 20 && Math.abs(pinchDistance - initialDistance) > this.camera.pinchToPanMaxDistance)) {
-                                if (this.pinchDeltaPercentage) {
-                                    this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * this.camera.radius * this.pinchDeltaPercentage;
-                                } else {
-                                    this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
-                                        (this.pinchPrecision *
-                                            ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
-                                            direction);
-                                }
-                                previousMultiTouchPanPosition.isPaning = false;
-                                previousMultiTouchPanPosition.isPinching = true;
-                            }
-                            else {
-                                if (cacheSoloPointer && cacheSoloPointer.pointerId === ed.pointerId && this.panningSensibility !== 0 && this.multiTouchPanning) {
-                                    if (!previousMultiTouchPanPosition.isPaning) {
-                                        previousMultiTouchPanPosition.isPaning = true;
-                                        previousMultiTouchPanPosition.isPinching = false;
-                                        previousMultiTouchPanPosition.x = ed.x;
-                                        previousMultiTouchPanPosition.y = ed.y;
-                                        return;
-                                    }
-
-                                    this.camera.inertialPanningX += -(ed.x - previousMultiTouchPanPosition.x) / (this.panningSensibility);
-                                    this.camera.inertialPanningY += (ed.y - previousMultiTouchPanPosition.y) / (this.panningSensibility);
+                            if (cacheSoloPointer && cacheSoloPointer.pointerId === ed.pointerId && this.panningSensibility !== 0 && this.multiTouchPanning) {
+                                if (!previousMultiTouchPanPosition.isPaning) {
+                                    previousMultiTouchPanPosition.isPaning = true;
+                                    previousMultiTouchPanPosition.isPinching = false;
+                                    previousMultiTouchPanPosition.x = ed.x;
+                                    previousMultiTouchPanPosition.y = ed.y;
+                                    return;
                                 }
-                            }
 
-                            if (cacheSoloPointer && cacheSoloPointer.pointerId === evt.pointerId) {
-                                previousMultiTouchPanPosition.x = ed.x;
-                                previousMultiTouchPanPosition.y = ed.y;
+                                this.camera.inertialPanningX += -(ed.x - previousMultiTouchPanPosition.x) / (this.panningSensibility);
+                                this.camera.inertialPanningY += (ed.y - previousMultiTouchPanPosition.y) / (this.panningSensibility);
                             }
                         }
 
-                        previousPinchSquaredDistance = pinchSquaredDistance;
+                        if (cacheSoloPointer && cacheSoloPointer.pointerId === evt.pointerId) {
+                            previousMultiTouchPanPosition.x = ed.x;
+                            previousMultiTouchPanPosition.y = ed.y;
+                        }
                     }
+
+                    previousPinchSquaredDistance = pinchSquaredDistance;
                 }
-            };
+            }
+        };
 
-            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE | PointerEventTypes.POINTERDOUBLETAP);
+        this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE | PointerEventTypes.POINTERDOUBLETAP);
 
-            this._onContextMenu = (evt) => {
-                evt.preventDefault();
-            };
+        this._onContextMenu = (evt) => {
+            evt.preventDefault();
+        };
 
-            if (!this.camera._useCtrlForPanning) {
-                element.addEventListener("contextmenu", this._onContextMenu, false);
-            }
-
-            this._onLostFocus = () => {
-                //this._keys = [];
-                pointA = pointB = null;
-                previousPinchSquaredDistance = 0;
-                previousMultiTouchPanPosition.isPaning = false;
-                previousMultiTouchPanPosition.isPinching = false;
-                twoFingerActivityCount = 0;
-                cacheSoloPointer = null;
-                initialDistance = 0;
-            };
+        if (!this.camera._useCtrlForPanning) {
+            element.addEventListener("contextmenu", this._onContextMenu, false);
+        }
 
-            this._onMouseMove = (evt) => {
-                if (!engine.isPointerLock) {
-                    return;
-                }
+        this._onLostFocus = () => {
+            //this._keys = [];
+            pointA = pointB = null;
+            previousPinchSquaredDistance = 0;
+            previousMultiTouchPanPosition.isPaning = false;
+            previousMultiTouchPanPosition.isPinching = false;
+            twoFingerActivityCount = 0;
+            cacheSoloPointer = null;
+            initialDistance = 0;
+        };
+
+        this._onMouseMove = (evt) => {
+            if (!engine.isPointerLock) {
+                return;
+            }
 
-                var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
-                var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
+            var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
+            var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
 
-                this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
-                this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
+            this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
+            this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
 
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-            };
+            if (!noPreventDefault) {
+                evt.preventDefault();
+            }
+        };
 
-            this._onGestureStart = (e) => {
-                if (window.MSGesture === undefined) {
-                    return;
-                }
+        this._onGestureStart = (e) => {
+            if (window.MSGesture === undefined) {
+                return;
+            }
 
-                if (!this._MSGestureHandler) {
-                    this._MSGestureHandler = new MSGesture();
-                    this._MSGestureHandler.target = element;
-                }
+            if (!this._MSGestureHandler) {
+                this._MSGestureHandler = new MSGesture();
+                this._MSGestureHandler.target = element;
+            }
 
-                this._MSGestureHandler.addPointer(e.pointerId);
-            };
+            this._MSGestureHandler.addPointer(e.pointerId);
+        };
 
-            this._onGesture = (e) => {
-                this.camera.radius *= e.scale;
+        this._onGesture = (e) => {
+            this.camera.radius *= e.scale;
 
-                if (e.preventDefault) {
-                    if (!noPreventDefault) {
-                        e.stopPropagation();
-                        e.preventDefault();
-                    }
+            if (e.preventDefault) {
+                if (!noPreventDefault) {
+                    e.stopPropagation();
+                    e.preventDefault();
                 }
-            };
+            }
+        };
 
-            element.addEventListener("mousemove", this._onMouseMove, false);
-            element.addEventListener("MSPointerDown", <EventListener>this._onGestureStart, false);
-            element.addEventListener("MSGestureChange", <EventListener>this._onGesture, false);
+        element.addEventListener("mousemove", this._onMouseMove, false);
+        element.addEventListener("MSPointerDown", <EventListener>this._onGestureStart, false);
+        element.addEventListener("MSGestureChange", <EventListener>this._onGesture, false);
+
+        Tools.RegisterTopRootEvents([
+            { name: "blur", handler: this._onLostFocus }
+        ]);
+    }
 
-            Tools.RegisterTopRootEvents([
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._onLostFocus) {
+            Tools.UnregisterTopRootEvents([
                 { name: "blur", handler: this._onLostFocus }
             ]);
         }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._onLostFocus) {
-                Tools.UnregisterTopRootEvents([
-                    { name: "blur", handler: this._onLostFocus }
-                ]);
-            }
-
-            if (element && this._observer) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
-                this._observer = null;
+        if (element && this._observer) {
+            this.camera.getScene().onPointerObservable.remove(this._observer);
+            this._observer = null;
 
-                if (this._onContextMenu) {
-                    element.removeEventListener("contextmenu", this._onContextMenu);
-                }
+            if (this._onContextMenu) {
+                element.removeEventListener("contextmenu", this._onContextMenu);
+            }
 
-                if (this._onMouseMove) {
-                    element.removeEventListener("mousemove", this._onMouseMove);
-                }
+            if (this._onMouseMove) {
+                element.removeEventListener("mousemove", this._onMouseMove);
+            }
 
-                if (this._onGestureStart) {
-                    element.removeEventListener("MSPointerDown", <EventListener>this._onGestureStart);
-                }
+            if (this._onGestureStart) {
+                element.removeEventListener("MSPointerDown", <EventListener>this._onGestureStart);
+            }
 
-                if (this._onGesture) {
-                    element.removeEventListener("MSGestureChange", <EventListener>this._onGesture);
-                }
+            if (this._onGesture) {
+                element.removeEventListener("MSGestureChange", <EventListener>this._onGesture);
+            }
 
-                this._isPanClick = false;
-                this.pinchInwards = true;
+            this._isPanClick = false;
+            this.pinchInwards = true;
 
-                this._onMouseMove = null;
-                this._onGestureStart = null;
-                this._onGesture = null;
-                this._MSGestureHandler = null;
-                this._onLostFocus = null;
-                this._onContextMenu = null;
-            }
+            this._onMouseMove = null;
+            this._onGestureStart = null;
+            this._onGesture = null;
+            this._MSGestureHandler = null;
+            this._onLostFocus = null;
+            this._onContextMenu = null;
         }
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "ArcRotateCameraPointersInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "ArcRotateCameraPointersInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "pointers";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "pointers";
     }
+}
 
-    (<any>CameraInputTypes)["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
+(<any>CameraInputTypes)["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;

+ 82 - 82
src/Cameras/Inputs/arcRotateCameraVRDeviceOrientationInput.ts

@@ -2,101 +2,101 @@ import { Nullable } from "../../types";
 import { ArcRotateCamera } from "../../Cameras/arcRotateCamera";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
 
+/**
+ * Manage the device orientation inputs (gyroscope) to control an arc rotate camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class ArcRotateCameraVRDeviceOrientationInput implements ICameraInput<ArcRotateCamera> {
     /**
-     * Manage the device orientation inputs (gyroscope) to control an arc rotate camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class ArcRotateCameraVRDeviceOrientationInput implements ICameraInput<ArcRotateCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: ArcRotateCamera;
-
-        /**
-         * Defines a correction factor applied on the alpha value retrieved from the orientation events.
-         */
-        public alphaCorrection = 1;
-
-        /**
-         * Defines a correction factor applied on the gamma value retrieved from the orientation events.
-         */
-        public gammaCorrection = 1;
-
-        private _alpha = 0;
-        private _gamma = 0;
-        private _dirty = false;
-
-        private _deviceOrientationHandler: () => void;
-
-        /**
-         * Instantiate a new ArcRotateCameraVRDeviceOrientationInput.
-         */
-        constructor() {
-            this._deviceOrientationHandler = this._onOrientationEvent.bind(this);
-        }
+    public camera: ArcRotateCamera;
 
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            this.camera.attachControl(element, noPreventDefault);
-            window.addEventListener("deviceorientation", this._deviceOrientationHandler);
-        }
+    /**
+     * Defines a correction factor applied on the alpha value retrieved from the orientation events.
+     */
+    public alphaCorrection = 1;
 
-        /** @hidden */
-        public _onOrientationEvent(evt: DeviceOrientationEvent): void {
-            if (evt.alpha !== null) {
-                this._alpha = (+evt.alpha | 0) * this.alphaCorrection;
-            }
+    /**
+     * Defines a correction factor applied on the gamma value retrieved from the orientation events.
+     */
+    public gammaCorrection = 1;
 
-            if (evt.gamma !== null) {
-                this._gamma = (+evt.gamma | 0) * this.gammaCorrection;
-            }
-            this._dirty = true;
-        }
+    private _alpha = 0;
+    private _gamma = 0;
+    private _dirty = false;
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this._dirty) {
-                this._dirty = false;
+    private _deviceOrientationHandler: () => void;
 
-                if (this._gamma < 0) {
-                    this._gamma = 180 + this._gamma;
-                }
+    /**
+     * Instantiate a new ArcRotateCameraVRDeviceOrientationInput.
+     */
+    constructor() {
+        this._deviceOrientationHandler = this._onOrientationEvent.bind(this);
+    }
 
-                this.camera.alpha = (-this._alpha / 180.0 * Math.PI) % Math.PI * 2;
-                this.camera.beta = (this._gamma / 180.0 * Math.PI);
-            }
-        }
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        this.camera.attachControl(element, noPreventDefault);
+        window.addEventListener("deviceorientation", this._deviceOrientationHandler);
+    }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            window.removeEventListener("deviceorientation", this._deviceOrientationHandler);
+    /** @hidden */
+    public _onOrientationEvent(evt: DeviceOrientationEvent): void {
+        if (evt.alpha !== null) {
+            this._alpha = (+evt.alpha | 0) * this.alphaCorrection;
         }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "ArcRotateCameraVRDeviceOrientationInput";
+        if (evt.gamma !== null) {
+            this._gamma = (+evt.gamma | 0) * this.gammaCorrection;
         }
+        this._dirty = true;
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "VRDeviceOrientation";
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this._dirty) {
+            this._dirty = false;
+
+            if (this._gamma < 0) {
+                this._gamma = 180 + this._gamma;
+            }
+
+            this.camera.alpha = (-this._alpha / 180.0 * Math.PI) % Math.PI * 2;
+            this.camera.beta = (this._gamma / 180.0 * Math.PI);
         }
     }
 
-    (<any>CameraInputTypes)["ArcRotateCameraVRDeviceOrientationInput"] = ArcRotateCameraVRDeviceOrientationInput;
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        window.removeEventListener("deviceorientation", this._deviceOrientationHandler);
+    }
+
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "ArcRotateCameraVRDeviceOrientationInput";
+    }
+
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "VRDeviceOrientation";
+    }
+}
+
+(<any>CameraInputTypes)["ArcRotateCameraVRDeviceOrientationInput"] = ArcRotateCameraVRDeviceOrientationInput;

+ 171 - 171
src/Cameras/Inputs/flyCameraKeyboardInput.ts

@@ -8,195 +8,195 @@ import { KeyboardInfo, KeyboardEventTypes } from "../../Events/keyboardEvents";
 import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 
+/**
+ * Listen to keyboard events to control the camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FlyCameraKeyboardInput implements ICameraInput<FlyCamera> {
     /**
-     * Listen to keyboard events to control the camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class FlyCameraKeyboardInput implements ICameraInput<FlyCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FlyCamera;
-
-        /**
-         * The list of keyboard keys used to control the forward move of the camera.
-         */
-        @serialize()
-        public keysForward = [87];
-
-        /**
-         * The list of keyboard keys used to control the backward move of the camera.
-         */
-        @serialize()
-        public keysBackward = [83];
-
-        /**
-         * The list of keyboard keys used to control the forward move of the camera.
-         */
-        @serialize()
-        public keysUp = [69];
-
-        /**
-         * The list of keyboard keys used to control the backward move of the camera.
-         */
-        @serialize()
-        public keysDown = [81];
-
-        /**
-         * The list of keyboard keys used to control the right strafe move of the camera.
-         */
-        @serialize()
-        public keysRight = [68];
-
-        /**
-         * The list of keyboard keys used to control the left strafe move of the camera.
-         */
-        @serialize()
-        public keysLeft = [65];
-
-        private _keys = new Array<number>();
-        private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
-        private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
-        private _engine: Engine;
-        private _scene: Scene;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            if (this._onCanvasBlurObserver) {
-                return;
-            }
+    public camera: FlyCamera;
+
+    /**
+     * The list of keyboard keys used to control the forward move of the camera.
+     */
+    @serialize()
+    public keysForward = [87];
+
+    /**
+     * The list of keyboard keys used to control the backward move of the camera.
+     */
+    @serialize()
+    public keysBackward = [83];
+
+    /**
+     * The list of keyboard keys used to control the forward move of the camera.
+     */
+    @serialize()
+    public keysUp = [69];
+
+    /**
+     * The list of keyboard keys used to control the backward move of the camera.
+     */
+    @serialize()
+    public keysDown = [81];
+
+    /**
+     * The list of keyboard keys used to control the right strafe move of the camera.
+     */
+    @serialize()
+    public keysRight = [68];
 
-            this._scene = this.camera.getScene();
-            this._engine = this._scene.getEngine();
-
-            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
-                this._keys = [];
-            });
-
-            this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
-                let evt = info.event;
-
-                if (info.type === KeyboardEventTypes.KEYDOWN) {
-                    if (this.keysForward.indexOf(evt.keyCode) !== -1 ||
-                        this.keysBackward.indexOf(evt.keyCode) !== -1 ||
-                        this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                        this.keysRight.indexOf(evt.keyCode) !== -1) {
-                        var index = this._keys.indexOf(evt.keyCode);
-
-                        if (index === -1) {
-                            this._keys.push(evt.keyCode);
-                        }
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
+    /**
+     * The list of keyboard keys used to control the left strafe move of the camera.
+     */
+    @serialize()
+    public keysLeft = [65];
+
+    private _keys = new Array<number>();
+    private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
+    private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
+    private _engine: Engine;
+    private _scene: Scene;
+
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        if (this._onCanvasBlurObserver) {
+            return;
+        }
+
+        this._scene = this.camera.getScene();
+        this._engine = this._scene.getEngine();
+
+        this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
+            this._keys = [];
+        });
+
+        this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
+            let evt = info.event;
+
+            if (info.type === KeyboardEventTypes.KEYDOWN) {
+                if (this.keysForward.indexOf(evt.keyCode) !== -1 ||
+                    this.keysBackward.indexOf(evt.keyCode) !== -1 ||
+                    this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                    this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                    this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                    this.keysRight.indexOf(evt.keyCode) !== -1) {
+                    var index = this._keys.indexOf(evt.keyCode);
+
+                    if (index === -1) {
+                        this._keys.push(evt.keyCode);
                     }
-                } else {
-                    if (this.keysForward.indexOf(evt.keyCode) !== -1 ||
-                        this.keysBackward.indexOf(evt.keyCode) !== -1 ||
-                        this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                        this.keysRight.indexOf(evt.keyCode) !== -1) {
-                        var index = this._keys.indexOf(evt.keyCode);
-
-                        if (index >= 0) {
-                            this._keys.splice(index, 1);
-                        }
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
                     }
                 }
-            });
-        }
-
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._scene) {
-                if (this._onKeyboardObserver) {
-                    this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+            } else {
+                if (this.keysForward.indexOf(evt.keyCode) !== -1 ||
+                    this.keysBackward.indexOf(evt.keyCode) !== -1 ||
+                    this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                    this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                    this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                    this.keysRight.indexOf(evt.keyCode) !== -1) {
+                    var index = this._keys.indexOf(evt.keyCode);
+
+                    if (index >= 0) {
+                        this._keys.splice(index, 1);
+                    }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
                 }
+            }
+        });
+    }
 
-                if (this._onCanvasBlurObserver) {
-                    this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
-                }
-                this._onKeyboardObserver = null;
-                this._onCanvasBlurObserver = null;
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._scene) {
+            if (this._onKeyboardObserver) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
             }
-            this._keys = [];
-        }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FlyCameraKeyboardInput";
+            if (this._onCanvasBlurObserver) {
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            }
+            this._onKeyboardObserver = null;
+            this._onCanvasBlurObserver = null;
         }
+        this._keys = [];
+    }
 
-        /** @hidden */
-        public _onLostFocus(e: FocusEvent): void {
-            this._keys = [];
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FlyCameraKeyboardInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "keyboard";
-        }
+    /** @hidden */
+    public _onLostFocus(e: FocusEvent): void {
+        this._keys = [];
+    }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this._onKeyboardObserver) {
-                var camera = this.camera;
-                // Keyboard
-                for (var index = 0; index < this._keys.length; index++) {
-                    var keyCode = this._keys[index];
-                    var speed = camera._computeLocalCameraSpeed();
-
-                    if (this.keysForward.indexOf(keyCode) !== -1) {
-                      camera._localDirection.copyFromFloats(0, 0, speed);
-                    } else
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "keyboard";
+    }
+
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this._onKeyboardObserver) {
+            var camera = this.camera;
+            // Keyboard
+            for (var index = 0; index < this._keys.length; index++) {
+                var keyCode = this._keys[index];
+                var speed = camera._computeLocalCameraSpeed();
+
+                if (this.keysForward.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, speed);
+                } else
                     if (this.keysBackward.indexOf(keyCode) !== -1) {
-                      camera._localDirection.copyFromFloats(0, 0, -speed);
+                        camera._localDirection.copyFromFloats(0, 0, -speed);
                     } else
-                    if (this.keysUp.indexOf(keyCode) !== -1) {
-                      camera._localDirection.copyFromFloats(0, speed, 0);
-                    } else
-                    if (this.keysDown.indexOf(keyCode) !== -1) {
-                      camera._localDirection.copyFromFloats(0, -speed, 0);
-                    } else
-                    if (this.keysRight.indexOf(keyCode) !== -1) {
-                      camera._localDirection.copyFromFloats(speed, 0, 0);
-                    } else
-                    if (this.keysLeft.indexOf(keyCode) !== -1) {
-                      camera._localDirection.copyFromFloats(-speed, 0, 0);
-                    }
-
-                    if (camera.getScene().useRightHandedSystem) {
-                        camera._localDirection.z *= -1;
-                    }
-
-                    camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
-                    Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
-                    camera.cameraDirection.addInPlace(camera._transformedDirection);
+                        if (this.keysUp.indexOf(keyCode) !== -1) {
+                            camera._localDirection.copyFromFloats(0, speed, 0);
+                        } else
+                            if (this.keysDown.indexOf(keyCode) !== -1) {
+                                camera._localDirection.copyFromFloats(0, -speed, 0);
+                            } else
+                                if (this.keysRight.indexOf(keyCode) !== -1) {
+                                    camera._localDirection.copyFromFloats(speed, 0, 0);
+                                } else
+                                    if (this.keysLeft.indexOf(keyCode) !== -1) {
+                                        camera._localDirection.copyFromFloats(-speed, 0, 0);
+                                    }
+
+                if (camera.getScene().useRightHandedSystem) {
+                    camera._localDirection.z *= -1;
                 }
+
+                camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
+                Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
+                camera.cameraDirection.addInPlace(camera._transformedDirection);
             }
         }
     }
+}
 
-    (<any>CameraInputTypes)["FlyCameraKeyboardInput"] = FlyCameraKeyboardInput;
+(<any>CameraInputTypes)["FlyCameraKeyboardInput"] = FlyCameraKeyboardInput;

+ 240 - 240
src/Cameras/Inputs/flyCameraMouseInput.ts

@@ -6,184 +6,184 @@ import { FlyCamera } from "../../Cameras/flyCamera";
 import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
 import { Scene } from "../../scene";
 import { Quaternion, Axis } from "../../Maths/math";
+/**
+ * Listen to mouse events to control the camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
+    /**
+     * Defines the camera the input is attached to.
+     */
+    public camera: FlyCamera;
+
+    /**
+     * Defines if touch is enabled. (Default is true.)
+     */
+    public touchEnabled: boolean;
+
+    /**
+     * Defines the buttons associated with the input to handle camera rotation.
+     */
+    @serialize()
+    public buttons = [0, 1, 2];
+
+    /**
+     * Assign buttons for Yaw control.
+     */
+    public buttonsYaw: number[] = [-1, 0, 1];
+
+    /**
+    * Assign buttons for Pitch control.
+    */
+    public buttonsPitch: number[] = [-1, 0, 1];
+
+    /**
+    * Assign buttons for Roll control.
+    */
+    public buttonsRoll: number[] = [2];
+
+    /**
+     * Detect if any button is being pressed while mouse is moved.
+     * -1 = Mouse locked.
+     * 0 = Left button.
+     * 1 = Middle Button.
+     * 2 = Right Button.
+     */
+    public activeButton: number = -1;
+
+    /**
+     * Defines the pointer's angular sensibility, to control the camera rotation speed.
+     * Higher values reduce its sensitivity.
+     */
+    @serialize()
+    public angularSensibility = 1000.0;
+
+    private _mousemoveCallback: (e: MouseEvent) => void;
+    private _observer: Nullable<Observer<PointerInfo>>;
+    private _rollObserver: Nullable<Observer<Scene>>;
+    private previousPosition: Nullable<{ x: number, y: number }> = null;
+    private noPreventDefault: boolean | undefined;
+    private element: HTMLElement;
+
     /**
      * Listen to mouse events to control the camera.
+     * @param touchEnabled Define if touch is enabled. (Default is true.)
      * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
      */
-    export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FlyCamera;
-
-        /**
-         * Defines if touch is enabled. (Default is true.)
-         */
-        public touchEnabled: boolean;
-
-        /**
-         * Defines the buttons associated with the input to handle camera rotation.
-         */
-        @serialize()
-        public buttons = [0, 1, 2];
-
-        /**
-         * Assign buttons for Yaw control.
-         */
-        public buttonsYaw: number[]    = [-1, 0, 1];
-
-        /**
-        * Assign buttons for Pitch control.
-        */
-        public buttonsPitch: number[]  = [-1, 0, 1];
-
-        /**
-        * Assign buttons for Roll control.
-        */
-        public buttonsRoll: number[]   = [2];
-
-        /**
-         * Detect if any button is being pressed while mouse is moved.
-         * -1 = Mouse locked.
-         * 0 = Left button.
-         * 1 = Middle Button.
-         * 2 = Right Button.
-         */
-        public activeButton: number = -1;
-
-        /**
-         * Defines the pointer's angular sensibility, to control the camera rotation speed.
-         * Higher values reduce its sensitivity.
-         */
-        @serialize()
-        public angularSensibility = 1000.0;
-
-        private _mousemoveCallback: (e: MouseEvent) => void;
-        private _observer: Nullable<Observer<PointerInfo>>;
-        private _rollObserver: Nullable<Observer<Scene>>;
-        private previousPosition: Nullable<{ x: number, y: number }> = null;
-        private noPreventDefault: boolean | undefined;
-        private element: HTMLElement;
-
-        /**
-         * Listen to mouse events to control the camera.
-         * @param touchEnabled Define if touch is enabled. (Default is true.)
-         * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
-         */
-        constructor(touchEnabled = true) {
-        }
+    constructor(touchEnabled = true) {
+    }
 
-        /**
-         * Attach the mouse control to the HTML DOM element.
-         * @param element Defines the element that listens to the input events.
-         * @param noPreventDefault Defines whether events caught by the controls should call preventdefault().
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            this.element = element;
-            this.noPreventDefault = noPreventDefault;
-
-            this._observer = this.camera.getScene().onPointerObservable.add(
-                (p: any, s: any) => {
-                    this._pointerInput(p, s);
-                },
-                PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE
-            );
-
-            // Correct Roll by rate, if enabled.
-            this._rollObserver = this.camera.getScene().onBeforeRenderObservable.add(
-                () => {
-                    if (this.camera.rollCorrect) {
-                        this.camera.restoreRoll(this.camera.rollCorrect);
-                    }
+    /**
+     * Attach the mouse control to the HTML DOM element.
+     * @param element Defines the element that listens to the input events.
+     * @param noPreventDefault Defines whether events caught by the controls should call preventdefault().
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        this.element = element;
+        this.noPreventDefault = noPreventDefault;
+
+        this._observer = this.camera.getScene().onPointerObservable.add(
+            (p: any, s: any) => {
+                this._pointerInput(p, s);
+            },
+            PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE
+        );
+
+        // Correct Roll by rate, if enabled.
+        this._rollObserver = this.camera.getScene().onBeforeRenderObservable.add(
+            () => {
+                if (this.camera.rollCorrect) {
+                    this.camera.restoreRoll(this.camera.rollCorrect);
                 }
-            );
-
-            // Helper function to keep 'this'.
-            this._mousemoveCallback = (e: any) => {
-                this._onMouseMove(e);
-            };
-            element.addEventListener("mousemove", this._mousemoveCallback, false);
-        }
+            }
+        );
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._observer && element) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
+        // Helper function to keep 'this'.
+        this._mousemoveCallback = (e: any) => {
+            this._onMouseMove(e);
+        };
+        element.addEventListener("mousemove", this._mousemoveCallback, false);
+    }
 
-                this.camera.getScene().onBeforeRenderObservable.remove(this._rollObserver);
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._observer && element) {
+            this.camera.getScene().onPointerObservable.remove(this._observer);
 
-                if (this._mousemoveCallback) {
-                    element.removeEventListener("mousemove", this._mousemoveCallback);
-                }
+            this.camera.getScene().onBeforeRenderObservable.remove(this._rollObserver);
 
-                this._observer = null;
-                this._rollObserver = null;
-                this.previousPosition = null;
-                this.noPreventDefault = undefined;
+            if (this._mousemoveCallback) {
+                element.removeEventListener("mousemove", this._mousemoveCallback);
             }
-        }
 
-        /**
-         * Gets the class name of the current input.
-         * @returns the class name.
-         */
-        public getClassName(): string {
-            return "FlyCameraMouseInput";
+            this._observer = null;
+            this._rollObserver = null;
+            this.previousPosition = null;
+            this.noPreventDefault = undefined;
         }
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input's friendly name.
-         */
-        public getSimpleName(): string {
-            return "mouse";
-        }
+    /**
+     * Gets the class name of the current input.
+     * @returns the class name.
+     */
+    public getClassName(): string {
+        return "FlyCameraMouseInput";
+    }
 
-        // Track mouse movement, when the pointer is not locked.
-        private _pointerInput(p: any, s: any): void {
-            var e = <PointerEvent>p.event;
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input's friendly name.
+     */
+    public getSimpleName(): string {
+        return "mouse";
+    }
 
-            let camera = this.camera;
-            let engine = camera.getEngine();
+    // Track mouse movement, when the pointer is not locked.
+    private _pointerInput(p: any, s: any): void {
+        var e = <PointerEvent>p.event;
 
-            if (engine.isInVRExclusivePointerMode) {
-                return;
-            }
+        let camera = this.camera;
+        let engine = camera.getEngine();
 
-            if (!this.touchEnabled && e.pointerType === "touch") {
-                return;
-            }
+        if (engine.isInVRExclusivePointerMode) {
+            return;
+        }
 
-            // Mouse is moved but an unknown mouse button is pressed.
-            if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(e.button) === -1) {
-                return;
-            }
+        if (!this.touchEnabled && e.pointerType === "touch") {
+            return;
+        }
 
-            var srcElement = <HTMLElement>(e.srcElement || e.target);
+        // Mouse is moved but an unknown mouse button is pressed.
+        if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(e.button) === -1) {
+            return;
+        }
 
-            // Mouse down.
-            if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
-                try {
-                    srcElement.setPointerCapture(e.pointerId);
-                } catch (e) {
-                    // Nothing to do with the error. Execution continues.
-                }
+        var srcElement = <HTMLElement>(e.srcElement || e.target);
 
-                this.previousPosition = {
-                    x: e.clientX,
-                    y: e.clientY
-                };
+        // Mouse down.
+        if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
+            try {
+                srcElement.setPointerCapture(e.pointerId);
+            } catch (e) {
+                // Nothing to do with the error. Execution continues.
+            }
 
-                this.activeButton = e.button;
+            this.previousPosition = {
+                x: e.clientX,
+                y: e.clientY
+            };
 
-                if (!this.noPreventDefault) {
-                    e.preventDefault();
-                    this.element.focus();
-                }
-            } else
+            this.activeButton = e.button;
+
+            if (!this.noPreventDefault) {
+                e.preventDefault();
+                this.element.focus();
+            }
+        } else
             // Mouse up.
             if (p.type === PointerEventTypes.POINTERUP && srcElement) {
                 try {
@@ -199,114 +199,114 @@ import { Quaternion, Axis } from "../../Maths/math";
                     e.preventDefault();
                 }
             } else
-            // Mouse move.
-            if (p.type === PointerEventTypes.POINTERMOVE) {
-                if (!this.previousPosition || engine.isPointerLock) {
-                    return;
-                }
+                // Mouse move.
+                if (p.type === PointerEventTypes.POINTERMOVE) {
+                    if (!this.previousPosition || engine.isPointerLock) {
+                        return;
+                    }
 
-                var offsetX = e.clientX - this.previousPosition.x;
-                var offsetY = e.clientY - this.previousPosition.y;
+                    var offsetX = e.clientX - this.previousPosition.x;
+                    var offsetY = e.clientY - this.previousPosition.y;
 
-                this.rotateCamera(offsetX, offsetY);
+                    this.rotateCamera(offsetX, offsetY);
 
-                this.previousPosition = {
-                    x: e.clientX,
-                    y: e.clientY
-                };
+                    this.previousPosition = {
+                        x: e.clientX,
+                        y: e.clientY
+                    };
 
-                if (!this.noPreventDefault) {
-                    e.preventDefault();
+                    if (!this.noPreventDefault) {
+                        e.preventDefault();
+                    }
                 }
-            }
-        }
+    }
 
-        // Track mouse movement, when pointer is locked.
-        private _onMouseMove(e: any): void {
-            let camera = this.camera;
-            let engine = camera.getEngine();
+    // Track mouse movement, when pointer is locked.
+    private _onMouseMove(e: any): void {
+        let camera = this.camera;
+        let engine = camera.getEngine();
 
-            if (!engine.isPointerLock || engine.isInVRExclusivePointerMode) {
-                return;
-            }
+        if (!engine.isPointerLock || engine.isInVRExclusivePointerMode) {
+            return;
+        }
 
-            var offsetX = e.movementX || e.mozMovementX || e.webkitMovementX || e.msMovementX || 0;
-            var offsetY = e.movementY || e.mozMovementY || e.webkitMovementY || e.msMovementY || 0;
+        var offsetX = e.movementX || e.mozMovementX || e.webkitMovementX || e.msMovementX || 0;
+        var offsetY = e.movementY || e.mozMovementY || e.webkitMovementY || e.msMovementY || 0;
 
-            this.rotateCamera(offsetX, offsetY);
+        this.rotateCamera(offsetX, offsetY);
 
-            this.previousPosition = null;
+        this.previousPosition = null;
 
-            if (!this.noPreventDefault) {
-                e.preventDefault();
-            }
+        if (!this.noPreventDefault) {
+            e.preventDefault();
         }
+    }
 
-        /**
-         * Rotate camera by mouse offset.
-         */
-        private rotateCamera(offsetX: number, offsetY: number): void {
-            let camera = this.camera;
-            let scene = this.camera.getScene();
-
-            if (scene.useRightHandedSystem) {
-              offsetX *= -1;
-            }
-
-            if (camera.parent && camera.parent._getWorldMatrixDeterminant() < 0) {
-                offsetX *= -1;
-            }
+    /**
+     * Rotate camera by mouse offset.
+     */
+    private rotateCamera(offsetX: number, offsetY: number): void {
+        let camera = this.camera;
+        let scene = this.camera.getScene();
 
-            var x = offsetX / this.angularSensibility;
-            var y = offsetY / this.angularSensibility;
+        if (scene.useRightHandedSystem) {
+            offsetX *= -1;
+        }
 
-            // Initialize to current rotation.
-            var currentRotation = Quaternion.RotationYawPitchRoll(
-                camera.rotation.y,
-                camera.rotation.x,
-                camera.rotation.z
-            );
-            var rotationChange: Quaternion;
+        if (camera.parent && camera.parent._getWorldMatrixDeterminant() < 0) {
+            offsetX *= -1;
+        }
 
-            // Pitch.
-            if (this.buttonsPitch.some((v) => { return v === this.activeButton; })) {
-                // Apply change in Radians to vector Angle.
-                rotationChange = Quaternion.RotationAxis(Axis.X, y);
-                // Apply Pitch to quaternion.
-                currentRotation.multiplyInPlace(rotationChange);
-            }
+        var x = offsetX / this.angularSensibility;
+        var y = offsetY / this.angularSensibility;
+
+        // Initialize to current rotation.
+        var currentRotation = Quaternion.RotationYawPitchRoll(
+            camera.rotation.y,
+            camera.rotation.x,
+            camera.rotation.z
+        );
+        var rotationChange: Quaternion;
+
+        // Pitch.
+        if (this.buttonsPitch.some((v) => { return v === this.activeButton; })) {
+            // Apply change in Radians to vector Angle.
+            rotationChange = Quaternion.RotationAxis(Axis.X, y);
+            // Apply Pitch to quaternion.
+            currentRotation.multiplyInPlace(rotationChange);
+        }
 
-            // Yaw.
-            if (this.buttonsYaw.some((v) => { return v === this.activeButton; })) {
+        // Yaw.
+        if (this.buttonsYaw.some((v) => { return v === this.activeButton; })) {
+            // Apply change in Radians to vector Angle.
+            rotationChange = Quaternion.RotationAxis(Axis.Y, x);
+            // Apply Yaw to quaternion.
+            currentRotation.multiplyInPlace(rotationChange);
+
+            // Add Roll, if banked turning is enabled, within Roll limit.
+            let limit = (camera.bankedTurnLimit) + camera._trackRoll; // Defaults to 90° plus manual roll.
+            if (camera.bankedTurn && -limit < camera.rotation.z && camera.rotation.z < limit) {
+                let bankingDelta = camera.bankedTurnMultiplier * -x;
                 // Apply change in Radians to vector Angle.
-                rotationChange = Quaternion.RotationAxis(Axis.Y, x);
+                rotationChange = Quaternion.RotationAxis(Axis.Z, bankingDelta);
                 // Apply Yaw to quaternion.
                 currentRotation.multiplyInPlace(rotationChange);
-
-                // Add Roll, if banked turning is enabled, within Roll limit.
-                let limit = (camera.bankedTurnLimit) + camera._trackRoll; // Defaults to 90° plus manual roll.
-                if (camera.bankedTurn && -limit < camera.rotation.z && camera.rotation.z < limit) {
-                    let bankingDelta = camera.bankedTurnMultiplier * -x;
-                    // Apply change in Radians to vector Angle.
-                    rotationChange = Quaternion.RotationAxis(Axis.Z, bankingDelta);
-                    // Apply Yaw to quaternion.
-                    currentRotation.multiplyInPlace(rotationChange);
-                }
-            }
-
-            // Roll.
-            if (this.buttonsRoll.some((v) => { return v === this.activeButton; })) {
-                // Apply change in Radians to vector Angle.
-                rotationChange = Quaternion.RotationAxis(Axis.Z, -x);
-                // Track Rolling.
-                camera._trackRoll -= x;
-                // Apply Pitch to quaternion.
-                currentRotation.multiplyInPlace(rotationChange);
             }
+        }
 
-            // Apply rotationQuaternion to Euler camera.rotation.
-            currentRotation.toEulerAnglesToRef(camera.rotation);
+        // Roll.
+        if (this.buttonsRoll.some((v) => { return v === this.activeButton; })) {
+            // Apply change in Radians to vector Angle.
+            rotationChange = Quaternion.RotationAxis(Axis.Z, -x);
+            // Track Rolling.
+            camera._trackRoll -= x;
+            // Apply Pitch to quaternion.
+            currentRotation.multiplyInPlace(rotationChange);
         }
+
+        // Apply rotationQuaternion to Euler camera.rotation.
+        currentRotation.toEulerAnglesToRef(camera.rotation);
     }
+}
 
-    (<any>CameraInputTypes)["FlyCameraMouseInput"] = FlyCameraMouseInput;
+(<any>CameraInputTypes)["FlyCameraMouseInput"] = FlyCameraMouseInput;

+ 173 - 173
src/Cameras/Inputs/followCameraKeyboardMoveInput.ts

@@ -7,210 +7,210 @@ import { Engine } from "../../Engines/engine";
 import { KeyboardInfo, KeyboardEventTypes } from "../../Events/keyboardEvents";
 import { Scene } from "../../scene";
 
+/**
+ * Manage the keyboard inputs to control the movement of an arc rotate camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FollowCameraKeyboardMoveInput implements ICameraInput<FollowCamera> {
     /**
-     * Manage the keyboard inputs to control the movement of an arc rotate camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class FollowCameraKeyboardMoveInput implements ICameraInput<FollowCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FollowCamera;
-
-        /**
-         * Defines the list of key codes associated with the up action (increase heightOffset)
-         */
-        @serialize()
-        public keysUp = [38];
-
-        /**
-         * Defines the list of key codes associated with the down action (decrease heightOffset)
-         */
-        @serialize()
-        public keysDown = [40];
-
-        /**
-         * Defines the list of key codes associated with the left action (increase rotation)
-         */
-        @serialize()
-        public keysLeft = [37];
-
-        /**
-         * Defines the list of key codes associated with the right action (decrease rotation)
-         */
-        @serialize()
-        public keysRight = [39];
-
-        /**
-         * Defines the rate of change of heightOffset.
-         */
-        @serialize()
-        public heightSensibility: number = 1;
-
-        /**
-         * Defines the rate of change of rotationOffset.
-         */
-        @serialize()
-        public rotationSensibility: number = 1;
-
-        /**
-         * Defines the rate of change of radius.
-         */
-        @serialize()
-        public radiusSensibility: number = 1;
-
-        /**
-         * Defines the minimum heightOffset value.
-         */
-        @serialize()
-        public minHeightOffset: number = 0;
-
-        /**
-         * Defines the minimum radius value.
-         */
-        @serialize()
-        public minRadius: number = 0;
-
-        private _keys = new Array<number>();
-        // private _ctrlPressed: boolean;
-        private _altPressed: boolean;
-        private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
-        private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
-        private _engine: Engine;
-        private _scene: Scene;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            if (this._onCanvasBlurObserver) {
-                return;
-            }
+    public camera: FollowCamera;
 
-            this._scene = this.camera.getScene();
-            this._engine = this._scene.getEngine();
+    /**
+     * Defines the list of key codes associated with the up action (increase heightOffset)
+     */
+    @serialize()
+    public keysUp = [38];
 
-            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
-                this._keys = [];
-            });
+    /**
+     * Defines the list of key codes associated with the down action (decrease heightOffset)
+     */
+    @serialize()
+    public keysDown = [40];
 
-            this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
-                let evt = info.event;
-                if (!evt.metaKey) {
-                    if (info.type === KeyboardEventTypes.KEYDOWN) {
-                        // this._ctrlPressed = evt.ctrlKey;
-                        this._altPressed = evt.altKey;
+    /**
+     * Defines the list of key codes associated with the left action (increase rotation)
+     */
+    @serialize()
+    public keysLeft = [37];
 
-                        if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                            this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                            this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                            this.keysRight.indexOf(evt.keyCode) !== -1) {
-                            var index = this._keys.indexOf(evt.keyCode);
+    /**
+     * Defines the list of key codes associated with the right action (decrease rotation)
+     */
+    @serialize()
+    public keysRight = [39];
 
-                            if (index === -1) {
-                                this._keys.push(evt.keyCode);
-                            }
+    /**
+     * Defines the rate of change of heightOffset.
+     */
+    @serialize()
+    public heightSensibility: number = 1;
 
-                            if (evt.preventDefault) {
-                                if (!noPreventDefault) {
-                                    evt.preventDefault();
-                                }
-                            }
+    /**
+     * Defines the rate of change of rotationOffset.
+     */
+    @serialize()
+    public rotationSensibility: number = 1;
+
+    /**
+     * Defines the rate of change of radius.
+     */
+    @serialize()
+    public radiusSensibility: number = 1;
+
+    /**
+     * Defines the minimum heightOffset value.
+     */
+    @serialize()
+    public minHeightOffset: number = 0;
+
+    /**
+     * Defines the minimum radius value.
+     */
+    @serialize()
+    public minRadius: number = 0;
+
+    private _keys = new Array<number>();
+    // private _ctrlPressed: boolean;
+    private _altPressed: boolean;
+    private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
+    private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
+    private _engine: Engine;
+    private _scene: Scene;
+
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        if (this._onCanvasBlurObserver) {
+            return;
+        }
+
+        this._scene = this.camera.getScene();
+        this._engine = this._scene.getEngine();
+
+        this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
+            this._keys = [];
+        });
+
+        this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
+            let evt = info.event;
+            if (!evt.metaKey) {
+                if (info.type === KeyboardEventTypes.KEYDOWN) {
+                    // this._ctrlPressed = evt.ctrlKey;
+                    this._altPressed = evt.altKey;
+
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index === -1) {
+                            this._keys.push(evt.keyCode);
                         }
-                    }
-                    else {
-                        if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                            this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                            this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                            this.keysRight.indexOf(evt.keyCode) !== -1) {
-                            var index = this._keys.indexOf(evt.keyCode);
-
-                            if (index >= 0) {
-                                this._keys.splice(index, 1);
-                            }
 
-                            if (evt.preventDefault) {
-                                if (!noPreventDefault) {
-                                    evt.preventDefault();
-                                }
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
                             }
                         }
                     }
                 }
-            });
-        }
+                else {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index >= 0) {
+                            this._keys.splice(index, 1);
+                        }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>) {
-            if (this._scene) {
-                if (this._onKeyboardObserver) {
-                    this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
-                }
-                if (this._onCanvasBlurObserver) {
-                    this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+                        if (evt.preventDefault) {
+                            if (!noPreventDefault) {
+                                evt.preventDefault();
+                            }
+                        }
+                    }
                 }
-                this._onKeyboardObserver = null;
-                this._onCanvasBlurObserver = null;
             }
+        });
+    }
 
-            this._keys = [];
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>) {
+        if (this._scene) {
+            if (this._onKeyboardObserver) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+            }
+            if (this._onCanvasBlurObserver) {
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
+            }
+            this._onKeyboardObserver = null;
+            this._onCanvasBlurObserver = null;
         }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this._onKeyboardObserver) {
-                for (var index = 0; index < this._keys.length; index++) {
-                    var keyCode = this._keys[index];
-                    if (this.keysLeft.indexOf(keyCode) !== -1) {
-                      this.camera.rotationOffset += this.rotationSensibility;
-                      this.camera.rotationOffset %= 180;
-                    } else if (this.keysUp.indexOf(keyCode) !== -1) {
-                      if (this._altPressed) {
+        this._keys = [];
+    }
+
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this._onKeyboardObserver) {
+            for (var index = 0; index < this._keys.length; index++) {
+                var keyCode = this._keys[index];
+                if (this.keysLeft.indexOf(keyCode) !== -1) {
+                    this.camera.rotationOffset += this.rotationSensibility;
+                    this.camera.rotationOffset %= 180;
+                } else if (this.keysUp.indexOf(keyCode) !== -1) {
+                    if (this._altPressed) {
                         this.camera.radius += this.radiusSensibility;
-                      } else {
+                    } else {
                         this.camera.heightOffset += this.heightSensibility;
-                      }
-                    } else if (this.keysRight.indexOf(keyCode) !== -1) {
-                      this.camera.rotationOffset -= this.rotationSensibility;
-                      this.camera.rotationOffset %= 180;
-                    } else if (this.keysDown.indexOf(keyCode) !== -1) {
-                      if (this._altPressed) {
+                    }
+                } else if (this.keysRight.indexOf(keyCode) !== -1) {
+                    this.camera.rotationOffset -= this.rotationSensibility;
+                    this.camera.rotationOffset %= 180;
+                } else if (this.keysDown.indexOf(keyCode) !== -1) {
+                    if (this._altPressed) {
                         this.camera.radius -= this.radiusSensibility;
                         this.camera.radius =
-                          Math.max(this.minRadius, this.camera.radius);
-                      } else {
+                            Math.max(this.minRadius, this.camera.radius);
+                    } else {
                         this.camera.heightOffset -= this.heightSensibility;
                         this.camera.heightOffset =
-                          Math.max(this.minHeightOffset, this.camera.heightOffset);
-                      }
+                            Math.max(this.minHeightOffset, this.camera.heightOffset);
                     }
                 }
             }
         }
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FollowCameraKeyboardMoveInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FollowCameraKeyboardMoveInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "keyboard";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "keyboard";
     }
+}
 
-    (<any>CameraInputTypes)["FollowCameraKeyboardMoveInput"] = FollowCameraKeyboardMoveInput;
+(<any>CameraInputTypes)["FollowCameraKeyboardMoveInput"] = FollowCameraKeyboardMoveInput;

+ 92 - 92
src/Cameras/Inputs/freeCameraDeviceOrientationInput.ts

@@ -3,111 +3,111 @@ import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManage
 import { FreeCamera } from "../../Cameras/freeCamera";
 import { Quaternion } from "../../Maths/math";
 import { Tools } from "../../Misc/tools";
-    /**
-     * Takes information about the orientation of the device as reported by the deviceorientation event to orient the camera.
-     * Screen rotation is taken into account.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
-     */
-    export class FreeCameraDeviceOrientationInput implements ICameraInput<FreeCamera> {
-        private _camera: FreeCamera;
+/**
+ * Takes information about the orientation of the device as reported by the deviceorientation event to orient the camera.
+ * Screen rotation is taken into account.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FreeCameraDeviceOrientationInput implements ICameraInput<FreeCamera> {
+    private _camera: FreeCamera;
 
-        private _screenOrientationAngle: number = 0;
+    private _screenOrientationAngle: number = 0;
 
-        private _constantTranform: Quaternion;
-        private _screenQuaternion: Quaternion = new Quaternion();
+    private _constantTranform: Quaternion;
+    private _screenQuaternion: Quaternion = new Quaternion();
 
-        private _alpha: number = 0;
-        private _beta: number = 0;
-        private _gamma: number = 0;
+    private _alpha: number = 0;
+    private _beta: number = 0;
+    private _gamma: number = 0;
 
-        /**
-         * Instantiates a new input
-         * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
-         */
-        constructor() {
-            this._constantTranform = new Quaternion(- Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
-            this._orientationChanged();
-        }
+    /**
+     * Instantiates a new input
+     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     */
+    constructor() {
+        this._constantTranform = new Quaternion(- Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
+        this._orientationChanged();
+    }
 
-        /**
-         * Define the camera controlled by the input.
-         */
-        public get camera(): FreeCamera {
-            return this._camera;
-        }
+    /**
+     * Define the camera controlled by the input.
+     */
+    public get camera(): FreeCamera {
+        return this._camera;
+    }
 
-        public set camera(camera: FreeCamera) {
-            this._camera = camera;
-            if (this._camera != null && !this._camera.rotationQuaternion) {
-                this._camera.rotationQuaternion = new Quaternion();
-            }
+    public set camera(camera: FreeCamera) {
+        this._camera = camera;
+        if (this._camera != null && !this._camera.rotationQuaternion) {
+            this._camera.rotationQuaternion = new Quaternion();
         }
+    }
 
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            window.addEventListener("orientationchange", this._orientationChanged);
-            window.addEventListener("deviceorientation", this._deviceOrientation);
-            //In certain cases, the attach control is called AFTER orientation was changed,
-            //So this is needed.
-            this._orientationChanged();
-        }
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        window.addEventListener("orientationchange", this._orientationChanged);
+        window.addEventListener("deviceorientation", this._deviceOrientation);
+        //In certain cases, the attach control is called AFTER orientation was changed,
+        //So this is needed.
+        this._orientationChanged();
+    }
 
-        private _orientationChanged = () => {
-            this._screenOrientationAngle = (<any>window.orientation !== undefined ? +<any>window.orientation : ((<any>window.screen).orientation && ((<any>window.screen).orientation)['angle'] ? ((<any>window.screen).orientation).angle : 0));
-            this._screenOrientationAngle = -Tools.ToRadians(this._screenOrientationAngle / 2);
-            this._screenQuaternion.copyFromFloats(0, Math.sin(this._screenOrientationAngle), 0, Math.cos(this._screenOrientationAngle));
-        }
+    private _orientationChanged = () => {
+        this._screenOrientationAngle = (<any>window.orientation !== undefined ? +<any>window.orientation : ((<any>window.screen).orientation && ((<any>window.screen).orientation)['angle'] ? ((<any>window.screen).orientation).angle : 0));
+        this._screenOrientationAngle = -Tools.ToRadians(this._screenOrientationAngle / 2);
+        this._screenQuaternion.copyFromFloats(0, Math.sin(this._screenOrientationAngle), 0, Math.cos(this._screenOrientationAngle));
+    }
 
-        private _deviceOrientation = (evt: DeviceOrientationEvent) => {
-            this._alpha = evt.alpha !== null ? evt.alpha : 0;
-            this._beta = evt.beta !== null ? evt.beta : 0;
-            this._gamma = evt.gamma !== null ? evt.gamma : 0;
-        }
+    private _deviceOrientation = (evt: DeviceOrientationEvent) => {
+        this._alpha = evt.alpha !== null ? evt.alpha : 0;
+        this._beta = evt.beta !== null ? evt.beta : 0;
+        this._gamma = evt.gamma !== null ? evt.gamma : 0;
+    }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            window.removeEventListener("orientationchange", this._orientationChanged);
-            window.removeEventListener("deviceorientation", this._deviceOrientation);
-        }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        window.removeEventListener("orientationchange", this._orientationChanged);
+        window.removeEventListener("deviceorientation", this._deviceOrientation);
+    }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            //if no device orientation provided, don't update the rotation.
-            //Only testing against alpha under the assumption thatnorientation will never be so exact when set.
-            if (!this._alpha) { return; }
-            Quaternion.RotationYawPitchRollToRef(Tools.ToRadians(this._alpha), Tools.ToRadians(this._beta), -Tools.ToRadians(this._gamma), this.camera.rotationQuaternion);
-            this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion);
-            this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform);
-            //Mirror on XY Plane
-            this._camera.rotationQuaternion.z *= -1;
-            this._camera.rotationQuaternion.w *= -1;
-        }
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        //if no device orientation provided, don't update the rotation.
+        //Only testing against alpha under the assumption thatnorientation will never be so exact when set.
+        if (!this._alpha) { return; }
+        Quaternion.RotationYawPitchRollToRef(Tools.ToRadians(this._alpha), Tools.ToRadians(this._beta), -Tools.ToRadians(this._gamma), this.camera.rotationQuaternion);
+        this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion);
+        this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform);
+        //Mirror on XY Plane
+        this._camera.rotationQuaternion.z *= -1;
+        this._camera.rotationQuaternion.w *= -1;
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FreeCameraDeviceOrientationInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FreeCameraDeviceOrientationInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "deviceOrientation";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "deviceOrientation";
     }
+}
 
-    (<any>CameraInputTypes)["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;
+(<any>CameraInputTypes)["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;

+ 114 - 114
src/Cameras/Inputs/freeCameraGamepadInput.ts

@@ -6,133 +6,133 @@ import { FreeCamera } from "../../Cameras/freeCamera";
 import { Matrix, Vector3, Vector2 } from "../../Maths/math";
 import { Gamepad } from "../../Gamepads/gamepad";
 
+/**
+ * Manage the gamepad inputs to control a free camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FreeCameraGamepadInput implements ICameraInput<FreeCamera> {
     /**
-     * Manage the gamepad inputs to control a free camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Define the camera the input is attached to.
      */
-    export class FreeCameraGamepadInput implements ICameraInput<FreeCamera> {
-        /**
-         * Define the camera the input is attached to.
-         */
-        public camera: FreeCamera;
-
-        /**
-         * Define the Gamepad controlling the input
-         */
-        public gamepad: Nullable<Gamepad>;
-
-        /**
-         * Defines the gamepad rotation sensiblity.
-         * This is the threshold from when rotation starts to be accounted for to prevent jittering.
-         */
-        @serialize()
-        public gamepadAngularSensibility = 200;
-
-        /**
-         * Defines the gamepad move sensiblity.
-         * This is the threshold from when moving starts to be accounted for for to prevent jittering.
-         */
-        @serialize()
-        public gamepadMoveSensibility = 40;
-
-        // private members
-        private _onGamepadConnectedObserver : Nullable<Observer<Gamepad>>;
-        private _onGamepadDisconnectedObserver : Nullable<Observer<Gamepad>>;
-        private _cameraTransform: Matrix = Matrix.Identity();
-        private _deltaTransform: Vector3 = Vector3.Zero();
-        private _vector3: Vector3 = Vector3.Zero();
-        private _vector2: Vector2 = Vector2.Zero();
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            let manager = this.camera.getScene().gamepadManager;
-            this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
-                if (gamepad.type !== Gamepad.POSE_ENABLED) {
-                    // prioritize XBOX gamepads.
-                    if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
-                        this.gamepad = gamepad;
-                    }
-                }
-            });
+    public camera: FreeCamera;
 
-            this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad) => {
-                if (this.gamepad === gamepad) {
-                    this.gamepad = null;
-                }
-            });
+    /**
+     * Define the Gamepad controlling the input
+     */
+    public gamepad: Nullable<Gamepad>;
 
-            this.gamepad = manager.getGamepadByType(Gamepad.XBOX);
-        }
+    /**
+     * Defines the gamepad rotation sensiblity.
+     * This is the threshold from when rotation starts to be accounted for to prevent jittering.
+     */
+    @serialize()
+    public gamepadAngularSensibility = 200;
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
-            this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
-            this.gamepad = null;
-        }
+    /**
+     * Defines the gamepad move sensiblity.
+     * This is the threshold from when moving starts to be accounted for for to prevent jittering.
+     */
+    @serialize()
+    public gamepadMoveSensibility = 40;
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this.gamepad && this.gamepad.leftStick) {
-                var camera = this.camera;
-                var LSValues = this.gamepad.leftStick;
-                var normalizedLX = LSValues.x / this.gamepadMoveSensibility;
-                var normalizedLY = LSValues.y / this.gamepadMoveSensibility;
-                LSValues.x = Math.abs(normalizedLX) > 0.005 ? 0 + normalizedLX : 0;
-                LSValues.y = Math.abs(normalizedLY) > 0.005 ? 0 + normalizedLY : 0;
-
-                var RSValues = this.gamepad.rightStick;
-                if (RSValues) {
-                    var normalizedRX = RSValues.x / this.gamepadAngularSensibility;
-                    var normalizedRY = RSValues.y / this.gamepadAngularSensibility;
-                    RSValues.x = Math.abs(normalizedRX) > 0.001 ? 0 + normalizedRX : 0;
-                    RSValues.y = Math.abs(normalizedRY) > 0.001 ? 0 + normalizedRY : 0;
-                }
-                else {
-                    RSValues = {x: 0, y: 0};
-                }
+    // private members
+    private _onGamepadConnectedObserver: Nullable<Observer<Gamepad>>;
+    private _onGamepadDisconnectedObserver: Nullable<Observer<Gamepad>>;
+    private _cameraTransform: Matrix = Matrix.Identity();
+    private _deltaTransform: Vector3 = Vector3.Zero();
+    private _vector3: Vector3 = Vector3.Zero();
+    private _vector2: Vector2 = Vector2.Zero();
 
-                if (!camera.rotationQuaternion) {
-                    Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, this._cameraTransform);
-                } else {
-                    camera.rotationQuaternion.toRotationMatrix(this._cameraTransform);
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        let manager = this.camera.getScene().gamepadManager;
+        this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
+            if (gamepad.type !== Gamepad.POSE_ENABLED) {
+                // prioritize XBOX gamepads.
+                if (!this.gamepad || gamepad.type === Gamepad.XBOX) {
+                    this.gamepad = gamepad;
                 }
+            }
+        });
 
-                var speed = camera._computeLocalCameraSpeed() * 50.0;
-                this._vector3.copyFromFloats(LSValues.x * speed, 0, -LSValues.y * speed);
+        this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad) => {
+            if (this.gamepad === gamepad) {
+                this.gamepad = null;
+            }
+        });
+
+        this.gamepad = manager.getGamepadByType(Gamepad.XBOX);
+    }
 
-                Vector3.TransformCoordinatesToRef(this._vector3, this._cameraTransform, this._deltaTransform);
-                camera.cameraDirection.addInPlace(this._deltaTransform);
-                this._vector2.copyFromFloats(RSValues.y, RSValues.x);
-                camera.cameraRotation.addInPlace(this._vector2);
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        this.camera.getScene().gamepadManager.onGamepadConnectedObservable.remove(this._onGamepadConnectedObserver);
+        this.camera.getScene().gamepadManager.onGamepadDisconnectedObservable.remove(this._onGamepadDisconnectedObserver);
+        this.gamepad = null;
+    }
+
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this.gamepad && this.gamepad.leftStick) {
+            var camera = this.camera;
+            var LSValues = this.gamepad.leftStick;
+            var normalizedLX = LSValues.x / this.gamepadMoveSensibility;
+            var normalizedLY = LSValues.y / this.gamepadMoveSensibility;
+            LSValues.x = Math.abs(normalizedLX) > 0.005 ? 0 + normalizedLX : 0;
+            LSValues.y = Math.abs(normalizedLY) > 0.005 ? 0 + normalizedLY : 0;
+
+            var RSValues = this.gamepad.rightStick;
+            if (RSValues) {
+                var normalizedRX = RSValues.x / this.gamepadAngularSensibility;
+                var normalizedRY = RSValues.y / this.gamepadAngularSensibility;
+                RSValues.x = Math.abs(normalizedRX) > 0.001 ? 0 + normalizedRX : 0;
+                RSValues.y = Math.abs(normalizedRY) > 0.001 ? 0 + normalizedRY : 0;
+            }
+            else {
+                RSValues = { x: 0, y: 0 };
             }
-        }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FreeCameraGamepadInput";
-        }
+            if (!camera.rotationQuaternion) {
+                Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, this._cameraTransform);
+            } else {
+                camera.rotationQuaternion.toRotationMatrix(this._cameraTransform);
+            }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "gamepad";
+            var speed = camera._computeLocalCameraSpeed() * 50.0;
+            this._vector3.copyFromFloats(LSValues.x * speed, 0, -LSValues.y * speed);
+
+            Vector3.TransformCoordinatesToRef(this._vector3, this._cameraTransform, this._deltaTransform);
+            camera.cameraDirection.addInPlace(this._deltaTransform);
+            this._vector2.copyFromFloats(RSValues.y, RSValues.x);
+            camera.cameraRotation.addInPlace(this._vector2);
         }
     }
 
-    (<any>CameraInputTypes)["FreeCameraGamepadInput"] = FreeCameraGamepadInput;
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FreeCameraGamepadInput";
+    }
+
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "gamepad";
+    }
+}
+
+(<any>CameraInputTypes)["FreeCameraGamepadInput"] = FreeCameraGamepadInput;

+ 147 - 147
src/Cameras/Inputs/freeCameraKeyboardMoveInput.ts

@@ -7,171 +7,171 @@ import { KeyboardInfo, KeyboardEventTypes } from "../../Events/keyboardEvents";
 import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Engine } from "../../Engines/engine";
+/**
+ * Manage the keyboard inputs to control the movement of a free camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FreeCameraKeyboardMoveInput implements ICameraInput<FreeCamera> {
     /**
-     * Manage the keyboard inputs to control the movement of a free camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class FreeCameraKeyboardMoveInput implements ICameraInput<FreeCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FreeCamera;
-
-        /**
-         * Gets or Set the list of keyboard keys used to control the forward move of the camera.
-         */
-        @serialize()
-        public keysUp = [38];
-
-        /**
-         * Gets or Set the list of keyboard keys used to control the backward move of the camera.
-         */
-        @serialize()
-        public keysDown = [40];
-
-        /**
-         * Gets or Set the list of keyboard keys used to control the left strafe move of the camera.
-         */
-        @serialize()
-        public keysLeft = [37];
-
-        /**
-         * Gets or Set the list of keyboard keys used to control the right strafe move of the camera.
-         */
-        @serialize()
-        public keysRight = [39];
-
-        private _keys = new Array<number>();
-        private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
-        private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
-        private _engine: Engine;
-        private _scene: Scene;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            if (this._onCanvasBlurObserver) {
-                return;
-            }
+    public camera: FreeCamera;
+
+    /**
+     * Gets or Set the list of keyboard keys used to control the forward move of the camera.
+     */
+    @serialize()
+    public keysUp = [38];
+
+    /**
+     * Gets or Set the list of keyboard keys used to control the backward move of the camera.
+     */
+    @serialize()
+    public keysDown = [40];
+
+    /**
+     * Gets or Set the list of keyboard keys used to control the left strafe move of the camera.
+     */
+    @serialize()
+    public keysLeft = [37];
+
+    /**
+     * Gets or Set the list of keyboard keys used to control the right strafe move of the camera.
+     */
+    @serialize()
+    public keysRight = [39];
 
-            this._scene = this.camera.getScene();
-            this._engine = this._scene.getEngine();
-
-            this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
-                this._keys = [];
-            });
-
-            this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
-                let evt = info.event;
-                if (!evt.metaKey) {
-                    if (info.type === KeyboardEventTypes.KEYDOWN) {
-                        if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                            this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                            this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                            this.keysRight.indexOf(evt.keyCode) !== -1) {
-                            var index = this._keys.indexOf(evt.keyCode);
-
-                            if (index === -1) {
-                                this._keys.push(evt.keyCode);
-                            }
-                            if (!noPreventDefault) {
-                                evt.preventDefault();
-                            }
+    private _keys = new Array<number>();
+    private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
+    private _onKeyboardObserver: Nullable<Observer<KeyboardInfo>>;
+    private _engine: Engine;
+    private _scene: Scene;
+
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        if (this._onCanvasBlurObserver) {
+            return;
+        }
+
+        this._scene = this.camera.getScene();
+        this._engine = this._scene.getEngine();
+
+        this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
+            this._keys = [];
+        });
+
+        this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
+            let evt = info.event;
+            if (!evt.metaKey) {
+                if (info.type === KeyboardEventTypes.KEYDOWN) {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index === -1) {
+                            this._keys.push(evt.keyCode);
                         }
-                    } else {
-                        if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
-                            this.keysDown.indexOf(evt.keyCode) !== -1 ||
-                            this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                            this.keysRight.indexOf(evt.keyCode) !== -1) {
-                            var index = this._keys.indexOf(evt.keyCode);
-
-                            if (index >= 0) {
-                                this._keys.splice(index, 1);
-                            }
-                            if (!noPreventDefault) {
-                                evt.preventDefault();
-                            }
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
+                        }
+                    }
+                } else {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index >= 0) {
+                            this._keys.splice(index, 1);
+                        }
+                        if (!noPreventDefault) {
+                            evt.preventDefault();
                         }
                     }
                 }
-            });
-        }
+            }
+        });
+    }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._scene) {
-                if (this._onKeyboardObserver) {
-                    this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
-                }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._scene) {
+            if (this._onKeyboardObserver) {
+                this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
+            }
 
-                if (this._onCanvasBlurObserver) {
-                    this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
-                }
-                this._onKeyboardObserver = null;
-                this._onCanvasBlurObserver = null;
+            if (this._onCanvasBlurObserver) {
+                this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
             }
-            this._keys = [];
+            this._onKeyboardObserver = null;
+            this._onCanvasBlurObserver = null;
         }
+        this._keys = [];
+    }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this._onKeyboardObserver) {
-                var camera = this.camera;
-                // Keyboard
-                for (var index = 0; index < this._keys.length; index++) {
-                    var keyCode = this._keys[index];
-                    var speed = camera._computeLocalCameraSpeed();
-
-                    if (this.keysLeft.indexOf(keyCode) !== -1) {
-                        camera._localDirection.copyFromFloats(-speed, 0, 0);
-                    } else if (this.keysUp.indexOf(keyCode) !== -1) {
-                        camera._localDirection.copyFromFloats(0, 0, speed);
-                    } else if (this.keysRight.indexOf(keyCode) !== -1) {
-                        camera._localDirection.copyFromFloats(speed, 0, 0);
-                    } else if (this.keysDown.indexOf(keyCode) !== -1) {
-                        camera._localDirection.copyFromFloats(0, 0, -speed);
-                    }
-
-                    if (camera.getScene().useRightHandedSystem) {
-                        camera._localDirection.z *= -1;
-                    }
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this._onKeyboardObserver) {
+            var camera = this.camera;
+            // Keyboard
+            for (var index = 0; index < this._keys.length; index++) {
+                var keyCode = this._keys[index];
+                var speed = camera._computeLocalCameraSpeed();
+
+                if (this.keysLeft.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(-speed, 0, 0);
+                } else if (this.keysUp.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, speed);
+                } else if (this.keysRight.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(speed, 0, 0);
+                } else if (this.keysDown.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, -speed);
+                }
 
-                    camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
-                    Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
-                    camera.cameraDirection.addInPlace(camera._transformedDirection);
+                if (camera.getScene().useRightHandedSystem) {
+                    camera._localDirection.z *= -1;
                 }
+
+                camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
+                Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
+                camera.cameraDirection.addInPlace(camera._transformedDirection);
             }
         }
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FreeCameraKeyboardMoveInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FreeCameraKeyboardMoveInput";
+    }
 
-        /** @hidden */
-        public _onLostFocus(): void {
-            this._keys = [];
-        }
+    /** @hidden */
+    public _onLostFocus(): void {
+        this._keys = [];
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "keyboard";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "keyboard";
     }
+}
 
-    (<any>CameraInputTypes)["FreeCameraKeyboardMoveInput"] = FreeCameraKeyboardMoveInput;
+(<any>CameraInputTypes)["FreeCameraKeyboardMoveInput"] = FreeCameraKeyboardMoveInput;

+ 143 - 143
src/Cameras/Inputs/freeCameraMouseInput.ts

@@ -4,188 +4,188 @@ import { Nullable } from "../../types";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
 import { FreeCamera } from "../../Cameras/freeCamera";
 import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
+/**
+ * Manage the mouse inputs to control the movement of a free camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
     /**
-     * Manage the mouse inputs to control the movement of a free camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FreeCamera;
+    public camera: FreeCamera;
 
-        /**
-         * Defines the buttons associated with the input to handle camera move.
-         */
-        @serialize()
-        public buttons = [0, 1, 2];
+    /**
+     * Defines the buttons associated with the input to handle camera move.
+     */
+    @serialize()
+    public buttons = [0, 1, 2];
 
-        /**
-         * Defines the pointer angular sensibility  along the X and Y axis or how fast is the camera rotating.
-         */
-        @serialize()
-        public angularSensibility = 2000.0;
+    /**
+     * Defines the pointer angular sensibility  along the X and Y axis or how fast is the camera rotating.
+     */
+    @serialize()
+    public angularSensibility = 2000.0;
 
-        private _pointerInput: (p: PointerInfo, s: EventState) => void;
-        private _onMouseMove: Nullable<(e: MouseEvent) => any>;
-        private _observer: Nullable<Observer<PointerInfo>>;
-        private previousPosition: Nullable<{ x: number, y: number }> = null;
+    private _pointerInput: (p: PointerInfo, s: EventState) => void;
+    private _onMouseMove: Nullable<(e: MouseEvent) => any>;
+    private _observer: Nullable<Observer<PointerInfo>>;
+    private previousPosition: Nullable<{ x: number, y: number }> = null;
 
+    /**
+     * Manage the mouse inputs to control the movement of a free camera.
+     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * @param touchEnabled Defines if touch is enabled or not
+     */
+    constructor(
         /**
-         * Manage the mouse inputs to control the movement of a free camera.
-         * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
-         * @param touchEnabled Defines if touch is enabled or not
+         * Define if touch is enabled in the mouse input
          */
-        constructor(
-            /**
-             * Define if touch is enabled in the mouse input
-             */
-            public touchEnabled = true) {
-        }
+        public touchEnabled = true) {
+    }
 
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            var engine = this.camera.getEngine();
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        var engine = this.camera.getEngine();
 
-            if (!this._pointerInput) {
-                this._pointerInput = (p) => {
-                    var evt = <PointerEvent>p.event;
+        if (!this._pointerInput) {
+            this._pointerInput = (p) => {
+                var evt = <PointerEvent>p.event;
 
-                    if (engine.isInVRExclusivePointerMode) {
-                        return;
-                    }
+                if (engine.isInVRExclusivePointerMode) {
+                    return;
+                }
 
-                    if (!this.touchEnabled && evt.pointerType === "touch") {
-                        return;
-                    }
+                if (!this.touchEnabled && evt.pointerType === "touch") {
+                    return;
+                }
 
-                    if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
-                        return;
-                    }
+                if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
+                    return;
+                }
 
-                    let srcElement = <HTMLElement>(evt.srcElement || evt.target);
+                let srcElement = <HTMLElement>(evt.srcElement || evt.target);
 
-                    if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
-                        try {
-                            srcElement.setPointerCapture(evt.pointerId);
-                        } catch (e) {
-                            //Nothing to do with the error. Execution will continue.
-                        }
+                if (p.type === PointerEventTypes.POINTERDOWN && srcElement) {
+                    try {
+                        srcElement.setPointerCapture(evt.pointerId);
+                    } catch (e) {
+                        //Nothing to do with the error. Execution will continue.
+                    }
 
-                        this.previousPosition = {
-                            x: evt.clientX,
-                            y: evt.clientY
-                        };
+                    this.previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
 
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                            element.focus();
-                        }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                        element.focus();
                     }
-                    else if (p.type === PointerEventTypes.POINTERUP && srcElement) {
-                        try {
-                            srcElement.releasePointerCapture(evt.pointerId);
-                        } catch (e) {
-                            //Nothing to do with the error.
-                        }
-
-                        this.previousPosition = null;
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
+                }
+                else if (p.type === PointerEventTypes.POINTERUP && srcElement) {
+                    try {
+                        srcElement.releasePointerCapture(evt.pointerId);
+                    } catch (e) {
+                        //Nothing to do with the error.
                     }
 
-                    else if (p.type === PointerEventTypes.POINTERMOVE) {
-                        if (!this.previousPosition || engine.isPointerLock) {
-                            return;
-                        }
+                    this.previousPosition = null;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                }
 
-                        var offsetX = evt.clientX - this.previousPosition.x;
-                        if (this.camera.getScene().useRightHandedSystem) { offsetX *= -1; }
-                        if (this.camera.parent && this.camera.parent._getWorldMatrixDeterminant() < 0) { offsetX *= -1; }
-                        this.camera.cameraRotation.y += offsetX / this.angularSensibility;
+                else if (p.type === PointerEventTypes.POINTERMOVE) {
+                    if (!this.previousPosition || engine.isPointerLock) {
+                        return;
+                    }
 
-                        var offsetY = evt.clientY - this.previousPosition.y;
-                        this.camera.cameraRotation.x += offsetY / this.angularSensibility;
+                    var offsetX = evt.clientX - this.previousPosition.x;
+                    if (this.camera.getScene().useRightHandedSystem) { offsetX *= -1; }
+                    if (this.camera.parent && this.camera.parent._getWorldMatrixDeterminant() < 0) { offsetX *= -1; }
+                    this.camera.cameraRotation.y += offsetX / this.angularSensibility;
 
-                        this.previousPosition = {
-                            x: evt.clientX,
-                            y: evt.clientY
-                        };
+                    var offsetY = evt.clientY - this.previousPosition.y;
+                    this.camera.cameraRotation.x += offsetY / this.angularSensibility;
 
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
-                    }
-                };
-            }
+                    this.previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
 
-            this._onMouseMove = (evt) => {
-                if (!engine.isPointerLock) {
-                    return;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
                 }
+            };
+        }
 
-                if (engine.isInVRExclusivePointerMode) {
-                    return;
-                }
+        this._onMouseMove = (evt) => {
+            if (!engine.isPointerLock) {
+                return;
+            }
 
-                var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
-                if (this.camera.getScene().useRightHandedSystem) { offsetX *= -1; }
-                if (this.camera.parent && this.camera.parent._getWorldMatrixDeterminant() < 0) { offsetX *= -1; }
-                this.camera.cameraRotation.y += offsetX / this.angularSensibility;
+            if (engine.isInVRExclusivePointerMode) {
+                return;
+            }
 
-                var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
-                this.camera.cameraRotation.x += offsetY / this.angularSensibility;
+            var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
+            if (this.camera.getScene().useRightHandedSystem) { offsetX *= -1; }
+            if (this.camera.parent && this.camera.parent._getWorldMatrixDeterminant() < 0) { offsetX *= -1; }
+            this.camera.cameraRotation.y += offsetX / this.angularSensibility;
 
-                this.previousPosition = null;
+            var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
+            this.camera.cameraRotation.x += offsetY / this.angularSensibility;
 
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-            };
+            this.previousPosition = null;
 
-            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
-            element.addEventListener("mousemove", this._onMouseMove, false);
+            if (!noPreventDefault) {
+                evt.preventDefault();
+            }
+        };
 
-        }
+        this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
+        element.addEventListener("mousemove", this._onMouseMove, false);
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._observer && element) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
+    }
 
-                if (this._onMouseMove) {
-                    element.removeEventListener("mousemove", this._onMouseMove);
-                }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._observer && element) {
+            this.camera.getScene().onPointerObservable.remove(this._observer);
 
-                this._observer = null;
-                this._onMouseMove = null;
-                this.previousPosition = null;
+            if (this._onMouseMove) {
+                element.removeEventListener("mousemove", this._onMouseMove);
             }
-        }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FreeCameraMouseInput";
+            this._observer = null;
+            this._onMouseMove = null;
+            this.previousPosition = null;
         }
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "mouse";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FreeCameraMouseInput";
+    }
+
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "mouse";
     }
+}
 
-    (<any>CameraInputTypes)["FreeCameraMouseInput"] = FreeCameraMouseInput;
+(<any>CameraInputTypes)["FreeCameraMouseInput"] = FreeCameraMouseInput;

+ 144 - 144
src/Cameras/Inputs/freeCameraTouchInput.ts

@@ -5,182 +5,182 @@ import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManage
 import { FreeCamera } from "../../Cameras/freeCamera";
 import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
 import { Matrix, Vector3 } from "../../Maths/math";
+/**
+ * Manage the touch inputs to control the movement of a free camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
     /**
-     * Manage the touch inputs to control the movement of a free camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class FreeCameraTouchInput implements ICameraInput<FreeCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FreeCamera;
-
-        /**
-         * Defines the touch sensibility for rotation.
-         * The higher the faster.
-         */
-        @serialize()
-        public touchAngularSensibility: number = 200000.0;
-
-        /**
-         * Defines the touch sensibility for move.
-         * The higher the faster.
-         */
-        @serialize()
-        public touchMoveSensibility: number = 250.0;
-
-        private _offsetX: Nullable<number> = null;
-        private _offsetY: Nullable<number> = null;
-
-        private _pointerPressed = new Array<number>();
-        private _pointerInput: (p: PointerInfo, s: EventState) => void;
-        private _observer: Nullable<Observer<PointerInfo>>;
-        private _onLostFocus: Nullable<(e: FocusEvent) => any>;
-
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            var previousPosition: Nullable<{ x: number, y: number }> = null;
-
-            if (this._pointerInput === undefined) {
-                this._onLostFocus = () => {
-                    this._offsetX = null;
-                    this._offsetY = null;
-                };
+    public camera: FreeCamera;
 
-                this._pointerInput = (p) => {
-                    var evt = <PointerEvent>p.event;
+    /**
+     * Defines the touch sensibility for rotation.
+     * The higher the faster.
+     */
+    @serialize()
+    public touchAngularSensibility: number = 200000.0;
 
-                    if (evt.pointerType === "mouse") {
-                        return;
-                    }
+    /**
+     * Defines the touch sensibility for move.
+     * The higher the faster.
+     */
+    @serialize()
+    public touchMoveSensibility: number = 250.0;
 
-                    if (p.type === PointerEventTypes.POINTERDOWN) {
+    private _offsetX: Nullable<number> = null;
+    private _offsetY: Nullable<number> = null;
+
+    private _pointerPressed = new Array<number>();
+    private _pointerInput: (p: PointerInfo, s: EventState) => void;
+    private _observer: Nullable<Observer<PointerInfo>>;
+    private _onLostFocus: Nullable<(e: FocusEvent) => any>;
+
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        var previousPosition: Nullable<{ x: number, y: number }> = null;
+
+        if (this._pointerInput === undefined) {
+            this._onLostFocus = () => {
+                this._offsetX = null;
+                this._offsetY = null;
+            };
 
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
+            this._pointerInput = (p) => {
+                var evt = <PointerEvent>p.event;
 
-                        this._pointerPressed.push(evt.pointerId);
+                if (evt.pointerType === "mouse") {
+                    return;
+                }
 
-                        if (this._pointerPressed.length !== 1) {
-                            return;
-                        }
+                if (p.type === PointerEventTypes.POINTERDOWN) {
 
-                        previousPosition = {
-                            x: evt.clientX,
-                            y: evt.clientY
-                        };
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
                     }
 
-                    else if (p.type === PointerEventTypes.POINTERUP) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
+                    this._pointerPressed.push(evt.pointerId);
 
-                        var index: number = this._pointerPressed.indexOf(evt.pointerId);
+                    if (this._pointerPressed.length !== 1) {
+                        return;
+                    }
 
-                        if (index === -1) {
-                            return;
-                        }
-                        this._pointerPressed.splice(index, 1);
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+                }
 
-                        if (index != 0) {
-                            return;
-                        }
-                        previousPosition = null;
-                        this._offsetX = null;
-                        this._offsetY = null;
+                else if (p.type === PointerEventTypes.POINTERUP) {
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
                     }
 
-                    else if (p.type === PointerEventTypes.POINTERMOVE) {
-                        if (!noPreventDefault) {
-                            evt.preventDefault();
-                        }
+                    var index: number = this._pointerPressed.indexOf(evt.pointerId);
 
-                        if (!previousPosition) {
-                            return;
-                        }
+                    if (index === -1) {
+                        return;
+                    }
+                    this._pointerPressed.splice(index, 1);
 
-                        var index: number = this._pointerPressed.indexOf(evt.pointerId);
+                    if (index != 0) {
+                        return;
+                    }
+                    previousPosition = null;
+                    this._offsetX = null;
+                    this._offsetY = null;
+                }
 
-                        if (index != 0) {
-                            return;
-                        }
+                else if (p.type === PointerEventTypes.POINTERMOVE) {
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
 
-                        this._offsetX = evt.clientX - previousPosition.x;
-                        this._offsetY = -(evt.clientY - previousPosition.y);
+                    if (!previousPosition) {
+                        return;
                     }
-                };
-            }
 
-            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
+                    var index: number = this._pointerPressed.indexOf(evt.pointerId);
 
-            if (this._onLostFocus) {
-                element.addEventListener("blur", this._onLostFocus);
-            }
-        }
+                    if (index != 0) {
+                        return;
+                    }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            if (this._pointerInput && element) {
-                if (this._observer) {
-                    this.camera.getScene().onPointerObservable.remove(this._observer);
-                    this._observer = null;
+                    this._offsetX = evt.clientX - previousPosition.x;
+                    this._offsetY = -(evt.clientY - previousPosition.y);
                 }
+            };
+        }
 
-                if (this._onLostFocus) {
-                    element.removeEventListener("blur", this._onLostFocus);
-                    this._onLostFocus = null;
-                }
-                this._pointerPressed = [];
-                this._offsetX = null;
-                this._offsetY = null;
-            }
+        this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
+
+        if (this._onLostFocus) {
+            element.addEventListener("blur", this._onLostFocus);
         }
+    }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs(): void {
-            if (this._offsetX && this._offsetY) {
-                var camera = this.camera;
-                camera.cameraRotation.y += this._offsetX / this.touchAngularSensibility;
-
-                if (this._pointerPressed.length > 1) {
-                    camera.cameraRotation.x += -this._offsetY / this.touchAngularSensibility;
-                } else {
-                    var speed = camera._computeLocalCameraSpeed();
-                    var direction = new Vector3(0, 0, speed * this._offsetY / this.touchMoveSensibility);
-
-                    Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
-                    camera.cameraDirection.addInPlace(Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
-                }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        if (this._pointerInput && element) {
+            if (this._observer) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
             }
-        }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FreeCameraTouchInput";
+            if (this._onLostFocus) {
+                element.removeEventListener("blur", this._onLostFocus);
+                this._onLostFocus = null;
+            }
+            this._pointerPressed = [];
+            this._offsetX = null;
+            this._offsetY = null;
         }
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "touch";
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs(): void {
+        if (this._offsetX && this._offsetY) {
+            var camera = this.camera;
+            camera.cameraRotation.y += this._offsetX / this.touchAngularSensibility;
+
+            if (this._pointerPressed.length > 1) {
+                camera.cameraRotation.x += -this._offsetY / this.touchAngularSensibility;
+            } else {
+                var speed = camera._computeLocalCameraSpeed();
+                var direction = new Vector3(0, 0, speed * this._offsetY / this.touchMoveSensibility);
+
+                Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
+                camera.cameraDirection.addInPlace(Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+            }
         }
     }
 
-    (<any>CameraInputTypes)["FreeCameraTouchInput"] = FreeCameraTouchInput;
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FreeCameraTouchInput";
+    }
+
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "touch";
+    }
+}
+
+(<any>CameraInputTypes)["FreeCameraTouchInput"] = FreeCameraTouchInput;

+ 81 - 81
src/Cameras/Inputs/freeCameraVirtualJoystickInput.ts

@@ -3,99 +3,99 @@ import { Nullable } from "../../types";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
 import { FreeCamera } from "../../Cameras/freeCamera";
 import { Matrix, Vector3 } from "../../Maths/math";
+/**
+ * Manage the Virtual Joystick inputs to control the movement of a free camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FreeCameraVirtualJoystickInput implements ICameraInput<FreeCamera> {
     /**
-     * Manage the Virtual Joystick inputs to control the movement of a free camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the camera the input is attached to.
      */
-    export class FreeCameraVirtualJoystickInput implements ICameraInput<FreeCamera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        public camera: FreeCamera;
+    public camera: FreeCamera;
 
-        private _leftjoystick: VirtualJoystick;
-        private _rightjoystick: VirtualJoystick;
+    private _leftjoystick: VirtualJoystick;
+    private _rightjoystick: VirtualJoystick;
 
-        /**
-         * Gets the left stick of the virtual joystick.
-         * @returns The virtual Joystick
-         */
-        public getLeftJoystick(): VirtualJoystick {
-            return this._leftjoystick;
-        }
+    /**
+     * Gets the left stick of the virtual joystick.
+     * @returns The virtual Joystick
+     */
+    public getLeftJoystick(): VirtualJoystick {
+        return this._leftjoystick;
+    }
 
-        /**
-         * Gets the right stick of the virtual joystick.
-         * @returns The virtual Joystick
-         */
-        public getRightJoystick(): VirtualJoystick {
-            return this._rightjoystick;
-        }
+    /**
+     * Gets the right stick of the virtual joystick.
+     * @returns The virtual Joystick
+     */
+    public getRightJoystick(): VirtualJoystick {
+        return this._rightjoystick;
+    }
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs() {
-            if (this._leftjoystick) {
-                var camera = this.camera;
-                var speed = camera._computeLocalCameraSpeed() * 50;
-                var cameraTransform = Matrix.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, 0);
-                var deltaTransform = Vector3.TransformCoordinates(new Vector3(this._leftjoystick.deltaPosition.x * speed, this._leftjoystick.deltaPosition.y * speed, this._leftjoystick.deltaPosition.z * speed), cameraTransform);
-                camera.cameraDirection = camera.cameraDirection.add(deltaTransform);
-                camera.cameraRotation = camera.cameraRotation.addVector3(this._rightjoystick.deltaPosition);
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs() {
+        if (this._leftjoystick) {
+            var camera = this.camera;
+            var speed = camera._computeLocalCameraSpeed() * 50;
+            var cameraTransform = Matrix.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, 0);
+            var deltaTransform = Vector3.TransformCoordinates(new Vector3(this._leftjoystick.deltaPosition.x * speed, this._leftjoystick.deltaPosition.y * speed, this._leftjoystick.deltaPosition.z * speed), cameraTransform);
+            camera.cameraDirection = camera.cameraDirection.add(deltaTransform);
+            camera.cameraRotation = camera.cameraRotation.addVector3(this._rightjoystick.deltaPosition);
 
-                if (!this._leftjoystick.pressed) {
-                    this._leftjoystick.deltaPosition = this._leftjoystick.deltaPosition.scale(0.9);
-                }
-                if (!this._rightjoystick.pressed) {
-                    this._rightjoystick.deltaPosition = this._rightjoystick.deltaPosition.scale(0.9);
-                }
+            if (!this._leftjoystick.pressed) {
+                this._leftjoystick.deltaPosition = this._leftjoystick.deltaPosition.scale(0.9);
+            }
+            if (!this._rightjoystick.pressed) {
+                this._rightjoystick.deltaPosition = this._rightjoystick.deltaPosition.scale(0.9);
             }
         }
+    }
 
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element : HTMLElement, noPreventDefault?: boolean): void {
-            this._leftjoystick = new VirtualJoystick(true);
-            this._leftjoystick.setAxisForUpDown(JoystickAxis.Z);
-            this._leftjoystick.setAxisForLeftRight(JoystickAxis.X);
-            this._leftjoystick.setJoystickSensibility(0.15);
-            this._rightjoystick = new VirtualJoystick(false);
-            this._rightjoystick.setAxisForUpDown(JoystickAxis.X);
-            this._rightjoystick.setAxisForLeftRight(JoystickAxis.Y);
-            this._rightjoystick.reverseUpDown = true;
-            this._rightjoystick.setJoystickSensibility(0.05);
-            this._rightjoystick.setJoystickColor("yellow");
-        }
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        this._leftjoystick = new VirtualJoystick(true);
+        this._leftjoystick.setAxisForUpDown(JoystickAxis.Z);
+        this._leftjoystick.setAxisForLeftRight(JoystickAxis.X);
+        this._leftjoystick.setJoystickSensibility(0.15);
+        this._rightjoystick = new VirtualJoystick(false);
+        this._rightjoystick.setAxisForUpDown(JoystickAxis.X);
+        this._rightjoystick.setAxisForLeftRight(JoystickAxis.Y);
+        this._rightjoystick.reverseUpDown = true;
+        this._rightjoystick.setJoystickSensibility(0.05);
+        this._rightjoystick.setJoystickColor("yellow");
+    }
 
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: Nullable<HTMLElement>): void {
-            this._leftjoystick.releaseCanvas();
-            this._rightjoystick.releaseCanvas();
-        }
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: Nullable<HTMLElement>): void {
+        this._leftjoystick.releaseCanvas();
+        this._rightjoystick.releaseCanvas();
+    }
 
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FreeCameraVirtualJoystickInput";
-        }
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FreeCameraVirtualJoystickInput";
+    }
 
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        public getSimpleName(): string {
-            return "virtualJoystick";
-        }
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    public getSimpleName(): string {
+        return "virtualJoystick";
     }
+}
 
-    (<any>CameraInputTypes)["FreeCameraVirtualJoystickInput"] = FreeCameraVirtualJoystickInput;
+(<any>CameraInputTypes)["FreeCameraVirtualJoystickInput"] = FreeCameraVirtualJoystickInput;

+ 29 - 29
src/Cameras/Stereoscopic/anaglyphArcRotateCamera.ts

@@ -4,37 +4,37 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("AnaglyphArcRotateCamera", (name, scene, options) => {
-        return () => new AnaglyphArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), options.interaxial_distance, scene);
-    });
+Node.AddNodeConstructor("AnaglyphArcRotateCamera", (name, scene, options) => {
+    return () => new AnaglyphArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), options.interaxial_distance, scene);
+});
+
+/**
+ * Camera used to simulate anaglyphic rendering (based on ArcRotateCamera)
+ * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+ */
+export class AnaglyphArcRotateCamera extends ArcRotateCamera {
 
     /**
-     * Camera used to simulate anaglyphic rendering (based on ArcRotateCamera)
-     * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+     * Creates a new AnaglyphArcRotateCamera
+     * @param name defines camera name
+     * @param alpha defines alpha angle (in radians)
+     * @param beta defines beta angle (in radians)
+     * @param radius defines radius
+     * @param target defines camera target
+     * @param interaxialDistance defines distance between each color axis
+     * @param scene defines the hosting scene
      */
-    export class AnaglyphArcRotateCamera extends ArcRotateCamera {
-
-        /**
-         * Creates a new AnaglyphArcRotateCamera
-         * @param name defines camera name
-         * @param alpha defines alpha angle (in radians)
-         * @param beta defines beta angle (in radians)
-         * @param radius defines radius
-         * @param target defines camera target
-         * @param interaxialDistance defines distance between each color axis
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, interaxialDistance: number, scene: Scene) {
-            super(name, alpha, beta, radius, target, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, interaxialDistance: number, scene: Scene) {
+        super(name, alpha, beta, radius, target, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns AnaglyphArcRotateCamera
-         */
-        public getClassName(): string {
-            return "AnaglyphArcRotateCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns AnaglyphArcRotateCamera
+     */
+    public getClassName(): string {
+        return "AnaglyphArcRotateCamera";
     }
+}

+ 25 - 25
src/Cameras/Stereoscopic/anaglyphFreeCamera.ts

@@ -4,33 +4,33 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("AnaglyphFreeCamera", (name, scene, options) => {
-        return () => new AnaglyphFreeCamera(name, Vector3.Zero(), options.interaxial_distance, scene);
-    });
+Node.AddNodeConstructor("AnaglyphFreeCamera", (name, scene, options) => {
+    return () => new AnaglyphFreeCamera(name, Vector3.Zero(), options.interaxial_distance, scene);
+});
 
+/**
+ * Camera used to simulate anaglyphic rendering (based on FreeCamera)
+ * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+ */
+export class AnaglyphFreeCamera extends FreeCamera {
     /**
-     * Camera used to simulate anaglyphic rendering (based on FreeCamera)
-     * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+     * Creates a new AnaglyphFreeCamera
+     * @param name defines camera name
+     * @param position defines initial position
+     * @param interaxialDistance defines distance between each color axis
+     * @param scene defines the hosting scene
      */
-    export class AnaglyphFreeCamera extends FreeCamera {
-        /**
-         * Creates a new AnaglyphFreeCamera
-         * @param name defines camera name
-         * @param position defines initial position
-         * @param interaxialDistance defines distance between each color axis
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, position: Vector3, interaxialDistance: number, scene: Scene) {
-            super(name, position, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, position: Vector3, interaxialDistance: number, scene: Scene) {
+        super(name, position, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns AnaglyphFreeCamera
-         */
-        public getClassName(): string {
-            return "AnaglyphFreeCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns AnaglyphFreeCamera
+     */
+    public getClassName(): string {
+        return "AnaglyphFreeCamera";
     }
+}

+ 25 - 25
src/Cameras/Stereoscopic/anaglyphGamepadCamera.ts

@@ -4,33 +4,33 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("AnaglyphGamepadCamera", (name, scene, options) => {
-        return () => new AnaglyphGamepadCamera(name, Vector3.Zero(), options.interaxial_distance, scene);
-    });
+Node.AddNodeConstructor("AnaglyphGamepadCamera", (name, scene, options) => {
+    return () => new AnaglyphGamepadCamera(name, Vector3.Zero(), options.interaxial_distance, scene);
+});
 
+/**
+ * Camera used to simulate anaglyphic rendering (based on GamepadCamera)
+ * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+ */
+export class AnaglyphGamepadCamera extends GamepadCamera {
     /**
-     * Camera used to simulate anaglyphic rendering (based on GamepadCamera)
-     * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+     * Creates a new AnaglyphGamepadCamera
+     * @param name defines camera name
+     * @param position defines initial position
+     * @param interaxialDistance defines distance between each color axis
+     * @param scene defines the hosting scene
      */
-    export class AnaglyphGamepadCamera extends GamepadCamera {
-        /**
-         * Creates a new AnaglyphGamepadCamera
-         * @param name defines camera name
-         * @param position defines initial position
-         * @param interaxialDistance defines distance between each color axis
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, position: Vector3, interaxialDistance: number, scene: Scene) {
-            super(name, position, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, position: Vector3, interaxialDistance: number, scene: Scene) {
+        super(name, position, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns AnaglyphGamepadCamera
-         */
-        public getClassName(): string {
-            return "AnaglyphGamepadCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns AnaglyphGamepadCamera
+     */
+    public getClassName(): string {
+        return "AnaglyphGamepadCamera";
     }
+}

+ 25 - 25
src/Cameras/Stereoscopic/anaglyphUniversalCamera.ts

@@ -4,33 +4,33 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("AnaglyphUniversalCamera", (name, scene, options) => {
-        return () => new AnaglyphUniversalCamera(name, Vector3.Zero(), options.interaxial_distance, scene);
-    });
+Node.AddNodeConstructor("AnaglyphUniversalCamera", (name, scene, options) => {
+    return () => new AnaglyphUniversalCamera(name, Vector3.Zero(), options.interaxial_distance, scene);
+});
 
+/**
+ * Camera used to simulate anaglyphic rendering (based on UniversalCamera)
+ * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+ */
+export class AnaglyphUniversalCamera extends UniversalCamera {
     /**
-     * Camera used to simulate anaglyphic rendering (based on UniversalCamera)
-     * @see http://doc.babylonjs.com/features/cameras#anaglyph-cameras
+     * Creates a new AnaglyphUniversalCamera
+     * @param name defines camera name
+     * @param position defines initial position
+     * @param interaxialDistance defines distance between each color axis
+     * @param scene defines the hosting scene
      */
-    export class AnaglyphUniversalCamera extends UniversalCamera {
-        /**
-         * Creates a new AnaglyphUniversalCamera
-         * @param name defines camera name
-         * @param position defines initial position
-         * @param interaxialDistance defines distance between each color axis
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, position: Vector3, interaxialDistance: number, scene: Scene) {
-            super(name, position, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, position: Vector3, interaxialDistance: number, scene: Scene) {
+        super(name, position, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.setCameraRigMode(Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns AnaglyphUniversalCamera
-         */
-        public getClassName(): string {
-            return "AnaglyphUniversalCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns AnaglyphUniversalCamera
+     */
+    public getClassName(): string {
+        return "AnaglyphUniversalCamera";
     }
+}

+ 30 - 30
src/Cameras/Stereoscopic/stereoscopicArcRotateCamera.ts

@@ -4,38 +4,38 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("StereoscopicArcRotateCamera", (name, scene, options) => {
-        return () => new StereoscopicArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
-    });
+Node.AddNodeConstructor("StereoscopicArcRotateCamera", (name, scene, options) => {
+    return () => new StereoscopicArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
+});
 
+/**
+ * Camera used to simulate stereoscopic rendering (based on ArcRotateCamera)
+ * @see http://doc.babylonjs.com/features/cameras
+ */
+export class StereoscopicArcRotateCamera extends ArcRotateCamera {
     /**
-     * Camera used to simulate stereoscopic rendering (based on ArcRotateCamera)
-     * @see http://doc.babylonjs.com/features/cameras
+     * Creates a new StereoscopicArcRotateCamera
+     * @param name defines camera name
+     * @param alpha defines alpha angle (in radians)
+     * @param beta defines beta angle (in radians)
+     * @param radius defines radius
+     * @param target defines camera target
+     * @param interaxialDistance defines distance between each color axis
+     * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
+     * @param scene defines the hosting scene
      */
-    export class StereoscopicArcRotateCamera extends ArcRotateCamera {
-        /**
-         * Creates a new StereoscopicArcRotateCamera
-         * @param name defines camera name
-         * @param alpha defines alpha angle (in radians)
-         * @param beta defines beta angle (in radians)
-         * @param radius defines radius
-         * @param target defines camera target
-         * @param interaxialDistance defines distance between each color axis
-         * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
-            super(name, alpha, beta, radius, target, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.isStereoscopicSideBySide = isStereoscopicSideBySide;
-            this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
+        super(name, alpha, beta, radius, target, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.isStereoscopicSideBySide = isStereoscopicSideBySide;
+        this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns StereoscopicArcRotateCamera
-         */
-        public getClassName(): string {
-            return "StereoscopicArcRotateCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns StereoscopicArcRotateCamera
+     */
+    public getClassName(): string {
+        return "StereoscopicArcRotateCamera";
     }
+}

+ 27 - 27
src/Cameras/Stereoscopic/stereoscopicFreeCamera.ts

@@ -4,35 +4,35 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("StereoscopicFreeCamera", (name, scene, options) => {
-        return () => new StereoscopicFreeCamera(name, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
-    });
+Node.AddNodeConstructor("StereoscopicFreeCamera", (name, scene, options) => {
+    return () => new StereoscopicFreeCamera(name, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
+});
 
+/**
+ * Camera used to simulate stereoscopic rendering (based on FreeCamera)
+ * @see http://doc.babylonjs.com/features/cameras
+ */
+export class StereoscopicFreeCamera extends FreeCamera {
     /**
-     * Camera used to simulate stereoscopic rendering (based on FreeCamera)
-     * @see http://doc.babylonjs.com/features/cameras
+     * Creates a new StereoscopicFreeCamera
+     * @param name defines camera name
+     * @param position defines initial position
+     * @param interaxialDistance defines distance between each color axis
+     * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
+     * @param scene defines the hosting scene
      */
-    export class StereoscopicFreeCamera extends FreeCamera {
-        /**
-         * Creates a new StereoscopicFreeCamera
-         * @param name defines camera name
-         * @param position defines initial position
-         * @param interaxialDistance defines distance between each color axis
-         * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, position: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
-            super(name, position, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.isStereoscopicSideBySide = isStereoscopicSideBySide;
-            this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, position: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
+        super(name, position, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.isStereoscopicSideBySide = isStereoscopicSideBySide;
+        this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns StereoscopicFreeCamera
-         */
-        public getClassName(): string {
-            return "StereoscopicFreeCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns StereoscopicFreeCamera
+     */
+    public getClassName(): string {
+        return "StereoscopicFreeCamera";
     }
+}

+ 27 - 27
src/Cameras/Stereoscopic/stereoscopicGamepadCamera.ts

@@ -4,35 +4,35 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("StereoscopicGamepadCamera", (name, scene, options) => {
-        return () => new StereoscopicGamepadCamera(name, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
-    });
+Node.AddNodeConstructor("StereoscopicGamepadCamera", (name, scene, options) => {
+    return () => new StereoscopicGamepadCamera(name, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
+});
 
+/**
+ * Camera used to simulate stereoscopic rendering (based on GamepadCamera)
+ * @see http://doc.babylonjs.com/features/cameras
+ */
+export class StereoscopicGamepadCamera extends GamepadCamera {
     /**
-     * Camera used to simulate stereoscopic rendering (based on GamepadCamera)
-     * @see http://doc.babylonjs.com/features/cameras
+     * Creates a new StereoscopicGamepadCamera
+     * @param name defines camera name
+     * @param position defines initial position
+     * @param interaxialDistance defines distance between each color axis
+     * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
+     * @param scene defines the hosting scene
      */
-    export class StereoscopicGamepadCamera extends GamepadCamera {
-        /**
-         * Creates a new StereoscopicGamepadCamera
-         * @param name defines camera name
-         * @param position defines initial position
-         * @param interaxialDistance defines distance between each color axis
-         * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, position: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
-            super(name, position, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.isStereoscopicSideBySide = isStereoscopicSideBySide;
-            this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, position: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
+        super(name, position, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.isStereoscopicSideBySide = isStereoscopicSideBySide;
+        this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns StereoscopicGamepadCamera
-         */
-        public getClassName(): string {
-            return "StereoscopicGamepadCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns StereoscopicGamepadCamera
+     */
+    public getClassName(): string {
+        return "StereoscopicGamepadCamera";
     }
+}

+ 27 - 27
src/Cameras/Stereoscopic/stereoscopicUniversalCamera.ts

@@ -4,34 +4,34 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("StereoscopicFreeCamera", (name, scene, options) => {
-        return () => new StereoscopicUniversalCamera(name, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
-    });
+Node.AddNodeConstructor("StereoscopicFreeCamera", (name, scene, options) => {
+    return () => new StereoscopicUniversalCamera(name, Vector3.Zero(), options.interaxial_distance, options.isStereoscopicSideBySide, scene);
+});
+/**
+ * Camera used to simulate stereoscopic rendering (based on UniversalCamera)
+ * @see http://doc.babylonjs.com/features/cameras
+ */
+export class StereoscopicUniversalCamera extends UniversalCamera {
     /**
-     * Camera used to simulate stereoscopic rendering (based on UniversalCamera)
-     * @see http://doc.babylonjs.com/features/cameras
+     * Creates a new StereoscopicUniversalCamera
+     * @param name defines camera name
+     * @param position defines initial position
+     * @param interaxialDistance defines distance between each color axis
+     * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
+     * @param scene defines the hosting scene
      */
-    export class StereoscopicUniversalCamera extends UniversalCamera {
-        /**
-         * Creates a new StereoscopicUniversalCamera
-         * @param name defines camera name
-         * @param position defines initial position
-         * @param interaxialDistance defines distance between each color axis
-         * @param isStereoscopicSideBySide defines is stereoscopic is done side by side or over under
-         * @param scene defines the hosting scene
-         */
-        constructor(name: string, position: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
-            super(name, position, scene);
-            this.interaxialDistance = interaxialDistance;
-            this.isStereoscopicSideBySide = isStereoscopicSideBySide;
-            this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
-        }
+    constructor(name: string, position: Vector3, interaxialDistance: number, isStereoscopicSideBySide: boolean, scene: Scene) {
+        super(name, position, scene);
+        this.interaxialDistance = interaxialDistance;
+        this.isStereoscopicSideBySide = isStereoscopicSideBySide;
+        this.setCameraRigMode(isStereoscopicSideBySide ? Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL : Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER, { interaxialDistance: interaxialDistance });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns StereoscopicUniversalCamera
-         */
-        public getClassName(): string {
-            return "StereoscopicUniversalCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns StereoscopicUniversalCamera
+     */
+    public getClassName(): string {
+        return "StereoscopicUniversalCamera";
     }
+}

+ 118 - 118
src/Cameras/VR/vrCameraMetrics.ts

@@ -1,132 +1,132 @@
 import { Matrix } from "../../Maths/math";
+/**
+ * This represents all the required metrics to create a VR camera.
+ * @see http://doc.babylonjs.com/babylon101/cameras#device-orientation-camera
+ */
+export class VRCameraMetrics {
     /**
-     * This represents all the required metrics to create a VR camera.
-     * @see http://doc.babylonjs.com/babylon101/cameras#device-orientation-camera
-     */
-    export class VRCameraMetrics {
-        /**
-         * Define the horizontal resolution off the screen.
-         */
-        public hResolution: number;
-        /**
-         * Define the vertical resolution off the screen.
-         */
-        public vResolution: number;
-        /**
-         * Define the horizontal screen size.
-         */
-        public hScreenSize: number;
-        /**
-         * Define the vertical screen size.
-         */
-        public vScreenSize: number;
-        /**
-         * Define the vertical screen center position.
-         */
-        public vScreenCenter: number;
-        /**
-         * Define the distance of the eyes to the screen.
-         */
-        public eyeToScreenDistance: number;
-        /**
-         * Define the distance between both lenses
-         */
-        public lensSeparationDistance: number;
-        /**
-         * Define the distance between both viewer's eyes.
-         */
-        public interpupillaryDistance: number;
-        /**
-         * Define the distortion factor of the VR postprocess.
-         * Please, touch with care.
-         */
-        public distortionK: number[];
-        /**
-         * Define the chromatic aberration correction factors for the VR post process.
-         */
-        public chromaAbCorrection: number[];
-        /**
-         * Define the scale factor of the post process.
-         * The smaller the better but the slower.
-         */
-        public postProcessScaleFactor: number;
-        /**
-         * Define an offset for the lens center.
-         */
-        public lensCenterOffset: number;
-        /**
-         * Define if the current vr camera should compensate the distortion of the lense or not.
-         */
-        public compensateDistortion = true;
+     * Define the horizontal resolution off the screen.
+     */
+    public hResolution: number;
+    /**
+     * Define the vertical resolution off the screen.
+     */
+    public vResolution: number;
+    /**
+     * Define the horizontal screen size.
+     */
+    public hScreenSize: number;
+    /**
+     * Define the vertical screen size.
+     */
+    public vScreenSize: number;
+    /**
+     * Define the vertical screen center position.
+     */
+    public vScreenCenter: number;
+    /**
+     * Define the distance of the eyes to the screen.
+     */
+    public eyeToScreenDistance: number;
+    /**
+     * Define the distance between both lenses
+     */
+    public lensSeparationDistance: number;
+    /**
+     * Define the distance between both viewer's eyes.
+     */
+    public interpupillaryDistance: number;
+    /**
+     * Define the distortion factor of the VR postprocess.
+     * Please, touch with care.
+     */
+    public distortionK: number[];
+    /**
+     * Define the chromatic aberration correction factors for the VR post process.
+     */
+    public chromaAbCorrection: number[];
+    /**
+     * Define the scale factor of the post process.
+     * The smaller the better but the slower.
+     */
+    public postProcessScaleFactor: number;
+    /**
+     * Define an offset for the lens center.
+     */
+    public lensCenterOffset: number;
+    /**
+     * Define if the current vr camera should compensate the distortion of the lense or not.
+     */
+    public compensateDistortion = true;
 
-        /**
-         * Gets the rendering aspect ratio based on the provided resolutions.
-         */
-        public get aspectRatio(): number {
-            return this.hResolution / (2 * this.vResolution);
-        }
+    /**
+     * Gets the rendering aspect ratio based on the provided resolutions.
+     */
+    public get aspectRatio(): number {
+        return this.hResolution / (2 * this.vResolution);
+    }
 
-        /**
-         * Gets the aspect ratio based on the FOV, scale factors, and real screen sizes.
-         */
-        public get aspectRatioFov(): number {
-            return (2 * Math.atan((this.postProcessScaleFactor * this.vScreenSize) / (2 * this.eyeToScreenDistance)));
-        }
+    /**
+     * Gets the aspect ratio based on the FOV, scale factors, and real screen sizes.
+     */
+    public get aspectRatioFov(): number {
+        return (2 * Math.atan((this.postProcessScaleFactor * this.vScreenSize) / (2 * this.eyeToScreenDistance)));
+    }
 
-        /**
-         * @hidden
-         */
-        public get leftHMatrix(): Matrix {
-            var meters = (this.hScreenSize / 4) - (this.lensSeparationDistance / 2);
-            var h = (4 * meters) / this.hScreenSize;
+    /**
+     * @hidden
+     */
+    public get leftHMatrix(): Matrix {
+        var meters = (this.hScreenSize / 4) - (this.lensSeparationDistance / 2);
+        var h = (4 * meters) / this.hScreenSize;
 
-            return Matrix.Translation(h, 0, 0);
-        }
+        return Matrix.Translation(h, 0, 0);
+    }
 
-        /**
-         * @hidden
-         */
-        public get rightHMatrix(): Matrix {
-            var meters = (this.hScreenSize / 4) - (this.lensSeparationDistance / 2);
-            var h = (4 * meters) / this.hScreenSize;
+    /**
+     * @hidden
+     */
+    public get rightHMatrix(): Matrix {
+        var meters = (this.hScreenSize / 4) - (this.lensSeparationDistance / 2);
+        var h = (4 * meters) / this.hScreenSize;
 
-            return Matrix.Translation(-h, 0, 0);
-        }
+        return Matrix.Translation(-h, 0, 0);
+    }
 
-        /**
-         * @hidden
-         */
-        public get leftPreViewMatrix(): Matrix {
-            return Matrix.Translation(0.5 * this.interpupillaryDistance, 0, 0);
-        }
+    /**
+     * @hidden
+     */
+    public get leftPreViewMatrix(): Matrix {
+        return Matrix.Translation(0.5 * this.interpupillaryDistance, 0, 0);
+    }
 
-        /**
-         * @hidden
-         */
-        public get rightPreViewMatrix(): Matrix {
-            return Matrix.Translation(-0.5 * this.interpupillaryDistance, 0, 0);
-        }
+    /**
+     * @hidden
+     */
+    public get rightPreViewMatrix(): Matrix {
+        return Matrix.Translation(-0.5 * this.interpupillaryDistance, 0, 0);
+    }
 
-        /**
-         * Get the default VRMetrics based on the most generic setup.
-         * @returns the default vr metrics
-         */
-        public static GetDefault(): VRCameraMetrics {
-            var result = new VRCameraMetrics();
+    /**
+     * Get the default VRMetrics based on the most generic setup.
+     * @returns the default vr metrics
+     */
+    public static GetDefault(): VRCameraMetrics {
+        var result = new VRCameraMetrics();
 
-            result.hResolution = 1280;
-            result.vResolution = 800;
-            result.hScreenSize = 0.149759993;
-            result.vScreenSize = 0.0935999975;
-            result.vScreenCenter = 0.0467999987;
-            result.eyeToScreenDistance = 0.0410000011;
-            result.lensSeparationDistance = 0.0635000020;
-            result.interpupillaryDistance = 0.0640000030;
-            result.distortionK = [1.0, 0.219999999, 0.239999995, 0.0];
-            result.chromaAbCorrection = [0.995999992, -0.00400000019, 1.01400006, 0.0];
-            result.postProcessScaleFactor = 1.714605507808412;
-            result.lensCenterOffset = 0.151976421;
+        result.hResolution = 1280;
+        result.vResolution = 800;
+        result.hScreenSize = 0.149759993;
+        result.vScreenSize = 0.0935999975;
+        result.vScreenCenter = 0.0467999987;
+        result.eyeToScreenDistance = 0.0410000011;
+        result.lensSeparationDistance = 0.0635000020;
+        result.interpupillaryDistance = 0.0640000030;
+        result.distortionK = [1.0, 0.219999999, 0.239999995, 0.0];
+        result.chromaAbCorrection = [0.995999992, -0.00400000019, 1.01400006, 0.0];
+        result.postProcessScaleFactor = 1.714605507808412;
+        result.lensCenterOffset = 0.151976421;
 
-            return result;
-        }
+        return result;
     }
+}

+ 31 - 31
src/Cameras/VR/vrDeviceOrientationArcRotateCamera.ts

@@ -5,41 +5,41 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("VRDeviceOrientationFreeCamera", (name, scene) => {
-        return () => new VRDeviceOrientationArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), scene);
-    });
+Node.AddNodeConstructor("VRDeviceOrientationFreeCamera", (name, scene) => {
+    return () => new VRDeviceOrientationArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), scene);
+});
+
+/**
+ * Camera used to simulate VR rendering (based on ArcRotateCamera)
+ * @see http://doc.babylonjs.com/babylon101/cameras#vr-device-orientation-cameras
+ */
+export class VRDeviceOrientationArcRotateCamera extends ArcRotateCamera {
 
     /**
-     * Camera used to simulate VR rendering (based on ArcRotateCamera)
-     * @see http://doc.babylonjs.com/babylon101/cameras#vr-device-orientation-cameras
+     * Creates a new VRDeviceOrientationArcRotateCamera
+     * @param name defines camera name
+     * @param alpha defines the camera rotation along the logitudinal axis
+     * @param beta defines the camera rotation along the latitudinal axis
+     * @param radius defines the camera distance from its target
+     * @param target defines the camera target
+     * @param scene defines the scene the camera belongs to
+     * @param compensateDistortion defines if the camera needs to compensate the lens distorsion
+     * @param vrCameraMetrics defines the vr metrics associated to the camera
      */
-    export class VRDeviceOrientationArcRotateCamera extends ArcRotateCamera {
-
-        /**
-         * Creates a new VRDeviceOrientationArcRotateCamera
-         * @param name defines camera name
-         * @param alpha defines the camera rotation along the logitudinal axis
-         * @param beta defines the camera rotation along the latitudinal axis
-         * @param radius defines the camera distance from its target
-         * @param target defines the camera target
-         * @param scene defines the scene the camera belongs to
-         * @param compensateDistortion defines if the camera needs to compensate the lens distorsion
-         * @param vrCameraMetrics defines the vr metrics associated to the camera
-         */
-        constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
-            super(name, alpha, beta, radius, target, scene);
+    constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
+        super(name, alpha, beta, radius, target, scene);
 
-            vrCameraMetrics.compensateDistortion = compensateDistortion;
-            this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: vrCameraMetrics });
+        vrCameraMetrics.compensateDistortion = compensateDistortion;
+        this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: vrCameraMetrics });
 
-            this.inputs.addVRDeviceOrientation();
-        }
+        this.inputs.addVRDeviceOrientation();
+    }
 
-        /**
-         * Gets camera class name
-         * @returns VRDeviceOrientationArcRotateCamera
-         */
-        public getClassName(): string {
-            return "VRDeviceOrientationArcRotateCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns VRDeviceOrientationArcRotateCamera
+     */
+    public getClassName(): string {
+        return "VRDeviceOrientationArcRotateCamera";
     }
+}

+ 27 - 27
src/Cameras/VR/vrDeviceOrientationFreeCamera.ts

@@ -5,36 +5,36 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("VRDeviceOrientationFreeCamera", (name, scene) => {
-        return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
-    });
+Node.AddNodeConstructor("VRDeviceOrientationFreeCamera", (name, scene) => {
+    return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
+});
+
+/**
+ * Camera used to simulate VR rendering (based on FreeCamera)
+ * @see http://doc.babylonjs.com/babylon101/cameras#vr-device-orientation-cameras
+ */
+export class VRDeviceOrientationFreeCamera extends DeviceOrientationCamera {
 
     /**
-     * Camera used to simulate VR rendering (based on FreeCamera)
-     * @see http://doc.babylonjs.com/babylon101/cameras#vr-device-orientation-cameras
+     * Creates a new VRDeviceOrientationFreeCamera
+     * @param name defines camera name
+     * @param position defines the start position of the camera
+     * @param scene defines the scene the camera belongs to
+     * @param compensateDistortion defines if the camera needs to compensate the lens distorsion
+     * @param vrCameraMetrics defines the vr metrics associated to the camera
      */
-    export class VRDeviceOrientationFreeCamera extends DeviceOrientationCamera {
-
-        /**
-         * Creates a new VRDeviceOrientationFreeCamera
-         * @param name defines camera name
-         * @param position defines the start position of the camera
-         * @param scene defines the scene the camera belongs to
-         * @param compensateDistortion defines if the camera needs to compensate the lens distorsion
-         * @param vrCameraMetrics defines the vr metrics associated to the camera
-         */
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
-            super(name, position, scene);
+    constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
+        super(name, position, scene);
 
-            vrCameraMetrics.compensateDistortion = compensateDistortion;
-            this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: vrCameraMetrics });
-        }
+        vrCameraMetrics.compensateDistortion = compensateDistortion;
+        this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: vrCameraMetrics });
+    }
 
-        /**
-         * Gets camera class name
-         * @returns VRDeviceOrientationFreeCamera
-         */
-        public getClassName(): string {
-            return "VRDeviceOrientationFreeCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns VRDeviceOrientationFreeCamera
+     */
+    public getClassName(): string {
+        return "VRDeviceOrientationFreeCamera";
     }
+}

+ 26 - 26
src/Cameras/VR/vrDeviceOrientationGamepadCamera.ts

@@ -4,35 +4,35 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { Node } from "../../node";
 
-    Node.AddNodeConstructor("VRDeviceOrientationGamepadCamera", (name, scene) => {
-        return () => new VRDeviceOrientationGamepadCamera(name, Vector3.Zero(), scene);
-    });
+Node.AddNodeConstructor("VRDeviceOrientationGamepadCamera", (name, scene) => {
+    return () => new VRDeviceOrientationGamepadCamera(name, Vector3.Zero(), scene);
+});
+
+/**
+ * Camera used to simulate VR rendering (based on VRDeviceOrientationFreeCamera)
+ * @see http://doc.babylonjs.com/babylon101/cameras#vr-device-orientation-cameras
+ */
+export class VRDeviceOrientationGamepadCamera extends VRDeviceOrientationFreeCamera {
 
     /**
-     * Camera used to simulate VR rendering (based on VRDeviceOrientationFreeCamera)
-     * @see http://doc.babylonjs.com/babylon101/cameras#vr-device-orientation-cameras
+     * Creates a new VRDeviceOrientationGamepadCamera
+     * @param name defines camera name
+     * @param position defines the start position of the camera
+     * @param scene defines the scene the camera belongs to
+     * @param compensateDistortion defines if the camera needs to compensate the lens distorsion
+     * @param vrCameraMetrics defines the vr metrics associated to the camera
      */
-    export class VRDeviceOrientationGamepadCamera extends VRDeviceOrientationFreeCamera {
-
-        /**
-         * Creates a new VRDeviceOrientationGamepadCamera
-         * @param name defines camera name
-         * @param position defines the start position of the camera
-         * @param scene defines the scene the camera belongs to
-         * @param compensateDistortion defines if the camera needs to compensate the lens distorsion
-         * @param vrCameraMetrics defines the vr metrics associated to the camera
-         */
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
-            super(name, position, scene, compensateDistortion, vrCameraMetrics);
+    constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
+        super(name, position, scene, compensateDistortion, vrCameraMetrics);
 
-            this.inputs.addGamepad();
-        }
+        this.inputs.addGamepad();
+    }
 
-        /**
-         * Gets camera class name
-         * @returns VRDeviceOrientationGamepadCamera
-         */
-        public getClassName(): string {
-            return "VRDeviceOrientationGamepadCamera";
-        }
+    /**
+     * Gets camera class name
+     * @returns VRDeviceOrientationGamepadCamera
+     */
+    public getClassName(): string {
+        return "VRDeviceOrientationGamepadCamera";
     }
+}

文件差異過大導致無法顯示
+ 1686 - 1686
src/Cameras/VR/vrExperienceHelper.ts


文件差異過大導致無法顯示
+ 665 - 665
src/Cameras/VR/webVRCamera.ts


+ 87 - 87
src/Cameras/XR/webXRCamera.ts

@@ -5,106 +5,106 @@ import { FreeCamera } from "../../Cameras/freeCamera";
 import { TargetCamera } from "../../Cameras/targetCamera";
 import { WebXRSessionManager } from "./webXRSessionManager";
 
+/**
+ * WebXR Camera which holds the views for the xrSession
+ * @see https://doc.babylonjs.com/how_to/webxr
+ */
+export class WebXRCamera extends FreeCamera {
+    private static _TmpMatrix = new Matrix();
+
     /**
-     * WebXR Camera which holds the views for the xrSession
-     * @see https://doc.babylonjs.com/how_to/webxr
+     * Creates a new webXRCamera, this should only be set at the camera after it has been updated by the xrSessionManager
+     * @param name the name of the camera
+     * @param scene the scene to add the camera to
      */
-    export class WebXRCamera extends FreeCamera {
-        private static _TmpMatrix = new Matrix();
+    constructor(name: string, scene: Scene) {
+        super(name, Vector3.Zero(), scene);
 
-        /**
-         * Creates a new webXRCamera, this should only be set at the camera after it has been updated by the xrSessionManager
-         * @param name the name of the camera
-         * @param scene the scene to add the camera to
-         */
-        constructor(name: string, scene: Scene) {
-            super(name, Vector3.Zero(), scene);
+        // Initial camera configuration
+        this.minZ = 0;
+        this.rotationQuaternion = new Quaternion();
+        this.cameraRigMode = Camera.RIG_MODE_CUSTOM;
+        this.updateUpVectorFromRotation = true;
+        this._updateNumberOfRigCameras(1);
+    }
 
-            // Initial camera configuration
-            this.minZ = 0;
-            this.rotationQuaternion = new Quaternion();
-            this.cameraRigMode = Camera.RIG_MODE_CUSTOM;
-            this.updateUpVectorFromRotation = true;
-            this._updateNumberOfRigCameras(1);
+    private _updateNumberOfRigCameras(viewCount = 1) {
+        while (this.rigCameras.length < viewCount) {
+            var newCamera = new TargetCamera("view: " + this.rigCameras.length, Vector3.Zero(), this.getScene());
+            newCamera.minZ = 0;
+            newCamera.parent = this;
+            newCamera.rotationQuaternion = new Quaternion();
+            newCamera.updateUpVectorFromRotation = true;
+            this.rigCameras.push(newCamera);
         }
-
-        private _updateNumberOfRigCameras(viewCount = 1) {
-            while (this.rigCameras.length < viewCount) {
-                var newCamera = new TargetCamera("view: " + this.rigCameras.length, Vector3.Zero(), this.getScene());
-                newCamera.minZ = 0;
-                newCamera.parent = this;
-                newCamera.rotationQuaternion = new Quaternion();
-                newCamera.updateUpVectorFromRotation = true;
-                this.rigCameras.push(newCamera);
-            }
-            while (this.rigCameras.length > viewCount) {
-                var removedCamera = this.rigCameras.pop();
-                if (removedCamera) {
-                    removedCamera.dispose();
-                }
+        while (this.rigCameras.length > viewCount) {
+            var removedCamera = this.rigCameras.pop();
+            if (removedCamera) {
+                removedCamera.dispose();
             }
         }
+    }
+
+    /** @hidden */
+    public _updateForDualEyeDebugging(pupilDistance = 0.01) {
+        // Create initial camera rigs
+        this._updateNumberOfRigCameras(2);
+        this.rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
+        this.rigCameras[0].position.x = -pupilDistance / 2;
+        this.rigCameras[0].outputRenderTarget = null;
+        this.rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
+        this.rigCameras[1].position.x = pupilDistance / 2;
+        this.rigCameras[1].outputRenderTarget = null;
+    }
 
-        /** @hidden */
-        public _updateForDualEyeDebugging(pupilDistance = 0.01) {
-            // Create initial camera rigs
-            this._updateNumberOfRigCameras(2);
-            this.rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
-            this.rigCameras[0].position.x = -pupilDistance / 2;
-            this.rigCameras[0].outputRenderTarget = null;
-            this.rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
-            this.rigCameras[1].position.x = pupilDistance / 2;
-            this.rigCameras[1].outputRenderTarget = null;
+    /**
+     * Updates the cameras position from the current pose information of the  XR session
+     * @param xrSessionManager the session containing pose information
+     * @returns true if the camera has been updated, false if the session did not contain pose or frame data
+     */
+    public updateFromXRSessionManager(xrSessionManager: WebXRSessionManager) {
+        // Ensure all frame data is available
+        if (!xrSessionManager._currentXRFrame || !xrSessionManager._currentXRFrame.getDevicePose) {
+            return false;
+        }
+        var pose = xrSessionManager._currentXRFrame.getDevicePose(xrSessionManager._frameOfReference);
+        if (!pose || !pose.poseModelMatrix) {
+            return false;
         }
 
-        /**
-         * Updates the cameras position from the current pose information of the  XR session
-         * @param xrSessionManager the session containing pose information
-         * @returns true if the camera has been updated, false if the session did not contain pose or frame data
-         */
-        public updateFromXRSessionManager(xrSessionManager: WebXRSessionManager) {
-            // Ensure all frame data is available
-            if (!xrSessionManager._currentXRFrame || !xrSessionManager._currentXRFrame.getDevicePose) {
-                return false;
-            }
-            var pose = xrSessionManager._currentXRFrame.getDevicePose(xrSessionManager._frameOfReference);
-            if (!pose || !pose.poseModelMatrix) {
-                return false;
-            }
+        // Update the parent cameras matrix
+        Matrix.FromFloat32ArrayToRefScaled(pose.poseModelMatrix, 0, 1, WebXRCamera._TmpMatrix);
+        if (!this._scene.useRightHandedSystem) {
+            WebXRCamera._TmpMatrix.toggleModelMatrixHandInPlace();
+        }
+        WebXRCamera._TmpMatrix.getTranslationToRef(this.position);
+        WebXRCamera._TmpMatrix.getRotationMatrixToRef(WebXRCamera._TmpMatrix);
+        Quaternion.FromRotationMatrixToRef(WebXRCamera._TmpMatrix, this.rotationQuaternion);
+        this.computeWorldMatrix();
 
-            // Update the parent cameras matrix
-            Matrix.FromFloat32ArrayToRefScaled(pose.poseModelMatrix, 0, 1, WebXRCamera._TmpMatrix);
+        // Update camera rigs
+        this._updateNumberOfRigCameras(xrSessionManager._currentXRFrame.views.length);
+        xrSessionManager._currentXRFrame.views.forEach((view, i) => {
+            // Update view/projection matrix
+            Matrix.FromFloat32ArrayToRefScaled(pose.getViewMatrix(view), 0, 1, this.rigCameras[i]._computedViewMatrix);
+            Matrix.FromFloat32ArrayToRefScaled(view.projectionMatrix, 0, 1, this.rigCameras[i]._projectionMatrix);
             if (!this._scene.useRightHandedSystem) {
-                WebXRCamera._TmpMatrix.toggleModelMatrixHandInPlace();
+                this.rigCameras[i]._computedViewMatrix.toggleModelMatrixHandInPlace();
+                this.rigCameras[i]._projectionMatrix.toggleProjectionMatrixHandInPlace();
             }
-            WebXRCamera._TmpMatrix.getTranslationToRef(this.position);
-            WebXRCamera._TmpMatrix.getRotationMatrixToRef(WebXRCamera._TmpMatrix);
-            Quaternion.FromRotationMatrixToRef(WebXRCamera._TmpMatrix, this.rotationQuaternion);
-            this.computeWorldMatrix();
-
-            // Update camera rigs
-            this._updateNumberOfRigCameras(xrSessionManager._currentXRFrame.views.length);
-            xrSessionManager._currentXRFrame.views.forEach((view, i) => {
-                // Update view/projection matrix
-                Matrix.FromFloat32ArrayToRefScaled(pose.getViewMatrix(view), 0, 1, this.rigCameras[i]._computedViewMatrix);
-                Matrix.FromFloat32ArrayToRefScaled(view.projectionMatrix, 0, 1, this.rigCameras[i]._projectionMatrix);
-                if (!this._scene.useRightHandedSystem) {
-                    this.rigCameras[i]._computedViewMatrix.toggleModelMatrixHandInPlace();
-                    this.rigCameras[i]._projectionMatrix.toggleProjectionMatrixHandInPlace();
-                }
 
-                // Update viewport
-                var viewport  = xrSessionManager._xrSession.baseLayer.getViewport(view);
-                var width = xrSessionManager._xrSession.baseLayer.framebufferWidth;
-                var height = xrSessionManager._xrSession.baseLayer.framebufferHeight;
-                this.rigCameras[i].viewport.width = viewport.width / width;
-                this.rigCameras[i].viewport.height = viewport.height / height;
-                this.rigCameras[i].viewport.x = viewport.x / width;
-                this.rigCameras[i].viewport.y = viewport.y / height;
+            // Update viewport
+            var viewport = xrSessionManager._xrSession.baseLayer.getViewport(view);
+            var width = xrSessionManager._xrSession.baseLayer.framebufferWidth;
+            var height = xrSessionManager._xrSession.baseLayer.framebufferHeight;
+            this.rigCameras[i].viewport.width = viewport.width / width;
+            this.rigCameras[i].viewport.height = viewport.height / height;
+            this.rigCameras[i].viewport.x = viewport.x / width;
+            this.rigCameras[i].viewport.y = viewport.y / height;
 
-                // Set cameras to render to the session's render target
-                this.rigCameras[i].outputRenderTarget = xrSessionManager._sessionRenderTargetTexture;
-            });
-            return true;
-        }
+            // Set cameras to render to the session's render target
+            this.rigCameras[i].outputRenderTarget = xrSessionManager._sessionRenderTargetTexture;
+        });
+        return true;
     }
+}

+ 123 - 123
src/Cameras/XR/webXREnterExitUI.ts

@@ -2,147 +2,147 @@ import { Nullable } from "../../types";
 import { Observable } from "../../Misc/observable";
 import { IDisposable, Scene } from "../../scene";
 import { WebXRExperienceHelper, WebXRState } from "./webXRExperienceHelper";
+/**
+ * Button which can be used to enter a different mode of XR
+ */
+export class WebXREnterExitUIButton {
     /**
-     * Button which can be used to enter a different mode of XR
+     * Creates a WebXREnterExitUIButton
+     * @param element button element
+     * @param initializationOptions XR initialization options for the button
      */
-    export class WebXREnterExitUIButton {
-        /**
-         * Creates a WebXREnterExitUIButton
-         * @param element button element
-         * @param initializationOptions XR initialization options for the button
-         */
-        constructor(
-            /** button element */
-            public element: HTMLElement,
-            /** XR initialization options for the button */
-            public initializationOptions: XRSessionCreationOptions
-        ) {}
-        /**
-         * Overwritable function which can be used to update the button's visuals when the state changes
-         * @param activeButton the current active button in the UI
-         */
-        update(activeButton: Nullable<WebXREnterExitUIButton>) {
-        }
+    constructor(
+        /** button element */
+        public element: HTMLElement,
+        /** XR initialization options for the button */
+        public initializationOptions: XRSessionCreationOptions
+    ) { }
+    /**
+     * Overwritable function which can be used to update the button's visuals when the state changes
+     * @param activeButton the current active button in the UI
+     */
+    update(activeButton: Nullable<WebXREnterExitUIButton>) {
     }
+}
 
+/**
+ * Options to create the webXR UI
+ */
+export class WebXREnterExitUIOptions {
     /**
-     * Options to create the webXR UI
+     * Context to enter xr with
      */
-    export class WebXREnterExitUIOptions {
-        /**
-         * Context to enter xr with
-         */
-        outputCanvasContext?: Nullable<WebGLRenderingContext>;
+    outputCanvasContext?: Nullable<WebGLRenderingContext>;
 
-        /**
-         * User provided buttons to enable/disable WebXR. The system will provide default if not set
-         */
-        customButtons?: Array<WebXREnterExitUIButton>;
-    }
     /**
-     * UI to allow the user to enter/exit XR mode
+     * User provided buttons to enable/disable WebXR. The system will provide default if not set
      */
-    export class WebXREnterExitUI implements IDisposable {
-        private _overlay: HTMLDivElement;
-        private _buttons: Array<WebXREnterExitUIButton> = [];
-        private _activeButton: Nullable<WebXREnterExitUIButton> = null;
-        /**
-         * Fired every time the active button is changed.
-         *
-         * When xr is entered via a button that launches xr that button will be the callback parameter
-         *
-         * When exiting xr the callback parameter will be null)
-         */
-        public activeButtonChangedObservable = new Observable<Nullable<WebXREnterExitUIButton>>();
-        /**
-         * Creates UI to allow the user to enter/exit XR mode
-         * @param scene the scene to add the ui to
-         * @param helper the xr experience helper to enter/exit xr with
-         * @param options options to configure the UI
-         * @returns the created ui
-         */
-        public static CreateAsync(scene: Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions): Promise<WebXREnterExitUI> {
-            var ui = new WebXREnterExitUI(scene, options);
-            var supportedPromises = ui._buttons.map((btn) => {
-                return helper.supportsSessionAsync(btn.initializationOptions);
-            });
-            helper.onStateChangedObservable.add((state) => {
-                if (state == WebXRState.NOT_IN_XR) {
-                    ui._updateButtons(null);
+    customButtons?: Array<WebXREnterExitUIButton>;
+}
+/**
+ * UI to allow the user to enter/exit XR mode
+ */
+export class WebXREnterExitUI implements IDisposable {
+    private _overlay: HTMLDivElement;
+    private _buttons: Array<WebXREnterExitUIButton> = [];
+    private _activeButton: Nullable<WebXREnterExitUIButton> = null;
+    /**
+     * Fired every time the active button is changed.
+     *
+     * When xr is entered via a button that launches xr that button will be the callback parameter
+     *
+     * When exiting xr the callback parameter will be null)
+     */
+    public activeButtonChangedObservable = new Observable<Nullable<WebXREnterExitUIButton>>();
+    /**
+     * Creates UI to allow the user to enter/exit XR mode
+     * @param scene the scene to add the ui to
+     * @param helper the xr experience helper to enter/exit xr with
+     * @param options options to configure the UI
+     * @returns the created ui
+     */
+    public static CreateAsync(scene: Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions): Promise<WebXREnterExitUI> {
+        var ui = new WebXREnterExitUI(scene, options);
+        var supportedPromises = ui._buttons.map((btn) => {
+            return helper.supportsSessionAsync(btn.initializationOptions);
+        });
+        helper.onStateChangedObservable.add((state) => {
+            if (state == WebXRState.NOT_IN_XR) {
+                ui._updateButtons(null);
+            }
+        });
+        return Promise.all(supportedPromises).then((results) => {
+            results.forEach((supported, i) => {
+                if (supported) {
+                    ui._overlay.appendChild(ui._buttons[i].element);
+                    ui._buttons[i].element.onclick = async() => {
+                        if (helper.state == WebXRState.IN_XR) {
+                            ui._updateButtons(null);
+                            await helper.exitXRAsync();
+                            return;
+                        } else if (helper.state == WebXRState.NOT_IN_XR) {
+                            ui._updateButtons(ui._buttons[i]);
+                            await helper.enterXRAsync(ui._buttons[i].initializationOptions, "eye-level");
+                        }
+                    };
                 }
             });
-            return Promise.all(supportedPromises).then((results) => {
-                results.forEach((supported, i) => {
-                    if (supported) {
-                        ui._overlay.appendChild(ui._buttons[i].element);
-                        ui._buttons[i].element.onclick = async() => {
-                            if (helper.state == WebXRState.IN_XR) {
-                                ui._updateButtons(null);
-                                await helper.exitXRAsync();
-                                return;
-                            } else if (helper.state == WebXRState.NOT_IN_XR) {
-                                ui._updateButtons(ui._buttons[i]);
-                                await helper.enterXRAsync(ui._buttons[i].initializationOptions, "eye-level");
-                            }
-                        };
-                    }
-                });
-                return ui;
-            });
-        }
-
-        private constructor(private scene: Scene, options: WebXREnterExitUIOptions) {
-            this._overlay = document.createElement("div");
-            this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
+            return ui;
+        });
+    }
 
-            if (options.customButtons) {
-                this._buttons = options.customButtons;
-            } else {
-                var hmdBtn = document.createElement("button");
-                hmdBtn.style.cssText = "color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-repeat:no-repeat; background-position: center; outline: none;";
-                hmdBtn.innerText = "HMD";
-                this._buttons.push(new WebXREnterExitUIButton(hmdBtn, {immersive: true, outputContext: options.outputCanvasContext}));
-                this._buttons[this._buttons.length - 1].update = function(activeButton: WebXREnterExitUIButton) {
-                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
-                    this.element.innerText = activeButton === this ? "EXIT" : "HMD";
-                };
+    private constructor(private scene: Scene, options: WebXREnterExitUIOptions) {
+        this._overlay = document.createElement("div");
+        this._overlay.style.cssText = "z-index:11;position: absolute; right: 20px;bottom: 50px;";
 
-                var windowBtn = document.createElement("button");
-                windowBtn.style.cssText = hmdBtn.style.cssText;
-                windowBtn.innerText = "Window";
-                this._buttons.push(new WebXREnterExitUIButton(windowBtn, { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext }));
-                this._buttons[this._buttons.length - 1].update = function(activeButton: WebXREnterExitUIButton) {
-                    this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
-                    this.element.innerText = activeButton === this ? "EXIT" : "Window";
-                };
-                this._updateButtons(null);
-            }
+        if (options.customButtons) {
+            this._buttons = options.customButtons;
+        } else {
+            var hmdBtn = document.createElement("button");
+            hmdBtn.style.cssText = "color: #868686; border-color: #868686; border-style: solid; margin-left: 10px; height: 50px; width: 80px; background-color: rgba(51,51,51,0.7); background-repeat:no-repeat; background-position: center; outline: none;";
+            hmdBtn.innerText = "HMD";
+            this._buttons.push(new WebXREnterExitUIButton(hmdBtn, { immersive: true, outputContext: options.outputCanvasContext }));
+            this._buttons[this._buttons.length - 1].update = function(activeButton: WebXREnterExitUIButton) {
+                this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                this.element.innerText = activeButton === this ? "EXIT" : "HMD";
+            };
 
-            var renderCanvas = scene.getEngine().getRenderingCanvas();
-            if (renderCanvas && renderCanvas.parentNode) {
-                renderCanvas.parentNode.appendChild(this._overlay);
-                scene.onDisposeObservable.addOnce(() => {
-                    this.dispose();
-                });
-            }
+            var windowBtn = document.createElement("button");
+            windowBtn.style.cssText = hmdBtn.style.cssText;
+            windowBtn.innerText = "Window";
+            this._buttons.push(new WebXREnterExitUIButton(windowBtn, { immersive: false, environmentIntegration: true, outputContext: options.outputCanvasContext }));
+            this._buttons[this._buttons.length - 1].update = function(activeButton: WebXREnterExitUIButton) {
+                this.element.style.display = (activeButton === null || activeButton === this) ? "" : "none";
+                this.element.innerText = activeButton === this ? "EXIT" : "Window";
+            };
+            this._updateButtons(null);
         }
 
-        private _updateButtons(activeButton: Nullable<WebXREnterExitUIButton>) {
-            this._activeButton = activeButton;
-            this._buttons.forEach((b) => {
-                b.update(this._activeButton);
+        var renderCanvas = scene.getEngine().getRenderingCanvas();
+        if (renderCanvas && renderCanvas.parentNode) {
+            renderCanvas.parentNode.appendChild(this._overlay);
+            scene.onDisposeObservable.addOnce(() => {
+                this.dispose();
             });
-            this.activeButtonChangedObservable.notifyObservers(this._activeButton);
         }
+    }
 
-        /**
-         * Disposes of the object
-         */
-        dispose() {
-            var renderCanvas = this.scene.getEngine().getRenderingCanvas();
-            if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
-                renderCanvas.parentNode.removeChild(this._overlay);
-            }
-            this.activeButtonChangedObservable.clear();
+    private _updateButtons(activeButton: Nullable<WebXREnterExitUIButton>) {
+        this._activeButton = activeButton;
+        this._buttons.forEach((b) => {
+            b.update(this._activeButton);
+        });
+        this.activeButtonChangedObservable.notifyObservers(this._activeButton);
+    }
+
+    /**
+     * Disposes of the object
+     */
+    dispose() {
+        var renderCanvas = this.scene.getEngine().getRenderingCanvas();
+        if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
+            renderCanvas.parentNode.removeChild(this._overlay);
         }
+        this.activeButtonChangedObservable.clear();
     }
+}

+ 170 - 170
src/Cameras/XR/webXRExperienceHelper.ts

@@ -7,194 +7,194 @@ import { Ray } from "../../Culling/ray";
 import { Camera } from "../../Cameras/camera";
 import { WebXRSessionManager } from "./webXRSessionManager";
 import { WebXRCamera } from "./webXRCamera";
+/**
+ * States of the webXR experience
+ */
+export enum WebXRState {
     /**
-     * States of the webXR experience
-     */
-    export enum WebXRState {
-        /**
-         * Transitioning to being in XR mode
-         */
-        ENTERING_XR,
-        /**
-         * Transitioning to non XR mode
-         */
-        EXITING_XR,
-        /**
-         * In XR mode and presenting
-         */
-        IN_XR,
-        /**
-         * Not entered XR mode
-         */
-        NOT_IN_XR
+     * Transitioning to being in XR mode
+     */
+    ENTERING_XR,
+    /**
+     * Transitioning to non XR mode
+     */
+    EXITING_XR,
+    /**
+     * In XR mode and presenting
+     */
+    IN_XR,
+    /**
+     * Not entered XR mode
+     */
+    NOT_IN_XR
+}
+/**
+ * Helper class used to enable XR
+ * @see https://doc.babylonjs.com/how_to/webxr
+ */
+export class WebXRExperienceHelper implements IDisposable {
+    /**
+     * Container which stores the xr camera and controllers as children. This can be used to move the camera/user as the camera's position is updated by the xr device
+     */
+    public container: AbstractMesh;
+    /**
+     * Camera used to render xr content
+     */
+    public camera: WebXRCamera;
+
+    /**
+     * The current state of the XR experience (eg. transitioning, in XR or not in XR)
+     */
+    public state: WebXRState = WebXRState.NOT_IN_XR;
+
+    private _setState(val: WebXRState) {
+        this.state = val;
+        this.onStateChangedObservable.notifyObservers(this.state);
     }
+
+    private static _TmpVector = new Vector3();
+
     /**
-     * Helper class used to enable XR
-     * @see https://doc.babylonjs.com/how_to/webxr
-     */
-    export class WebXRExperienceHelper implements IDisposable {
-        /**
-         * Container which stores the xr camera and controllers as children. This can be used to move the camera/user as the camera's position is updated by the xr device
-         */
-        public container: AbstractMesh;
-        /**
-         * Camera used to render xr content
-         */
-        public camera: WebXRCamera;
-
-        /**
-         * The current state of the XR experience (eg. transitioning, in XR or not in XR)
-         */
-        public state: WebXRState = WebXRState.NOT_IN_XR;
-
-        private _setState(val: WebXRState) {
-            this.state = val;
-            this.onStateChangedObservable.notifyObservers(this.state);
-        }
+     * Fires when the state of the experience helper has changed
+     */
+    public onStateChangedObservable = new Observable<WebXRState>();
 
-        private static _TmpVector = new Vector3();
-
-        /**
-         * Fires when the state of the experience helper has changed
-         */
-        public onStateChangedObservable = new Observable<WebXRState>();
-
-        /** @hidden */
-        public _sessionManager: WebXRSessionManager;
-
-        private _nonVRCamera: Nullable<Camera> = null;
-        private _originalSceneAutoClear = true;
-
-        private _supported = false;
-
-        /**
-         * Creates the experience helper
-         * @param scene the scene to attach the experience helper to
-         * @returns a promise for the experience helper
-         */
-        public static CreateAsync(scene: Scene): Promise<WebXRExperienceHelper> {
-            var helper = new WebXRExperienceHelper(scene);
-            return helper._sessionManager.initializeAsync().then(() => {
-                helper._supported = true;
-                return helper;
-            }).catch(() => {
-                return helper;
-            });
-        }
+    /** @hidden */
+    public _sessionManager: WebXRSessionManager;
 
-        /**
-         * Creates a WebXRExperienceHelper
-         * @param scene The scene the helper should be created in
-         */
-        private constructor(private scene: Scene) {
-            this.camera = new WebXRCamera("", scene);
-            this._sessionManager = new WebXRSessionManager(scene);
-            this.container = new AbstractMesh("", scene);
-            this.camera.parent = this.container;
-        }
+    private _nonVRCamera: Nullable<Camera> = null;
+    private _originalSceneAutoClear = true;
+
+    private _supported = false;
+
+    /**
+     * Creates the experience helper
+     * @param scene the scene to attach the experience helper to
+     * @returns a promise for the experience helper
+     */
+    public static CreateAsync(scene: Scene): Promise<WebXRExperienceHelper> {
+        var helper = new WebXRExperienceHelper(scene);
+        return helper._sessionManager.initializeAsync().then(() => {
+            helper._supported = true;
+            return helper;
+        }).catch(() => {
+            return helper;
+        });
+    }
+
+    /**
+     * Creates a WebXRExperienceHelper
+     * @param scene The scene the helper should be created in
+     */
+    private constructor(private scene: Scene) {
+        this.camera = new WebXRCamera("", scene);
+        this._sessionManager = new WebXRSessionManager(scene);
+        this.container = new AbstractMesh("", scene);
+        this.camera.parent = this.container;
+    }
+
+    /**
+     * Exits XR mode and returns the scene to its original state
+     * @returns promise that resolves after xr mode has exited
+     */
+    public exitXRAsync() {
+        this._setState(WebXRState.EXITING_XR);
+        return this._sessionManager.exitXRAsync();
+    }
 
-        /**
-         * Exits XR mode and returns the scene to its original state
-         * @returns promise that resolves after xr mode has exited
-         */
-        public exitXRAsync() {
-            this._setState(WebXRState.EXITING_XR);
-            return this._sessionManager.exitXRAsync();
+    /**
+     * Enters XR mode (This must be done within a user interaction in most browsers eg. button click)
+     * @param sessionCreationOptions options for the XR session
+     * @param frameOfReference frame of reference of the XR session
+     * @returns promise that resolves after xr mode has entered
+     */
+    public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
+        if (!this._supported) {
+            throw "XR session not supported by this browser";
         }
+        this._setState(WebXRState.ENTERING_XR);
 
-        /**
-         * Enters XR mode (This must be done within a user interaction in most browsers eg. button click)
-         * @param sessionCreationOptions options for the XR session
-         * @param frameOfReference frame of reference of the XR session
-         * @returns promise that resolves after xr mode has entered
-         */
-        public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
-            if (!this._supported) {
-                throw "XR session not supported by this browser";
-            }
-            this._setState(WebXRState.ENTERING_XR);
-
-            return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(() => {
-                // Cache pre xr scene settings
-                this._originalSceneAutoClear = this.scene.autoClear;
-                this._nonVRCamera = this.scene.activeCamera;
-
-                // Overwrite current scene settings
-                this.scene.autoClear = false;
-                this.scene.activeCamera = this.camera;
-
-                this._sessionManager.onXRFrameObservable.add(() => {
-                    this.camera.updateFromXRSessionManager(this._sessionManager);
-                });
+        return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(() => {
+            // Cache pre xr scene settings
+            this._originalSceneAutoClear = this.scene.autoClear;
+            this._nonVRCamera = this.scene.activeCamera;
 
-                this._sessionManager.onXRSessionEnded.addOnce(() => {
-                    // Reset camera rigs output render target to ensure sessions render target is not drawn after it ends
-                    this.camera.rigCameras.forEach((c) => {
-                        c.outputRenderTarget = null;
-                    });
+            // Overwrite current scene settings
+            this.scene.autoClear = false;
+            this.scene.activeCamera = this.camera;
 
-                    // Restore scene settings
-                    this.scene.autoClear = this._originalSceneAutoClear;
-                    this.scene.activeCamera = this._nonVRCamera;
-                    this._sessionManager.onXRFrameObservable.clear();
+            this._sessionManager.onXRFrameObservable.add(() => {
+                this.camera.updateFromXRSessionManager(this._sessionManager);
+            });
 
-                    this._setState(WebXRState.NOT_IN_XR);
+            this._sessionManager.onXRSessionEnded.addOnce(() => {
+                // Reset camera rigs output render target to ensure sessions render target is not drawn after it ends
+                this.camera.rigCameras.forEach((c) => {
+                    c.outputRenderTarget = null;
                 });
-                this._setState(WebXRState.IN_XR);
+
+                // Restore scene settings
+                this.scene.autoClear = this._originalSceneAutoClear;
+                this.scene.activeCamera = this._nonVRCamera;
+                this._sessionManager.onXRFrameObservable.clear();
+
+                this._setState(WebXRState.NOT_IN_XR);
             });
-        }
+            this._setState(WebXRState.IN_XR);
+        });
+    }
 
-        /**
-         * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
-         * @param ray ray to cast into the environment
-         * @returns Promise which resolves with a collision point in the environment if it exists
-         */
-        public environmentPointHitTestAsync(ray: Ray): Promise<Nullable<Vector3>> {
-            return this._sessionManager.environmentPointHitTestAsync(ray);
-        }
+    /**
+     * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
+     * @param ray ray to cast into the environment
+     * @returns Promise which resolves with a collision point in the environment if it exists
+     */
+    public environmentPointHitTestAsync(ray: Ray): Promise<Nullable<Vector3>> {
+        return this._sessionManager.environmentPointHitTestAsync(ray);
+    }
 
-        /**
-         * Updates the global position of the camera by moving the camera's container
-         * This should be used instead of modifying the camera's position as it will be overwritten by an xrSessions's update frame
-         * @param position The desired global position of the camera
-         */
-        public setPositionOfCameraUsingContainer(position: Vector3) {
-            this.camera.globalPosition.subtractToRef(position, WebXRExperienceHelper._TmpVector);
-            this.container.position.subtractInPlace(WebXRExperienceHelper._TmpVector);
-        }
+    /**
+     * Updates the global position of the camera by moving the camera's container
+     * This should be used instead of modifying the camera's position as it will be overwritten by an xrSessions's update frame
+     * @param position The desired global position of the camera
+     */
+    public setPositionOfCameraUsingContainer(position: Vector3) {
+        this.camera.globalPosition.subtractToRef(position, WebXRExperienceHelper._TmpVector);
+        this.container.position.subtractInPlace(WebXRExperienceHelper._TmpVector);
+    }
 
-        /**
-         * Rotates the xr camera by rotating the camera's container around the camera's position
-         * This should be used instead of modifying the camera's rotation as it will be overwritten by an xrSessions's update frame
-         * @param rotation the desired quaternion rotation to apply to the camera
-         */
-        public rotateCameraByQuaternionUsingContainer(rotation: Quaternion) {
-            if (!this.container.rotationQuaternion) {
-                this.container.rotationQuaternion = Quaternion.FromEulerVector(this.container.rotation);
-            }
-            this.container.rotationQuaternion.multiplyInPlace(rotation);
-            this.container.position.rotateByQuaternionAroundPointToRef(rotation, this.camera.globalPosition, this.container.position);
+    /**
+     * Rotates the xr camera by rotating the camera's container around the camera's position
+     * This should be used instead of modifying the camera's rotation as it will be overwritten by an xrSessions's update frame
+     * @param rotation the desired quaternion rotation to apply to the camera
+     */
+    public rotateCameraByQuaternionUsingContainer(rotation: Quaternion) {
+        if (!this.container.rotationQuaternion) {
+            this.container.rotationQuaternion = Quaternion.FromEulerVector(this.container.rotation);
         }
+        this.container.rotationQuaternion.multiplyInPlace(rotation);
+        this.container.position.rotateByQuaternionAroundPointToRef(rotation, this.camera.globalPosition, this.container.position);
+    }
 
-        /**
-         * Checks if the creation options are supported by the xr session
-         * @param options creation options
-         * @returns true if supported
-         */
-        public supportsSessionAsync(options: XRSessionCreationOptions) {
-            if (!this._supported) {
-                return Promise.resolve(false);
-            }
-            return this._sessionManager.supportsSessionAsync(options);
+    /**
+     * Checks if the creation options are supported by the xr session
+     * @param options creation options
+     * @returns true if supported
+     */
+    public supportsSessionAsync(options: XRSessionCreationOptions) {
+        if (!this._supported) {
+            return Promise.resolve(false);
         }
+        return this._sessionManager.supportsSessionAsync(options);
+    }
 
-        /**
-         * Disposes of the experience helper
-         */
-        public dispose() {
-            this.camera.dispose();
-            this.container.dispose();
-            this.onStateChangedObservable.clear();
-            this._sessionManager.dispose();
-        }
+    /**
+     * Disposes of the experience helper
+     */
+    public dispose() {
+        this.camera.dispose();
+        this.container.dispose();
+        this.onStateChangedObservable.clear();
+        this._sessionManager.dispose();
     }
+}

+ 83 - 83
src/Cameras/XR/webXRInput.ts

@@ -4,105 +4,105 @@ import { Matrix, Quaternion } from "../../Maths/math";
 import { IDisposable, Scene } from "../../scene";
 import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { WebXRExperienceHelper } from "./webXRExperienceHelper";
+/**
+ * Represents an XR input
+ */
+export class WebXRController {
     /**
-     * Represents an XR input
+     * Represents the part of the controller that is held. This may not exist if the controller is the head mounted display itself, if thats the case only the pointer from the head will be availible
      */
-    export class WebXRController {
-        /**
-         * Represents the part of the controller that is held. This may not exist if the controller is the head mounted display itself, if thats the case only the pointer from the head will be availible
-         */
-        public grip?: AbstractMesh;
-        /**
-         * Pointer which can be used to select objects or attach a visible laser to
-         */
-        public pointer: AbstractMesh;
+    public grip?: AbstractMesh;
+    /**
+     * Pointer which can be used to select objects or attach a visible laser to
+     */
+    public pointer: AbstractMesh;
 
-        /**
-         * Creates the controller
-         * @see https://doc.babylonjs.com/how_to/webxr
-         * @param scene the scene which the controller should be associated to
-         */
-        constructor(scene: Scene) {
-            this.pointer = new AbstractMesh("controllerPointer", scene);
-        }
-        /**
-         * Disposes of the object
-         */
-        dispose() {
-            if (this.grip) {
-                this.grip.dispose();
-            }
-            this.pointer.dispose();
+    /**
+     * Creates the controller
+     * @see https://doc.babylonjs.com/how_to/webxr
+     * @param scene the scene which the controller should be associated to
+     */
+    constructor(scene: Scene) {
+        this.pointer = new AbstractMesh("controllerPointer", scene);
+    }
+    /**
+     * Disposes of the object
+     */
+    dispose() {
+        if (this.grip) {
+            this.grip.dispose();
         }
+        this.pointer.dispose();
     }
+}
 
+/**
+ * XR input used to track XR inputs such as controllers/rays
+ */
+export class WebXRInput implements IDisposable {
     /**
-     * XR input used to track XR inputs such as controllers/rays
+     * XR controllers being tracked
      */
-    export class WebXRInput implements IDisposable {
-        /**
-         * XR controllers being tracked
-         */
-        public controllers: Array<WebXRController> = [];
-        private _tmpMatrix = new Matrix();
-        private _frameObserver: Nullable<Observer<any>>;
+    public controllers: Array<WebXRController> = [];
+    private _tmpMatrix = new Matrix();
+    private _frameObserver: Nullable<Observer<any>>;
 
-        /**
-         * Initializes the WebXRInput
-         * @param helper experience helper which the input should be created for
-         */
-        public constructor(private helper: WebXRExperienceHelper) {
-            this._frameObserver = helper._sessionManager.onXRFrameObservable.add(() => {
-                if (!helper._sessionManager._currentXRFrame || !helper._sessionManager._currentXRFrame.getDevicePose) {
-                    return;
-                }
+    /**
+     * Initializes the WebXRInput
+     * @param helper experience helper which the input should be created for
+     */
+    public constructor(private helper: WebXRExperienceHelper) {
+        this._frameObserver = helper._sessionManager.onXRFrameObservable.add(() => {
+            if (!helper._sessionManager._currentXRFrame || !helper._sessionManager._currentXRFrame.getDevicePose) {
+                return;
+            }
 
-                var xrFrame = helper._sessionManager._currentXRFrame;
-                var inputSources = helper._sessionManager._xrSession.getInputSources();
+            var xrFrame = helper._sessionManager._currentXRFrame;
+            var inputSources = helper._sessionManager._xrSession.getInputSources();
 
-                inputSources.forEach((input, i) => {
-                    let inputPose = xrFrame.getInputPose(input, helper._sessionManager._frameOfReference);
-                    if (inputPose) {
-                        if (this.controllers.length <= i) {
-                            this.controllers.push(new WebXRController(helper.container.getScene()));
-                        }
-                        var controller = this.controllers[i];
+            inputSources.forEach((input, i) => {
+                let inputPose = xrFrame.getInputPose(input, helper._sessionManager._frameOfReference);
+                if (inputPose) {
+                    if (this.controllers.length <= i) {
+                        this.controllers.push(new WebXRController(helper.container.getScene()));
+                    }
+                    var controller = this.controllers[i];
 
-                        // Manage the grip if it exists
-                        if (inputPose.gripMatrix) {
-                            if (!controller.grip) {
-                                controller.grip = new AbstractMesh("controllerGrip", helper.container.getScene());
-                            }
-                            Matrix.FromFloat32ArrayToRefScaled(inputPose.gripMatrix, 0, 1, this._tmpMatrix);
-                            if (!controller.grip.getScene().useRightHandedSystem) {
-                                this._tmpMatrix.toggleModelMatrixHandInPlace();
-                            }
-                            if (!controller.grip.rotationQuaternion) {
-                                controller.grip.rotationQuaternion = new Quaternion();
-                            }
-                            this._tmpMatrix.decompose(controller.grip.scaling, controller.grip.rotationQuaternion, controller.grip.position);
+                    // Manage the grip if it exists
+                    if (inputPose.gripMatrix) {
+                        if (!controller.grip) {
+                            controller.grip = new AbstractMesh("controllerGrip", helper.container.getScene());
                         }
-
-                        // Manager pointer of controller
-                        Matrix.FromFloat32ArrayToRefScaled(inputPose.targetRay.transformMatrix, 0, 1, this._tmpMatrix);
-                        if (!controller.pointer.getScene().useRightHandedSystem) {
+                        Matrix.FromFloat32ArrayToRefScaled(inputPose.gripMatrix, 0, 1, this._tmpMatrix);
+                        if (!controller.grip.getScene().useRightHandedSystem) {
                             this._tmpMatrix.toggleModelMatrixHandInPlace();
                         }
-                        if (!controller.pointer.rotationQuaternion) {
-                            controller.pointer.rotationQuaternion = new Quaternion();
+                        if (!controller.grip.rotationQuaternion) {
+                            controller.grip.rotationQuaternion = new Quaternion();
                         }
-                        this._tmpMatrix.decompose(controller.pointer.scaling, controller.pointer.rotationQuaternion, controller.pointer.position);
+                        this._tmpMatrix.decompose(controller.grip.scaling, controller.grip.rotationQuaternion, controller.grip.position);
                     }
-                });
-            });
-        }
-        /**
-         * Disposes of the object
-         */
-        public dispose() {
-            this.controllers.forEach((c) => {
-                c.dispose();
+
+                    // Manager pointer of controller
+                    Matrix.FromFloat32ArrayToRefScaled(inputPose.targetRay.transformMatrix, 0, 1, this._tmpMatrix);
+                    if (!controller.pointer.getScene().useRightHandedSystem) {
+                        this._tmpMatrix.toggleModelMatrixHandInPlace();
+                    }
+                    if (!controller.pointer.rotationQuaternion) {
+                        controller.pointer.rotationQuaternion = new Quaternion();
+                    }
+                    this._tmpMatrix.decompose(controller.pointer.scaling, controller.pointer.rotationQuaternion, controller.pointer.position);
+                }
             });
-            this.helper._sessionManager.onXRFrameObservable.remove(this._frameObserver);
-        }
+        });
+    }
+    /**
+     * Disposes of the object
+     */
+    public dispose() {
+        this.controllers.forEach((c) => {
+            c.dispose();
+        });
+        this.helper._sessionManager.onXRFrameObservable.remove(this._frameObserver);
     }
+}

+ 50 - 50
src/Cameras/XR/webXRManagedOutputCanvas.ts

@@ -1,63 +1,63 @@
 import { Nullable } from "../../types";
 import { IDisposable } from "../../scene";
 import { WebXRExperienceHelper, WebXRState } from "./webXRExperienceHelper";
+/**
+ * Creates a canvas that is added/removed from the webpage when entering/exiting XR
+ */
+export class WebXRManagedOutputCanvas implements IDisposable {
+    private _canvas: Nullable<HTMLCanvasElement> = null;
     /**
-     * Creates a canvas that is added/removed from the webpage when entering/exiting XR
+     * xrpresent context of the canvas which can be used to display/mirror xr content
      */
-    export class WebXRManagedOutputCanvas implements IDisposable {
-        private _canvas: Nullable<HTMLCanvasElement> = null;
-        /**
-         * xrpresent context of the canvas which can be used to display/mirror xr content
-         */
-        public canvasContext: Nullable<WebGLRenderingContext> = null;
-        /**
-         * Initializes the canvas to be added/removed upon entering/exiting xr
-         * @param helper the xr experience helper used to trigger adding/removing of the canvas
-         * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
-         */
-        public constructor(helper: WebXRExperienceHelper, canvas?: HTMLCanvasElement) {
-            if (!canvas) {
-                canvas = document.createElement('canvas');
-                canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #000000;";
-            }
-            this._setManagedOutputCanvas(canvas);
-            helper.onStateChangedObservable.add((stateInfo) => {
-                if (stateInfo == WebXRState.ENTERING_XR) {
-                    // The canvas is added to the screen before entering XR because currently the xr session must be initialized while the canvas is added render properly
-                    this._addCanvas();
-                }else if (helper.state == WebXRState.NOT_IN_XR) {
-                    this._removeCanvas();
-                }
-            });
-        }
-        /**
-         * Disposes of the object
-         */
-        public dispose() {
-            this._removeCanvas();
-            this._setManagedOutputCanvas(null);
+    public canvasContext: Nullable<WebGLRenderingContext> = null;
+    /**
+     * Initializes the canvas to be added/removed upon entering/exiting xr
+     * @param helper the xr experience helper used to trigger adding/removing of the canvas
+     * @param canvas The canvas to be added/removed (If not specified a full screen canvas will be created)
+     */
+    public constructor(helper: WebXRExperienceHelper, canvas?: HTMLCanvasElement) {
+        if (!canvas) {
+            canvas = document.createElement('canvas');
+            canvas.style.cssText = "position:absolute; bottom:0px;right:0px;z-index:10;width:100%;height:100%;background-color: #000000;";
         }
-
-        private _setManagedOutputCanvas(canvas: Nullable<HTMLCanvasElement>) {
-            this._removeCanvas();
-            if (!canvas) {
-                this._canvas = null;
-                this.canvasContext = null;
-            }else {
-                this._canvas = canvas;
-                this.canvasContext = <any>this._canvas.getContext('xrpresent');
+        this._setManagedOutputCanvas(canvas);
+        helper.onStateChangedObservable.add((stateInfo) => {
+            if (stateInfo == WebXRState.ENTERING_XR) {
+                // The canvas is added to the screen before entering XR because currently the xr session must be initialized while the canvas is added render properly
+                this._addCanvas();
+            } else if (helper.state == WebXRState.NOT_IN_XR) {
+                this._removeCanvas();
             }
+        });
+    }
+    /**
+     * Disposes of the object
+     */
+    public dispose() {
+        this._removeCanvas();
+        this._setManagedOutputCanvas(null);
+    }
+
+    private _setManagedOutputCanvas(canvas: Nullable<HTMLCanvasElement>) {
+        this._removeCanvas();
+        if (!canvas) {
+            this._canvas = null;
+            this.canvasContext = null;
+        } else {
+            this._canvas = canvas;
+            this.canvasContext = <any>this._canvas.getContext('xrpresent');
         }
+    }
 
-        private _addCanvas() {
-            if (this._canvas) {
-                document.body.appendChild(this._canvas);
-            }
+    private _addCanvas() {
+        if (this._canvas) {
+            document.body.appendChild(this._canvas);
         }
+    }
 
-        private _removeCanvas() {
-            if (this._canvas && document.body.contains(this._canvas)) {
-                document.body.removeChild(this._canvas);
-            }
+    private _removeCanvas() {
+        if (this._canvas && document.body.contains(this._canvas)) {
+            document.body.removeChild(this._canvas);
         }
     }
+}

+ 159 - 159
src/Cameras/XR/webXRSessionManager.ts

@@ -6,132 +6,132 @@ import { Vector3, Matrix } from "../../Maths/math";
 import { InternalTexture } from "../../Materials/Textures/internalTexture";
 import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
 import { Ray } from "../../Culling/ray";
+/**
+ * Manages an XRSession
+ * @see https://doc.babylonjs.com/how_to/webxr
+ */
+export class WebXRSessionManager implements IDisposable {
     /**
-     * Manages an XRSession
-     * @see https://doc.babylonjs.com/how_to/webxr
+     * Fires every time a new xrFrame arrives which can be used to update the camera
      */
-    export class WebXRSessionManager implements IDisposable {
-        /**
-         * Fires every time a new xrFrame arrives which can be used to update the camera
-         */
-        public onXRFrameObservable: Observable<any> = new Observable<any>();
-        /**
-         * Fires when the xr session is ended either by the device or manually done
-         */
-        public onXRSessionEnded: Observable<any> = new Observable<any>();
-
-        /** @hidden */
-        public _xrSession: XRSession;
-        /** @hidden */
-        public _frameOfReference: XRFrameOfReference;
-        /** @hidden */
-        public _sessionRenderTargetTexture: Nullable<RenderTargetTexture> = null;
-        /** @hidden */
-        public _currentXRFrame: Nullable<XRFrame>;
-        private _xrNavigator: any;
-        private _xrDevice: XRDevice;
-        private _tmpMatrix = new Matrix();
-
-        /**
-         * Constructs a WebXRSessionManager, this must be initialized within a user action before usage
-         * @param scene The scene which the session should be created for
-         */
-        constructor(private scene: Scene) {
+    public onXRFrameObservable: Observable<any> = new Observable<any>();
+    /**
+     * Fires when the xr session is ended either by the device or manually done
+     */
+    public onXRSessionEnded: Observable<any> = new Observable<any>();
+
+    /** @hidden */
+    public _xrSession: XRSession;
+    /** @hidden */
+    public _frameOfReference: XRFrameOfReference;
+    /** @hidden */
+    public _sessionRenderTargetTexture: Nullable<RenderTargetTexture> = null;
+    /** @hidden */
+    public _currentXRFrame: Nullable<XRFrame>;
+    private _xrNavigator: any;
+    private _xrDevice: XRDevice;
+    private _tmpMatrix = new Matrix();
 
-        }
+    /**
+     * Constructs a WebXRSessionManager, this must be initialized within a user action before usage
+     * @param scene The scene which the session should be created for
+     */
+    constructor(private scene: Scene) {
 
-        /**
-         * Initializes the manager
-         * After initialization enterXR can be called to start an XR session
-         * @returns Promise which resolves after it is initialized
-         */
-        public initializeAsync(): Promise<void> {
-            Logger.Warn("The WebXR APIs are still under development and are subject to change in the future.");
-            // Check if the browser supports webXR
-            this._xrNavigator = navigator;
-            if (!this._xrNavigator.xr) {
-                return Promise.reject("webXR not supported by this browser");
-            }
-             // Request the webXR device
-            return this._xrNavigator.xr.requestDevice().then((device: XRDevice) => {
-                this._xrDevice = device;
-                return (<any>this.scene.getEngine()._gl).setCompatibleXRDevice(this._xrDevice);
-            });
-        }
+    }
 
-        /**
-         * Enters XR with the desired XR session options, this must be done with a user action (eg. button click event)
-         * @param sessionCreationOptions xr options to create the session with
-         * @param frameOfReferenceType option to configure how the xr pose is expressed
-         * @returns Promise which resolves after it enters XR
-         */
-        public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: string): Promise<void> {
-            // initialize session
-            return this._xrDevice.requestSession(sessionCreationOptions).then((session: XRSession) => {
-                this._xrSession = session;
-
-                // handle when the session is ended (By calling session.end or device ends its own session eg. pressing home button on phone)
-                this._xrSession.addEventListener("end", () => {
-                    // Remove render target texture and notify frame obervers
-                    this._sessionRenderTargetTexture = null;
-
-                    // Restore frame buffer to avoid clear on xr framebuffer after session end
-                    this.scene.getEngine().restoreDefaultFramebuffer();
-
-                    // Need to restart render loop as after the session is ended the last request for new frame will never call callback
-                    this.scene.getEngine().customAnimationFrameRequester = null;
-                    this.onXRSessionEnded.notifyObservers(null);
-                    this.scene.getEngine()._renderLoop();
-                }, {once : true});
-
-                this._xrSession.baseLayer = new XRWebGLLayer(this._xrSession, this.scene.getEngine()._gl);
-                return this._xrSession.requestFrameOfReference(frameOfReferenceType);
-            }).then((frameOfRef: any) => {
-                this._frameOfReference = frameOfRef;
-                // Tell the engine's render loop to be driven by the xr session's refresh rate and provide xr pose information
-                this.scene.getEngine().customAnimationFrameRequester = {
-                    requestAnimationFrame: this._xrSession.requestAnimationFrame.bind(this._xrSession),
-                    renderFunction: (timestamp: number, xrFrame: Nullable<XRFrame>) => {
-                        // Store the XR frame in the manager to be consumed by the XR camera to update pose
-                        this._currentXRFrame = xrFrame;
-                        this.onXRFrameObservable.notifyObservers(null);
-                        this.scene.getEngine()._renderLoop();
-                    }
-                };
-                // Create render target texture from xr's webgl render target
-                this._sessionRenderTargetTexture = WebXRSessionManager._CreateRenderTargetTextureFromSession(this._xrSession, this.scene);
+    /**
+     * Initializes the manager
+     * After initialization enterXR can be called to start an XR session
+     * @returns Promise which resolves after it is initialized
+     */
+    public initializeAsync(): Promise<void> {
+        Logger.Warn("The WebXR APIs are still under development and are subject to change in the future.");
+        // Check if the browser supports webXR
+        this._xrNavigator = navigator;
+        if (!this._xrNavigator.xr) {
+            return Promise.reject("webXR not supported by this browser");
+        }
+        // Request the webXR device
+        return this._xrNavigator.xr.requestDevice().then((device: XRDevice) => {
+            this._xrDevice = device;
+            return (<any>this.scene.getEngine()._gl).setCompatibleXRDevice(this._xrDevice);
+        });
+    }
 
-                // Stop window's animation frame and trigger sessions animation frame
-                window.cancelAnimationFrame(this.scene.getEngine()._frameHandler);
+    /**
+     * Enters XR with the desired XR session options, this must be done with a user action (eg. button click event)
+     * @param sessionCreationOptions xr options to create the session with
+     * @param frameOfReferenceType option to configure how the xr pose is expressed
+     * @returns Promise which resolves after it enters XR
+     */
+    public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReferenceType: string): Promise<void> {
+        // initialize session
+        return this._xrDevice.requestSession(sessionCreationOptions).then((session: XRSession) => {
+            this._xrSession = session;
+
+            // handle when the session is ended (By calling session.end or device ends its own session eg. pressing home button on phone)
+            this._xrSession.addEventListener("end", () => {
+                // Remove render target texture and notify frame obervers
+                this._sessionRenderTargetTexture = null;
+
+                // Restore frame buffer to avoid clear on xr framebuffer after session end
+                this.scene.getEngine().restoreDefaultFramebuffer();
+
+                // Need to restart render loop as after the session is ended the last request for new frame will never call callback
+                this.scene.getEngine().customAnimationFrameRequester = null;
+                this.onXRSessionEnded.notifyObservers(null);
                 this.scene.getEngine()._renderLoop();
-            });
-        }
+            }, { once: true });
+
+            this._xrSession.baseLayer = new XRWebGLLayer(this._xrSession, this.scene.getEngine()._gl);
+            return this._xrSession.requestFrameOfReference(frameOfReferenceType);
+        }).then((frameOfRef: any) => {
+            this._frameOfReference = frameOfRef;
+            // Tell the engine's render loop to be driven by the xr session's refresh rate and provide xr pose information
+            this.scene.getEngine().customAnimationFrameRequester = {
+                requestAnimationFrame: this._xrSession.requestAnimationFrame.bind(this._xrSession),
+                renderFunction: (timestamp: number, xrFrame: Nullable<XRFrame>) => {
+                    // Store the XR frame in the manager to be consumed by the XR camera to update pose
+                    this._currentXRFrame = xrFrame;
+                    this.onXRFrameObservable.notifyObservers(null);
+                    this.scene.getEngine()._renderLoop();
+                }
+            };
+            // Create render target texture from xr's webgl render target
+            this._sessionRenderTargetTexture = WebXRSessionManager._CreateRenderTargetTextureFromSession(this._xrSession, this.scene);
+
+            // Stop window's animation frame and trigger sessions animation frame
+            window.cancelAnimationFrame(this.scene.getEngine()._frameHandler);
+            this.scene.getEngine()._renderLoop();
+        });
+    }
 
-        /**
-         * Stops the xrSession and restores the renderloop
-         * @returns Promise which resolves after it exits XR
-         */
-        public exitXRAsync() {
-            return this._xrSession.end();
-        }
+    /**
+     * Stops the xrSession and restores the renderloop
+     * @returns Promise which resolves after it exits XR
+     */
+    public exitXRAsync() {
+        return this._xrSession.end();
+    }
 
-        /**
-         * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
-         * @param ray ray to cast into the environment
-         * @returns Promise which resolves with a collision point in the environment if it exists
-         */
-        public environmentPointHitTestAsync(ray: Ray): Promise<Nullable<Vector3>> {
-            return new Promise((res) => {
-                // Compute left handed inputs to request hit test
-                var origin = new Float32Array([ray.origin.x, ray.origin.y, ray.origin.z]);
-                var direction = new Float32Array([ray.direction.x, ray.direction.y, ray.direction.z]);
-                if (!this.scene.useRightHandedSystem) {
-                    origin[2] *= -1;
-                    direction[2] *= -1;
-                }
+    /**
+     * Fires a ray and returns the closest hit in the xr sessions enviornment, useful to place objects in AR
+     * @param ray ray to cast into the environment
+     * @returns Promise which resolves with a collision point in the environment if it exists
+     */
+    public environmentPointHitTestAsync(ray: Ray): Promise<Nullable<Vector3>> {
+        return new Promise((res) => {
+            // Compute left handed inputs to request hit test
+            var origin = new Float32Array([ray.origin.x, ray.origin.y, ray.origin.z]);
+            var direction = new Float32Array([ray.direction.x, ray.direction.y, ray.direction.z]);
+            if (!this.scene.useRightHandedSystem) {
+                origin[2] *= -1;
+                direction[2] *= -1;
+            }
 
-                // Fire hittest
-                this._xrSession.requestHitTest(origin, direction, this._frameOfReference)
+            // Fire hittest
+            this._xrSession.requestHitTest(origin, direction, this._frameOfReference)
                 .then((hits: any) => {
                     if (hits.length > 0) {
                         Matrix.FromFloat32ArrayToRefScaled(hits[0].hitMatrix, 0, 1.0, this._tmpMatrix);
@@ -140,53 +140,53 @@ import { Ray } from "../../Culling/ray";
                             hitPoint.z *= -1;
                         }
                         res(hitPoint);
-                    }else {
+                    } else {
                         res(null);
                     }
                 }).catch(() => {
-                        res(null);
-                    });
-            });
-        }
-
-        /**
-         * Checks if a session would be supported for the creation options specified
-         * @param options creation options to check if they are supported
-         * @returns true if supported
-         */
-        public supportsSessionAsync(options: XRSessionCreationOptions) {
-            return this._xrDevice.supportsSession(options).then(() => {
-                return true;
-            }).catch(() => {
-                    return false;
+                    res(null);
                 });
-        }
+        });
+    }
 
-        /**
-         * @hidden
-         * Converts the render layer of xrSession to a render target
-         * @param session session to create render target for
-         * @param scene scene the new render target should be created for
-         */
-        public static _CreateRenderTargetTextureFromSession(session: XRSession, scene: Scene) {
-            // Create internal texture
-            var internalTexture = new InternalTexture(scene.getEngine(), InternalTexture.DATASOURCE_UNKNOWN, true);
-            internalTexture.width = session.baseLayer.framebufferWidth;
-            internalTexture.height = session.baseLayer.framebufferHeight;
-            internalTexture._framebuffer = session.baseLayer.framebuffer;
-
-             // Create render target texture from the internal texture
-            var renderTargetTexture = new RenderTargetTexture("XR renderTargetTexture", {width: internalTexture.width, height: internalTexture.height}, scene, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
-            renderTargetTexture._texture = internalTexture;
-
-             return renderTargetTexture;
-        }
+    /**
+     * Checks if a session would be supported for the creation options specified
+     * @param options creation options to check if they are supported
+     * @returns true if supported
+     */
+    public supportsSessionAsync(options: XRSessionCreationOptions) {
+        return this._xrDevice.supportsSession(options).then(() => {
+            return true;
+        }).catch(() => {
+            return false;
+        });
+    }
 
-        /**
-         * Disposes of the session manager
-         */
-        public dispose() {
-            this.onXRFrameObservable.clear();
-            this.onXRSessionEnded.clear();
-        }
+    /**
+     * @hidden
+     * Converts the render layer of xrSession to a render target
+     * @param session session to create render target for
+     * @param scene scene the new render target should be created for
+     */
+    public static _CreateRenderTargetTextureFromSession(session: XRSession, scene: Scene) {
+        // Create internal texture
+        var internalTexture = new InternalTexture(scene.getEngine(), InternalTexture.DATASOURCE_UNKNOWN, true);
+        internalTexture.width = session.baseLayer.framebufferWidth;
+        internalTexture.height = session.baseLayer.framebufferHeight;
+        internalTexture._framebuffer = session.baseLayer.framebuffer;
+
+        // Create render target texture from the internal texture
+        var renderTargetTexture = new RenderTargetTexture("XR renderTargetTexture", { width: internalTexture.width, height: internalTexture.height }, scene, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
+        renderTargetTexture._texture = internalTexture;
+
+        return renderTargetTexture;
+    }
+
+    /**
+     * Disposes of the session manager
+     */
+    public dispose() {
+        this.onXRFrameObservable.clear();
+        this.onXRSessionEnded.clear();
     }
+}

文件差異過大導致無法顯示
+ 913 - 913
src/Cameras/arcRotateCamera.ts


+ 43 - 43
src/Cameras/arcRotateCameraInputsManager.ts

@@ -5,53 +5,53 @@ import { ArcRotateCameraKeyboardMoveInput } from "../Cameras/Inputs/arcRotateCam
 import { ArcRotateCameraMouseWheelInput } from "../Cameras/Inputs/arcRotateCameraMouseWheelInput";
 import { CameraInputsManager } from "../Cameras/cameraInputsManager";
 
+/**
+ * Default Inputs manager for the ArcRotateCamera.
+ * It groups all the default supported inputs for ease of use.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class ArcRotateCameraInputsManager extends CameraInputsManager<ArcRotateCamera> {
     /**
-     * Default Inputs manager for the ArcRotateCamera.
-     * It groups all the default supported inputs for ease of use.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Instantiates a new ArcRotateCameraInputsManager.
+     * @param camera Defines the camera the inputs belong to
      */
-    export class ArcRotateCameraInputsManager extends CameraInputsManager<ArcRotateCamera> {
-        /**
-         * Instantiates a new ArcRotateCameraInputsManager.
-         * @param camera Defines the camera the inputs belong to
-         */
-        constructor(camera: ArcRotateCamera) {
-            super(camera);
-        }
+    constructor(camera: ArcRotateCamera) {
+        super(camera);
+    }
 
-        /**
-         * Add mouse wheel input support to the input manager.
-         * @returns the current input manager
-         */
-        public addMouseWheel(): ArcRotateCameraInputsManager {
-            this.add(new ArcRotateCameraMouseWheelInput());
-            return this;
-        }
+    /**
+     * Add mouse wheel input support to the input manager.
+     * @returns the current input manager
+     */
+    public addMouseWheel(): ArcRotateCameraInputsManager {
+        this.add(new ArcRotateCameraMouseWheelInput());
+        return this;
+    }
 
-        /**
-         * Add pointers input support to the input manager.
-         * @returns the current input manager
-         */
-        public addPointers(): ArcRotateCameraInputsManager {
-            this.add(new ArcRotateCameraPointersInput());
-            return this;
-        }
+    /**
+     * Add pointers input support to the input manager.
+     * @returns the current input manager
+     */
+    public addPointers(): ArcRotateCameraInputsManager {
+        this.add(new ArcRotateCameraPointersInput());
+        return this;
+    }
 
-        /**
-         * Add keyboard input support to the input manager.
-         * @returns the current input manager
-         */
-        public addKeyboard(): ArcRotateCameraInputsManager {
-            this.add(new ArcRotateCameraKeyboardMoveInput());
-            return this;
-        }
+    /**
+     * Add keyboard input support to the input manager.
+     * @returns the current input manager
+     */
+    public addKeyboard(): ArcRotateCameraInputsManager {
+        this.add(new ArcRotateCameraKeyboardMoveInput());
+        return this;
+    }
 
-        /**
-         * Add orientation input support to the input manager.
-         * @returns the current input manager
-         */
-        public addVRDeviceOrientation(): ArcRotateCameraInputsManager {
-            this.add(new ArcRotateCameraVRDeviceOrientationInput());
-            return this;
-        }
+    /**
+     * Add orientation input support to the input manager.
+     * @returns the current input manager
+     */
+    public addVRDeviceOrientation(): ArcRotateCameraInputsManager {
+        this.add(new ArcRotateCameraVRDeviceOrientationInput());
+        return this;
     }
+}

文件差異過大導致無法顯示
+ 1058 - 1058
src/Cameras/camera.ts


+ 240 - 240
src/Cameras/cameraInputsManager.ts

@@ -2,298 +2,298 @@ import { Logger } from "../Misc/logger";
 import { SerializationHelper } from "../Misc/decorators";
 import { Nullable } from "../types";
 import { Camera } from "./camera";
+/**
+ * @ignore
+ * This is a list of all the different input types that are available in the application.
+ * Fo instance: ArcRotateCameraGamepadInput...
+ */
+export var CameraInputTypes = {};
+
+/**
+ * This is the contract to implement in order to create a new input class.
+ * Inputs are dealing with listening to user actions and moving the camera accordingly.
+ */
+export interface ICameraInput<TCamera extends Camera> {
+    /**
+     * Defines the camera the input is attached to.
+     */
+    camera: Nullable<TCamera>;
+    /**
+     * Gets the class name of the current intput.
+     * @returns the class name
+     */
+    getClassName(): string;
+    /**
+     * Get the friendly name associated with the input class.
+     * @returns the input friendly name
+     */
+    getSimpleName(): string;
+    /**
+     * Attach the input controls to a specific dom element to get the input from.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    attachControl(element: HTMLElement, noPreventDefault?: boolean): void;
+    /**
+     * Detach the current controls from the specified dom element.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    detachControl(element: Nullable<HTMLElement>): void;
     /**
-     * @ignore
-     * This is a list of all the different input types that are available in the application.
-     * Fo instance: ArcRotateCameraGamepadInput...
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
      */
-    export var CameraInputTypes = {};
+    checkInputs?: () => void;
+}
 
+/**
+ * Represents a map of input types to input instance or input index to input instance.
+ */
+export interface CameraInputsMap<TCamera extends Camera> {
     /**
-     * This is the contract to implement in order to create a new input class.
-     * Inputs are dealing with listening to user actions and moving the camera accordingly.
+     * Accessor to the input by input type.
      */
-    export interface ICameraInput<TCamera extends Camera> {
-        /**
-         * Defines the camera the input is attached to.
-         */
-        camera: Nullable<TCamera>;
-        /**
-         * Gets the class name of the current intput.
-         * @returns the class name
-         */
-        getClassName(): string;
-        /**
-         * Get the friendly name associated with the input class.
-         * @returns the input friendly name
-         */
-        getSimpleName(): string;
-        /**
-         * Attach the input controls to a specific dom element to get the input from.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        attachControl(element: HTMLElement, noPreventDefault?: boolean): void;
-        /**
-         * Detach the current controls from the specified dom element.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        detachControl(element: Nullable<HTMLElement>): void;
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        checkInputs?: () => void;
-    }
+    [name: string]: ICameraInput<TCamera>;
+    /**
+     * Accessor to the input by input index.
+     */
+    [idx: number]: ICameraInput<TCamera>;
+}
 
+/**
+ * This represents the input manager used within a camera.
+ * It helps dealing with all the different kind of input attached to a camera.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class CameraInputsManager<TCamera extends Camera> {
     /**
-     * Represents a map of input types to input instance or input index to input instance.
+     * Defines the list of inputs attahed to the camera.
      */
-    export interface CameraInputsMap<TCamera extends Camera> {
-        /**
-         * Accessor to the input by input type.
-         */
-        [name: string]: ICameraInput<TCamera>;
-        /**
-         * Accessor to the input by input index.
-         */
-        [idx: number]: ICameraInput<TCamera>;
-    }
+    public attached: CameraInputsMap<TCamera>;
 
     /**
-     * This represents the input manager used within a camera.
-     * It helps dealing with all the different kind of input attached to a camera.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Defines the dom element the camera is collecting inputs from.
+     * This is null if the controls have not been attached.
      */
-    export class CameraInputsManager<TCamera extends Camera> {
-        /**
-         * Defines the list of inputs attahed to the camera.
-         */
-        public attached: CameraInputsMap<TCamera>;
+    public attachedElement: Nullable<HTMLElement>;
 
-        /**
-         * Defines the dom element the camera is collecting inputs from.
-         * This is null if the controls have not been attached.
-         */
-        public attachedElement: Nullable<HTMLElement>;
+    /**
+     * Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public noPreventDefault: boolean;
 
-        /**
-         * Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public noPreventDefault: boolean;
+    /**
+     * Defined the camera the input manager belongs to.
+     */
+    public camera: TCamera;
 
-        /**
-         * Defined the camera the input manager belongs to.
-         */
-        public camera: TCamera;
+    /**
+     * Update the current camera state depending on the inputs that have been used this frame.
+     * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
+     */
+    public checkInputs: () => void;
 
-        /**
-         * Update the current camera state depending on the inputs that have been used this frame.
-         * This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
-         */
-        public checkInputs: () => void;
+    /**
+     * Instantiate a new Camera Input Manager.
+     * @param camera Defines the camera the input manager blongs to
+     */
+    constructor(camera: TCamera) {
+        this.attached = {};
+        this.camera = camera;
+        this.checkInputs = () => { };
+    }
 
-        /**
-         * Instantiate a new Camera Input Manager.
-         * @param camera Defines the camera the input manager blongs to
-         */
-        constructor(camera: TCamera) {
-            this.attached = {};
-            this.camera = camera;
-            this.checkInputs = () => { };
+    /**
+     * Add an input method to a camera
+     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * @param input camera input method
+     */
+    public add(input: ICameraInput<TCamera>): void {
+        var type = input.getSimpleName();
+        if (this.attached[type]) {
+            Logger.Warn("camera input of type " + type + " already exists on camera");
+            return;
         }
 
-        /**
-         * Add an input method to a camera
-         * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
-         * @param input camera input method
-         */
-        public add(input: ICameraInput<TCamera>): void {
-            var type = input.getSimpleName();
-            if (this.attached[type]) {
-                Logger.Warn("camera input of type " + type + " already exists on camera");
-                return;
-            }
-
-            this.attached[type] = input;
+        this.attached[type] = input;
 
-            input.camera = this.camera;
+        input.camera = this.camera;
 
-            //for checkInputs, we are dynamically creating a function
-            //the goal is to avoid the performance penalty of looping for inputs in the render loop
-            if (input.checkInputs) {
-                this.checkInputs = this._addCheckInputs(input.checkInputs.bind(input));
-            }
+        //for checkInputs, we are dynamically creating a function
+        //the goal is to avoid the performance penalty of looping for inputs in the render loop
+        if (input.checkInputs) {
+            this.checkInputs = this._addCheckInputs(input.checkInputs.bind(input));
+        }
 
-            if (this.attachedElement) {
-                input.attachControl(this.attachedElement);
-            }
+        if (this.attachedElement) {
+            input.attachControl(this.attachedElement);
         }
+    }
 
-        /**
-         * Remove a specific input method from a camera
-         * example: camera.inputs.remove(camera.inputs.attached.mouse);
-         * @param inputToRemove camera input method
-         */
-        public remove(inputToRemove: ICameraInput<TCamera>): void {
-            for (var cam in this.attached) {
-                var input = this.attached[cam];
-                if (input === inputToRemove) {
-                    input.detachControl(this.attachedElement);
-                    input.camera = null;
-                    delete this.attached[cam];
-                    this.rebuildInputCheck();
-                }
+    /**
+     * Remove a specific input method from a camera
+     * example: camera.inputs.remove(camera.inputs.attached.mouse);
+     * @param inputToRemove camera input method
+     */
+    public remove(inputToRemove: ICameraInput<TCamera>): void {
+        for (var cam in this.attached) {
+            var input = this.attached[cam];
+            if (input === inputToRemove) {
+                input.detachControl(this.attachedElement);
+                input.camera = null;
+                delete this.attached[cam];
+                this.rebuildInputCheck();
             }
         }
+    }
 
-        /**
-         * Remove a specific input type from a camera
-         * example: camera.inputs.remove("ArcRotateCameraGamepadInput");
-         * @param inputType the type of the input to remove
-         */
-        public removeByType(inputType: string): void {
-            for (var cam in this.attached) {
-                var input = this.attached[cam];
-                if (input.getClassName() === inputType) {
-                    input.detachControl(this.attachedElement);
-                    input.camera = null;
-                    delete this.attached[cam];
-                    this.rebuildInputCheck();
-                }
+    /**
+     * Remove a specific input type from a camera
+     * example: camera.inputs.remove("ArcRotateCameraGamepadInput");
+     * @param inputType the type of the input to remove
+     */
+    public removeByType(inputType: string): void {
+        for (var cam in this.attached) {
+            var input = this.attached[cam];
+            if (input.getClassName() === inputType) {
+                input.detachControl(this.attachedElement);
+                input.camera = null;
+                delete this.attached[cam];
+                this.rebuildInputCheck();
             }
         }
+    }
 
-        private _addCheckInputs(fn: () => void) {
-            var current = this.checkInputs;
-            return () => {
-                current();
-                fn();
-            };
-        }
+    private _addCheckInputs(fn: () => void) {
+        var current = this.checkInputs;
+        return () => {
+            current();
+            fn();
+        };
+    }
 
-        /**
-         * Attach the input controls to the currently attached dom element to listen the events from.
-         * @param input Defines the input to attach
-         */
-        public attachInput(input: ICameraInput<TCamera>): void {
-            if (this.attachedElement) {
-                input.attachControl(this.attachedElement, this.noPreventDefault);
-            }
+    /**
+     * Attach the input controls to the currently attached dom element to listen the events from.
+     * @param input Defines the input to attach
+     */
+    public attachInput(input: ICameraInput<TCamera>): void {
+        if (this.attachedElement) {
+            input.attachControl(this.attachedElement, this.noPreventDefault);
         }
+    }
 
-        /**
-         * Attach the current manager inputs controls to a specific dom element to listen the events from.
-         * @param element Defines the dom element to collect the events from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachElement(element: HTMLElement, noPreventDefault: boolean = false): void {
-            if (this.attachedElement) {
-                return;
-            }
+    /**
+     * Attach the current manager inputs controls to a specific dom element to listen the events from.
+     * @param element Defines the dom element to collect the events from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachElement(element: HTMLElement, noPreventDefault: boolean = false): void {
+        if (this.attachedElement) {
+            return;
+        }
 
-            noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
-            this.attachedElement = element;
-            this.noPreventDefault = noPreventDefault;
+        noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
+        this.attachedElement = element;
+        this.noPreventDefault = noPreventDefault;
 
-            for (var cam in this.attached) {
-                this.attached[cam].attachControl(element, noPreventDefault);
-            }
+        for (var cam in this.attached) {
+            this.attached[cam].attachControl(element, noPreventDefault);
         }
+    }
 
-        /**
-         * Detach the current manager inputs controls from a specific dom element.
-         * @param element Defines the dom element to collect the events from
-         * @param disconnect Defines whether the input should be removed from the current list of attached inputs
-         */
-        public detachElement(element: HTMLElement, disconnect = false): void {
-            if (this.attachedElement !== element) {
-                return;
-            }
+    /**
+     * Detach the current manager inputs controls from a specific dom element.
+     * @param element Defines the dom element to collect the events from
+     * @param disconnect Defines whether the input should be removed from the current list of attached inputs
+     */
+    public detachElement(element: HTMLElement, disconnect = false): void {
+        if (this.attachedElement !== element) {
+            return;
+        }
 
-            for (var cam in this.attached) {
-                this.attached[cam].detachControl(element);
+        for (var cam in this.attached) {
+            this.attached[cam].detachControl(element);
 
-                if (disconnect) {
-                    this.attached[cam].camera = null;
-                }
+            if (disconnect) {
+                this.attached[cam].camera = null;
             }
-
-            this.attachedElement = null;
         }
 
-        /**
-         * Rebuild the dynamic inputCheck function from the current list of
-         * defined inputs in the manager.
-         */
-        public rebuildInputCheck(): void {
-            this.checkInputs = () => { };
+        this.attachedElement = null;
+    }
 
-            for (var cam in this.attached) {
-                var input = this.attached[cam];
-                if (input.checkInputs) {
-                    this.checkInputs = this._addCheckInputs(input.checkInputs.bind(input));
-                }
-            }
-        }
+    /**
+     * Rebuild the dynamic inputCheck function from the current list of
+     * defined inputs in the manager.
+     */
+    public rebuildInputCheck(): void {
+        this.checkInputs = () => { };
 
-        /**
-         * Remove all attached input methods from a camera
-         */
-        public clear(): void {
-            if (this.attachedElement) {
-                this.detachElement(this.attachedElement, true);
+        for (var cam in this.attached) {
+            var input = this.attached[cam];
+            if (input.checkInputs) {
+                this.checkInputs = this._addCheckInputs(input.checkInputs.bind(input));
             }
-            this.attached = {};
-            this.attachedElement = null;
-            this.checkInputs = () => { };
         }
+    }
 
-        /**
-         * Serialize the current input manager attached to a camera.
-         * This ensures than once parsed,
-         * the input associated to the camera will be identical to the current ones
-         * @param serializedCamera Defines the camera serialization JSON the input serialization should write to
-         */
-        public serialize(serializedCamera: any): void {
-            var inputs: { [key: string]: any } = {};
-            for (var cam in this.attached) {
-                var input = this.attached[cam];
-                var res = SerializationHelper.Serialize(input);
-                inputs[input.getClassName()] = res;
-            }
+    /**
+     * Remove all attached input methods from a camera
+     */
+    public clear(): void {
+        if (this.attachedElement) {
+            this.detachElement(this.attachedElement, true);
+        }
+        this.attached = {};
+        this.attachedElement = null;
+        this.checkInputs = () => { };
+    }
 
-            serializedCamera.inputsmgr = inputs;
+    /**
+     * Serialize the current input manager attached to a camera.
+     * This ensures than once parsed,
+     * the input associated to the camera will be identical to the current ones
+     * @param serializedCamera Defines the camera serialization JSON the input serialization should write to
+     */
+    public serialize(serializedCamera: any): void {
+        var inputs: { [key: string]: any } = {};
+        for (var cam in this.attached) {
+            var input = this.attached[cam];
+            var res = SerializationHelper.Serialize(input);
+            inputs[input.getClassName()] = res;
         }
 
-        /**
-         * Parses an input manager serialized JSON to restore the previous list of inputs
-         * and states associated to a camera.
-         * @param parsedCamera Defines the JSON to parse
-         */
-        public parse(parsedCamera: any): void {
-            var parsedInputs = parsedCamera.inputsmgr;
-            if (parsedInputs) {
-                this.clear();
+        serializedCamera.inputsmgr = inputs;
+    }
 
-                for (var n in parsedInputs) {
-                    var construct = (<any>CameraInputTypes)[n];
-                    if (construct) {
-                        var parsedinput = parsedInputs[n];
-                        var input = SerializationHelper.Parse(() => { return new construct(); }, parsedinput, null);
-                        this.add(input as any);
-                    }
+    /**
+     * Parses an input manager serialized JSON to restore the previous list of inputs
+     * and states associated to a camera.
+     * @param parsedCamera Defines the JSON to parse
+     */
+    public parse(parsedCamera: any): void {
+        var parsedInputs = parsedCamera.inputsmgr;
+        if (parsedInputs) {
+            this.clear();
+
+            for (var n in parsedInputs) {
+                var construct = (<any>CameraInputTypes)[n];
+                if (construct) {
+                    var parsedinput = parsedInputs[n];
+                    var input = SerializationHelper.Parse(() => { return new construct(); }, parsedinput, null);
+                    this.add(input as any);
                 }
-            } else {
-                //2016-03-08 this part is for managing backward compatibility
-                for (var n in this.attached) {
-                    var construct = (<any>CameraInputTypes)[this.attached[n].getClassName()];
-                    if (construct) {
-                        var input = SerializationHelper.Parse(() => { return new construct(); }, parsedCamera, null);
-                        this.remove(this.attached[n]);
-                        this.add(input as any);
-                    }
+            }
+        } else {
+            //2016-03-08 this part is for managing backward compatibility
+            for (var n in this.attached) {
+                var construct = (<any>CameraInputTypes)[this.attached[n].getClassName()];
+                if (construct) {
+                    var input = SerializationHelper.Parse(() => { return new construct(); }, parsedCamera, null);
+                    this.remove(this.attached[n]);
+                    this.add(input as any);
                 }
             }
         }
     }
+}

+ 61 - 61
src/Cameras/deviceOrientationCamera.ts

@@ -3,77 +3,77 @@ import { Scene } from "../scene";
 import { Quaternion, Vector3, Axis } from "../Maths/math";
 import { Node } from "../node";
 
-    Node.AddNodeConstructor("DeviceOrientationCamera", (name, scene) => {
-        return () => new DeviceOrientationCamera(name, Vector3.Zero(), scene);
-    });
+Node.AddNodeConstructor("DeviceOrientationCamera", (name, scene) => {
+    return () => new DeviceOrientationCamera(name, Vector3.Zero(), scene);
+});
+
+// We're mainly based on the logic defined into the FreeCamera code
+/**
+ * This is a camera specifically designed to react to device orientation events such as a modern mobile device
+ * being tilted forward or back and left or right.
+ */
+export class DeviceOrientationCamera extends FreeCamera {
+
+    private _initialQuaternion: Quaternion;
+    private _quaternionCache: Quaternion;
 
-    // We're mainly based on the logic defined into the FreeCamera code
     /**
-     * This is a camera specifically designed to react to device orientation events such as a modern mobile device
-     * being tilted forward or back and left or right.
+     * Creates a new device orientation camera
+     * @param name The name of the camera
+     * @param position The start position camera
+     * @param scene The scene the camera belongs to
      */
-    export class DeviceOrientationCamera extends FreeCamera {
+    constructor(name: string, position: Vector3, scene: Scene) {
+        super(name, position, scene);
+        this._quaternionCache = new Quaternion();
+        this.inputs.addDeviceOrientation();
+    }
 
-        private _initialQuaternion: Quaternion;
-        private _quaternionCache: Quaternion;
+    /**
+     * Gets the current instance class name ("DeviceOrientationCamera").
+     * This helps avoiding instanceof at run time.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "DeviceOrientationCamera";
+    }
 
-        /**
-         * Creates a new device orientation camera
-         * @param name The name of the camera
-         * @param position The start position camera
-         * @param scene The scene the camera belongs to
-         */
-        constructor(name: string, position: Vector3, scene: Scene) {
-            super(name, position, scene);
-            this._quaternionCache = new Quaternion();
-            this.inputs.addDeviceOrientation();
+    /**
+     * @hidden
+     * Checks and applies the current values of the inputs to the camera. (Internal use only)
+     */
+    public _checkInputs(): void {
+        super._checkInputs();
+        this._quaternionCache.copyFrom(this.rotationQuaternion);
+        if (this._initialQuaternion) {
+            this._initialQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
         }
+    }
 
-        /**
-         * Gets the current instance class name ("DeviceOrientationCamera").
-         * This helps avoiding instanceof at run time.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "DeviceOrientationCamera";
-        }
+    /**
+     * Reset the camera to its default orientation on the specified axis only.
+     * @param axis The axis to reset
+     */
+    public resetToCurrentRotation(axis: Axis = Axis.Y): void {
 
-        /**
-         * @hidden
-         * Checks and applies the current values of the inputs to the camera. (Internal use only)
-         */
-        public _checkInputs(): void {
-            super._checkInputs();
-            this._quaternionCache.copyFrom(this.rotationQuaternion);
-            if (this._initialQuaternion) {
-                this._initialQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
-            }
-        }
+        //can only work if this camera has a rotation quaternion already.
+        if (!this.rotationQuaternion) { return; }
 
-        /**
-         * Reset the camera to its default orientation on the specified axis only.
-         * @param axis The axis to reset
-         */
-        public resetToCurrentRotation(axis: Axis = Axis.Y): void {
+        if (!this._initialQuaternion) {
+            this._initialQuaternion = new Quaternion();
+        }
 
-            //can only work if this camera has a rotation quaternion already.
-            if (!this.rotationQuaternion) { return; }
+        this._initialQuaternion.copyFrom(this._quaternionCache || this.rotationQuaternion);
 
-            if (!this._initialQuaternion) {
-                this._initialQuaternion = new Quaternion();
+        ['x', 'y', 'z'].forEach((axisName) => {
+            if (!(<any>axis)[axisName]) {
+                (<any>this._initialQuaternion)[axisName] = 0;
+            } else {
+                (<any>this._initialQuaternion)[axisName] *= -1;
             }
-
-            this._initialQuaternion.copyFrom(this._quaternionCache || this.rotationQuaternion);
-
-            ['x', 'y', 'z'].forEach((axisName) => {
-                if (!(<any>axis)[axisName]) {
-                    (<any>this._initialQuaternion)[axisName] = 0;
-                } else {
-                    (<any>this._initialQuaternion)[axisName] *= -1;
-                }
-            });
-            this._initialQuaternion.normalize();
-            //force rotation update
-            this._initialQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
-        }
+        });
+        this._initialQuaternion.normalize();
+        //force rotation update
+        this._initialQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
     }
+}

+ 342 - 342
src/Cameras/flyCamera.ts

@@ -9,417 +9,417 @@ import { TargetCamera } from "./targetCamera";
 import { FlyCameraInputsManager } from "./flyCameraInputsManager";
 import { FlyCameraMouseInput } from "../Cameras/Inputs/flyCameraMouseInput";
 import { FlyCameraKeyboardInput } from "../Cameras/Inputs/flyCameraKeyboardInput";
+/**
+ * This is a flying camera, designed for 3D movement and rotation in all directions,
+ * such as in a 3D Space Shooter or a Flight Simulator.
+ */
+export class FlyCamera extends TargetCamera {
     /**
-     * This is a flying camera, designed for 3D movement and rotation in all directions,
-     * such as in a 3D Space Shooter or a Flight Simulator.
+     * Define the collision ellipsoid of the camera.
+     * This is helpful for simulating a camera body, like a player's body.
+     * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity#arcrotatecamera
      */
-    export class FlyCamera extends TargetCamera {
-        /**
-         * Define the collision ellipsoid of the camera.
-         * This is helpful for simulating a camera body, like a player's body.
-         * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity#arcrotatecamera
-         */
-        @serializeAsVector3()
-        public ellipsoid = new Vector3(1, 1, 1);
-
-        /**
-         * Define an offset for the position of the ellipsoid around the camera.
-         * This can be helpful if the camera is attached away from the player's body center,
-         * such as at its head.
-         */
-        @serializeAsVector3()
-        public ellipsoidOffset = new Vector3(0, 0, 0);
-
-        /**
-         * Enable or disable collisions of the camera with the rest of the scene objects.
-         */
-        @serialize()
-        public checkCollisions = false;
-
-        /**
-         * Enable or disable gravity on the camera.
-         */
-        @serialize()
-        public applyGravity = false;
-
-        /**
-         * Define the current direction the camera is moving to.
-         */
-        public cameraDirection = Vector3.Zero();
-
-        /**
-         * Define the current local rotation of the camera as a quaternion to prevent Gimbal lock.
-         * This overrides and empties cameraRotation.
-         */
-        public rotationQuaternion: Quaternion;
-
-        /**
-         * Track Roll to maintain the wanted Rolling when looking around.
-         */
-        public _trackRoll: number = 0;
-
-        /**
-        * Slowly correct the Roll to its original value after a Pitch+Yaw rotation.
-        */
-        public rollCorrect: number = 100;
-
-        /**
-         * Mimic a banked turn, Rolling the camera when Yawing.
-         * It's recommended to use rollCorrect = 10 for faster banking correction.
-         */
-        public bankedTurn: boolean = false;
-
-        /**
-         * Limit in radians for how much Roll banking will add. (Default: 90°)
-         */
-        public bankedTurnLimit: number = Math.PI / 2;
-
-        /**
-         * Value of 0 disables the banked Roll.
-         * Value of 1 is equal to the Yaw angle in radians.
-         */
-        public bankedTurnMultiplier: number = 1;
-
-        /**
-         * The inputs manager loads all the input sources, such as keyboard and mouse.
-         */
-        public inputs: FlyCameraInputsManager;
-
-        /**
-         * Gets the input sensibility for mouse input.
-         * Higher values reduce sensitivity.
-         */
-        public get angularSensibility(): number {
-            var mouse = <FlyCameraMouseInput>this.inputs.attached["mouse"];
-            if (mouse) {
-                return mouse.angularSensibility;
-            }
+    @serializeAsVector3()
+    public ellipsoid = new Vector3(1, 1, 1);
 
-            return 0;
-        }
+    /**
+     * Define an offset for the position of the ellipsoid around the camera.
+     * This can be helpful if the camera is attached away from the player's body center,
+     * such as at its head.
+     */
+    @serializeAsVector3()
+    public ellipsoidOffset = new Vector3(0, 0, 0);
 
-        /**
-         * Sets the input sensibility for a mouse input.
-         * Higher values reduce sensitivity.
-         */
-        public set angularSensibility(value: number) {
-            var mouse = <FlyCameraMouseInput>this.inputs.attached["mouse"];
-            if (mouse) {
-                mouse.angularSensibility = value;
-            }
-        }
+    /**
+     * Enable or disable collisions of the camera with the rest of the scene objects.
+     */
+    @serialize()
+    public checkCollisions = false;
 
-        /**
-         * Get the keys for camera movement forward.
-         */
-        public get keysForward(): number[] {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                return keyboard.keysForward;
-            }
+    /**
+     * Enable or disable gravity on the camera.
+     */
+    @serialize()
+    public applyGravity = false;
 
-            return [];
-        }
+    /**
+     * Define the current direction the camera is moving to.
+     */
+    public cameraDirection = Vector3.Zero();
 
-        /**
-        * Set the keys for camera movement forward.
-        */
-        public set keysForward(value: number[]) {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                keyboard.keysForward = value;
-            }
+    /**
+     * Define the current local rotation of the camera as a quaternion to prevent Gimbal lock.
+     * This overrides and empties cameraRotation.
+     */
+    public rotationQuaternion: Quaternion;
+
+    /**
+     * Track Roll to maintain the wanted Rolling when looking around.
+     */
+    public _trackRoll: number = 0;
+
+    /**
+    * Slowly correct the Roll to its original value after a Pitch+Yaw rotation.
+    */
+    public rollCorrect: number = 100;
+
+    /**
+     * Mimic a banked turn, Rolling the camera when Yawing.
+     * It's recommended to use rollCorrect = 10 for faster banking correction.
+     */
+    public bankedTurn: boolean = false;
+
+    /**
+     * Limit in radians for how much Roll banking will add. (Default: 90°)
+     */
+    public bankedTurnLimit: number = Math.PI / 2;
+
+    /**
+     * Value of 0 disables the banked Roll.
+     * Value of 1 is equal to the Yaw angle in radians.
+     */
+    public bankedTurnMultiplier: number = 1;
+
+    /**
+     * The inputs manager loads all the input sources, such as keyboard and mouse.
+     */
+    public inputs: FlyCameraInputsManager;
+
+    /**
+     * Gets the input sensibility for mouse input.
+     * Higher values reduce sensitivity.
+     */
+    public get angularSensibility(): number {
+        var mouse = <FlyCameraMouseInput>this.inputs.attached["mouse"];
+        if (mouse) {
+            return mouse.angularSensibility;
         }
 
-        /**
-         * Get the keys for camera movement backward.
-         */
-        public get keysBackward(): number[] {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                return keyboard.keysBackward;
-            }
+        return 0;
+    }
 
-            return [];
+    /**
+     * Sets the input sensibility for a mouse input.
+     * Higher values reduce sensitivity.
+     */
+    public set angularSensibility(value: number) {
+        var mouse = <FlyCameraMouseInput>this.inputs.attached["mouse"];
+        if (mouse) {
+            mouse.angularSensibility = value;
         }
+    }
 
-        public set keysBackward(value: number[]) {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                keyboard.keysBackward = value;
-            }
+    /**
+     * Get the keys for camera movement forward.
+     */
+    public get keysForward(): number[] {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            return keyboard.keysForward;
         }
 
-        /**
-         * Get the keys for camera movement up.
-         */
-        public get keysUp(): number[] {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                return keyboard.keysUp;
-            }
+        return [];
+    }
 
-            return [];
+    /**
+    * Set the keys for camera movement forward.
+    */
+    public set keysForward(value: number[]) {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            keyboard.keysForward = value;
         }
+    }
 
-        /**
-        * Set the keys for camera movement up.
-        */
-        public set keysUp(value: number[]) {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                keyboard.keysUp = value;
-            }
+    /**
+     * Get the keys for camera movement backward.
+     */
+    public get keysBackward(): number[] {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            return keyboard.keysBackward;
         }
 
-        /**
-         * Get the keys for camera movement down.
-         */
-        public get keysDown(): number[] {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                return keyboard.keysDown;
-            }
+        return [];
+    }
 
-            return [];
+    public set keysBackward(value: number[]) {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            keyboard.keysBackward = value;
         }
+    }
 
-        /**
-        * Set the keys for camera movement down.
-        */
-        public set keysDown(value: number[]) {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                keyboard.keysDown = value;
-            }
+    /**
+     * Get the keys for camera movement up.
+     */
+    public get keysUp(): number[] {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            return keyboard.keysUp;
         }
 
-        /**
-         * Get the keys for camera movement left.
-         */
-        public get keysLeft(): number[] {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                return keyboard.keysLeft;
-            }
+        return [];
+    }
 
-            return [];
+    /**
+    * Set the keys for camera movement up.
+    */
+    public set keysUp(value: number[]) {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            keyboard.keysUp = value;
         }
+    }
 
-        /**
-        * Set the keys for camera movement left.
-        */
-        public set keysLeft(value: number[]) {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                keyboard.keysLeft = value;
-            }
+    /**
+     * Get the keys for camera movement down.
+     */
+    public get keysDown(): number[] {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            return keyboard.keysDown;
         }
 
-        /**
-         * Set the keys for camera movement right.
-         */
-        public get keysRight(): number[] {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                return keyboard.keysRight;
-            }
+        return [];
+    }
 
-            return [];
+    /**
+    * Set the keys for camera movement down.
+    */
+    public set keysDown(value: number[]) {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            keyboard.keysDown = value;
         }
+    }
 
-        /**
-        * Set the keys for camera movement right.
-        */
-        public set keysRight(value: number[]) {
-            var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
-            if (keyboard) {
-                keyboard.keysRight = value;
-            }
+    /**
+     * Get the keys for camera movement left.
+     */
+    public get keysLeft(): number[] {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            return keyboard.keysLeft;
         }
 
-        /**
-         * Event raised when the camera collides with a mesh in the scene.
-         */
-        public onCollide: (collidedMesh: AbstractMesh) => void;
-
-        private _collider: Collider;
-        private _needMoveForGravity = false;
-        private _oldPosition = Vector3.Zero();
-        private _diffPosition = Vector3.Zero();
-        private _newPosition = Vector3.Zero();
-
-        /** @hidden */
-        public _localDirection: Vector3;
-        /** @hidden */
-        public _transformedDirection: Vector3;
-
-        /**
-         * Instantiates a FlyCamera.
-         * This is a flying camera, designed for 3D movement and rotation in all directions,
-         * such as in a 3D Space Shooter or a Flight Simulator.
-         * @param name Define the name of the camera in the scene.
-         * @param position Define the starting position of the camera in the scene.
-         * @param scene Define the scene the camera belongs to.
-         * @param setActiveOnSceneIfNoneActive Defines wheter the camera should be marked as active, if no other camera has been defined as active.
-        */
-        constructor(name: string, position: Vector3, scene: Scene, setActiveOnSceneIfNoneActive = true) {
-            super(name, position, scene, setActiveOnSceneIfNoneActive);
-            this.inputs = new FlyCameraInputsManager(this);
-            this.inputs.addKeyboard().addMouse();
+        return [];
+    }
+
+    /**
+    * Set the keys for camera movement left.
+    */
+    public set keysLeft(value: number[]) {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            keyboard.keysLeft = value;
         }
+    }
 
-        /**
-         * Attach a control to the HTML DOM element.
-         * @param element Defines the element that listens to the input events.
-         * @param noPreventDefault Defines whether events caught by the controls should call preventdefault(). https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-            this.inputs.attachElement(element, noPreventDefault);
+    /**
+     * Set the keys for camera movement right.
+     */
+    public get keysRight(): number[] {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            return keyboard.keysRight;
         }
 
-        /**
-         * Detach a control from the HTML DOM element.
-         * The camera will stop reacting to that input.
-         * @param element Defines the element that listens to the input events.
-         */
-        public detachControl(element: HTMLElement): void {
-            this.inputs.detachElement(element);
+        return [];
+    }
 
-            this.cameraDirection = new Vector3(0, 0, 0);
+    /**
+    * Set the keys for camera movement right.
+    */
+    public set keysRight(value: number[]) {
+        var keyboard = <FlyCameraKeyboardInput>this.inputs.attached["keyboard"];
+        if (keyboard) {
+            keyboard.keysRight = value;
         }
+    }
+
+    /**
+     * Event raised when the camera collides with a mesh in the scene.
+     */
+    public onCollide: (collidedMesh: AbstractMesh) => void;
 
-        // Collisions.
-        private _collisionMask = -1;
+    private _collider: Collider;
+    private _needMoveForGravity = false;
+    private _oldPosition = Vector3.Zero();
+    private _diffPosition = Vector3.Zero();
+    private _newPosition = Vector3.Zero();
 
-        /**
-         * Get the mask that the camera ignores in collision events.
-         */
-        public get collisionMask(): number {
-            return this._collisionMask;
-        }
+    /** @hidden */
+    public _localDirection: Vector3;
+    /** @hidden */
+    public _transformedDirection: Vector3;
 
-        /**
-        * Set the mask that the camera ignores in collision events.
-        */
-        public set collisionMask(mask: number) {
-            this._collisionMask = !isNaN(mask) ? mask : -1;
-        }
+    /**
+     * Instantiates a FlyCamera.
+     * This is a flying camera, designed for 3D movement and rotation in all directions,
+     * such as in a 3D Space Shooter or a Flight Simulator.
+     * @param name Define the name of the camera in the scene.
+     * @param position Define the starting position of the camera in the scene.
+     * @param scene Define the scene the camera belongs to.
+     * @param setActiveOnSceneIfNoneActive Defines wheter the camera should be marked as active, if no other camera has been defined as active.
+    */
+    constructor(name: string, position: Vector3, scene: Scene, setActiveOnSceneIfNoneActive = true) {
+        super(name, position, scene, setActiveOnSceneIfNoneActive);
+        this.inputs = new FlyCameraInputsManager(this);
+        this.inputs.addKeyboard().addMouse();
+    }
 
-        /** @hidden */
-        public _collideWithWorld(displacement: Vector3): void {
-            var globalPosition: Vector3;
+    /**
+     * Attach a control to the HTML DOM element.
+     * @param element Defines the element that listens to the input events.
+     * @param noPreventDefault Defines whether events caught by the controls should call preventdefault(). https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        this.inputs.attachElement(element, noPreventDefault);
+    }
 
-            if (this.parent) {
-                globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
-            } else {
-                globalPosition = this.position;
-            }
+    /**
+     * Detach a control from the HTML DOM element.
+     * The camera will stop reacting to that input.
+     * @param element Defines the element that listens to the input events.
+     */
+    public detachControl(element: HTMLElement): void {
+        this.inputs.detachElement(element);
 
-            globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
-            this._oldPosition.addInPlace(this.ellipsoidOffset);
+        this.cameraDirection = new Vector3(0, 0, 0);
+    }
 
-            if (!this._collider) {
-                this._collider = new Collider();
-            }
+    // Collisions.
+    private _collisionMask = -1;
 
-            this._collider._radius = this.ellipsoid;
-            this._collider.collisionMask = this._collisionMask;
+    /**
+     * Get the mask that the camera ignores in collision events.
+     */
+    public get collisionMask(): number {
+        return this._collisionMask;
+    }
 
-            // No need for clone, as long as gravity is not on.
-            var actualDisplacement = displacement;
+    /**
+    * Set the mask that the camera ignores in collision events.
+    */
+    public set collisionMask(mask: number) {
+        this._collisionMask = !isNaN(mask) ? mask : -1;
+    }
 
-            // Add gravity to direction to prevent dual-collision checking.
-            if (this.applyGravity) {
-                // This prevents mending with cameraDirection, a global variable of the fly camera class.
-                actualDisplacement = displacement.add(this.getScene().gravity);
-            }
+    /** @hidden */
+    public _collideWithWorld(displacement: Vector3): void {
+        var globalPosition: Vector3;
 
-            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+        if (this.parent) {
+            globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
+        } else {
+            globalPosition = this.position;
         }
 
-        /** @hidden */
-        private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
+        globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
+        this._oldPosition.addInPlace(this.ellipsoidOffset);
 
-            var updatePosition = (newPos: Vector3) => {
-                this._newPosition.copyFrom(newPos);
+        if (!this._collider) {
+            this._collider = new Collider();
+        }
 
-                this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
+        this._collider._radius = this.ellipsoid;
+        this._collider.collisionMask = this._collisionMask;
 
-                if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
-                    this.position.addInPlace(this._diffPosition);
-                    if (this.onCollide && collidedMesh) {
-                        this.onCollide(collidedMesh);
-                    }
-                }
-            };
+        // No need for clone, as long as gravity is not on.
+        var actualDisplacement = displacement;
 
-            updatePosition(newPosition);
+        // Add gravity to direction to prevent dual-collision checking.
+        if (this.applyGravity) {
+            // This prevents mending with cameraDirection, a global variable of the fly camera class.
+            actualDisplacement = displacement.add(this.getScene().gravity);
         }
 
-        /** @hidden */
-        public _checkInputs(): void {
-            if (!this._localDirection) {
-                this._localDirection = Vector3.Zero();
-                this._transformedDirection = Vector3.Zero();
+        this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+    }
+
+    /** @hidden */
+    private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
+
+        var updatePosition = (newPos: Vector3) => {
+            this._newPosition.copyFrom(newPos);
+
+            this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
+
+            if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
+                this.position.addInPlace(this._diffPosition);
+                if (this.onCollide && collidedMesh) {
+                    this.onCollide(collidedMesh);
+                }
             }
+        };
 
-            this.inputs.checkInputs();
+        updatePosition(newPosition);
+    }
 
-            super._checkInputs();
+    /** @hidden */
+    public _checkInputs(): void {
+        if (!this._localDirection) {
+            this._localDirection = Vector3.Zero();
+            this._transformedDirection = Vector3.Zero();
         }
 
-        /** @hidden */
-        public _decideIfNeedsToMove(): boolean {
-            return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
-        }
+        this.inputs.checkInputs();
 
-        /** @hidden */
-        public _updatePosition(): void {
-            if (this.checkCollisions && this.getScene().collisionsEnabled) {
-                this._collideWithWorld(this.cameraDirection);
-            } else {
-                super._updatePosition();
-            }
+        super._checkInputs();
+    }
+
+    /** @hidden */
+    public _decideIfNeedsToMove(): boolean {
+        return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
+    }
+
+    /** @hidden */
+    public _updatePosition(): void {
+        if (this.checkCollisions && this.getScene().collisionsEnabled) {
+            this._collideWithWorld(this.cameraDirection);
+        } else {
+            super._updatePosition();
         }
+    }
 
-        /**
-         * Restore the Roll to its target value at the rate specified.
-         * @param rate - Higher means slower restoring.
-         * @hidden
-         */
-        public restoreRoll(rate: number): void {
-            let limit = this._trackRoll;    // Target Roll.
-            let z = this.rotation.z; // Current Roll.
-            let delta = limit - z;          // Difference in Roll.
-
-            let minRad = 0.001; // Tenth of a radian is a barely noticable difference.
-
-            // If the difference is noticable, restore the Roll.
-            if (Math.abs(delta) >= minRad) {
-                // Change Z rotation towards the target Roll.
-                this.rotation.z += delta / rate;
-
-                // Match when near enough.
-                if (Math.abs(limit - this.rotation.z) <= minRad) {
-                    this.rotation.z = limit;
-                }
+    /**
+     * Restore the Roll to its target value at the rate specified.
+     * @param rate - Higher means slower restoring.
+     * @hidden
+     */
+    public restoreRoll(rate: number): void {
+        let limit = this._trackRoll;    // Target Roll.
+        let z = this.rotation.z; // Current Roll.
+        let delta = limit - z;          // Difference in Roll.
+
+        let minRad = 0.001; // Tenth of a radian is a barely noticable difference.
+
+        // If the difference is noticable, restore the Roll.
+        if (Math.abs(delta) >= minRad) {
+            // Change Z rotation towards the target Roll.
+            this.rotation.z += delta / rate;
+
+            // Match when near enough.
+            if (Math.abs(limit - this.rotation.z) <= minRad) {
+                this.rotation.z = limit;
             }
         }
+    }
 
-        /**
-         * Destroy the camera and release the current resources held by it.
-         */
-        public dispose(): void {
-            this.inputs.clear();
-            super.dispose();
-        }
+    /**
+     * Destroy the camera and release the current resources held by it.
+     */
+    public dispose(): void {
+        this.inputs.clear();
+        super.dispose();
+    }
 
-        /**
-         * Get the current object class name.
-         * @returns the class name.
-         */
-        public getClassName(): string {
-            return "FlyCamera";
-        }
+    /**
+     * Get the current object class name.
+     * @returns the class name.
+     */
+    public getClassName(): string {
+        return "FlyCamera";
     }
+}

+ 28 - 28
src/Cameras/flyCameraInputsManager.ts

@@ -3,36 +3,36 @@ import { CameraInputsManager } from "./cameraInputsManager";
 import { FlyCameraMouseInput } from "../Cameras/Inputs/flyCameraMouseInput";
 import { FlyCameraKeyboardInput } from "../Cameras/Inputs/flyCameraKeyboardInput";
 
+/**
+ * Default Inputs manager for the FlyCamera.
+ * It groups all the default supported inputs for ease of use.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FlyCameraInputsManager extends CameraInputsManager<FlyCamera> {
     /**
-     * Default Inputs manager for the FlyCamera.
-     * It groups all the default supported inputs for ease of use.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Instantiates a new FlyCameraInputsManager.
+     * @param camera Defines the camera the inputs belong to.
      */
-    export class FlyCameraInputsManager extends CameraInputsManager<FlyCamera> {
-        /**
-         * Instantiates a new FlyCameraInputsManager.
-         * @param camera Defines the camera the inputs belong to.
-         */
-        constructor(camera: FlyCamera) {
-            super(camera);
-        }
+    constructor(camera: FlyCamera) {
+        super(camera);
+    }
 
-        /**
-         * Add keyboard input support to the input manager.
-         * @returns the new FlyCameraKeyboardMoveInput().
-         */
-        addKeyboard(): FlyCameraInputsManager {
-            this.add(new FlyCameraKeyboardInput());
-            return this;
-        }
+    /**
+     * Add keyboard input support to the input manager.
+     * @returns the new FlyCameraKeyboardMoveInput().
+     */
+    addKeyboard(): FlyCameraInputsManager {
+        this.add(new FlyCameraKeyboardInput());
+        return this;
+    }
 
-        /**
-         * Add mouse input support to the input manager.
-         * @param touchEnabled Enable touch screen support.
-         * @returns the new FlyCameraMouseInput().
-         */
-        addMouse(touchEnabled = true): FlyCameraInputsManager {
-            this.add(new FlyCameraMouseInput(touchEnabled));
-            return this;
-        }
+    /**
+     * Add mouse input support to the input manager.
+     * @param touchEnabled Enable touch screen support.
+     * @returns the new FlyCameraMouseInput().
+     */
+    addMouse(touchEnabled = true): FlyCameraInputsManager {
+        this.add(new FlyCameraMouseInput(touchEnabled));
+        return this;
     }
+}

+ 190 - 190
src/Cameras/followCamera.ts

@@ -7,221 +7,221 @@ import { Matrix, Vector3 } from "../Maths/math";
 import { Node } from "../node";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { FollowCameraInputsManager } from './followCameraInputsManager';
-    Node.AddNodeConstructor("FollowCamera", (name, scene) => {
-        return () => new FollowCamera(name, Vector3.Zero(), scene);
-    });
+Node.AddNodeConstructor("FollowCamera", (name, scene) => {
+    return () => new FollowCamera(name, Vector3.Zero(), scene);
+});
+
+Node.AddNodeConstructor("ArcFollowCamera", (name, scene) => {
+    return () => new ArcFollowCamera(name, 0, 0, 1.0, null, scene);
+});
+
+/**
+ * A follow camera takes a mesh as a target and follows it as it moves. Both a free camera version followCamera and
+ * an arc rotate version arcFollowCamera are available.
+ * @see http://doc.babylonjs.com/features/cameras#follow-camera
+ */
+export class FollowCamera extends TargetCamera {
+    /**
+     * Distance the follow camera should follow an object at
+     */
+    @serialize()
+    public radius: number = 12;
+
+    /**
+     * Define a rotation offset between the camera and the object it follows
+     */
+    @serialize()
+    public rotationOffset: number = 0;
+
+    /**
+     * Define a height offset between the camera and the object it follows.
+     * It can help following an object from the top (like a car chaing a plane)
+     */
+    @serialize()
+    public heightOffset: number = 4;
+
+    /**
+     * Define how fast the camera can accelerate to follow it s target.
+     */
+    @serialize()
+    public cameraAcceleration: number = 0.05;
+
+    /**
+     * Define the speed limit of the camera following an object.
+     */
+    @serialize()
+    public maxCameraSpeed: number = 20;
+
+    /**
+     * Define the target of the camera.
+     */
+    @serializeAsMeshReference("lockedTargetId")
+    public lockedTarget: Nullable<AbstractMesh>;
 
-    Node.AddNodeConstructor("ArcFollowCamera", (name, scene) => {
-        return () => new ArcFollowCamera(name, 0, 0, 1.0, null, scene);
-    });
+    /**
+     * Defines the input associated with the camera.
+     */
+    public inputs: FollowCameraInputsManager;
 
     /**
-     * A follow camera takes a mesh as a target and follows it as it moves. Both a free camera version followCamera and
-     * an arc rotate version arcFollowCamera are available.
+     * Instantiates the follow camera.
      * @see http://doc.babylonjs.com/features/cameras#follow-camera
+     * @param name Define the name of the camera in the scene
+     * @param position Define the position of the camera
+     * @param scene Define the scene the camera belong to
+     * @param lockedTarget Define the target of the camera
      */
-    export class FollowCamera extends TargetCamera {
-        /**
-         * Distance the follow camera should follow an object at
-         */
-        @serialize()
-        public radius: number = 12;
-
-        /**
-         * Define a rotation offset between the camera and the object it follows
-         */
-        @serialize()
-        public rotationOffset: number = 0;
-
-        /**
-         * Define a height offset between the camera and the object it follows.
-         * It can help following an object from the top (like a car chaing a plane)
-         */
-        @serialize()
-        public heightOffset: number = 4;
-
-        /**
-         * Define how fast the camera can accelerate to follow it s target.
-         */
-        @serialize()
-        public cameraAcceleration: number = 0.05;
-
-        /**
-         * Define the speed limit of the camera following an object.
-         */
-        @serialize()
-        public maxCameraSpeed: number = 20;
-
-        /**
-         * Define the target of the camera.
-         */
-        @serializeAsMeshReference("lockedTargetId")
-        public lockedTarget: Nullable<AbstractMesh>;
-
-        /**
-         * Defines the input associated with the camera.
-         */
-        public inputs: FollowCameraInputsManager;
-
-        /**
-         * Instantiates the follow camera.
-         * @see http://doc.babylonjs.com/features/cameras#follow-camera
-         * @param name Define the name of the camera in the scene
-         * @param position Define the position of the camera
-         * @param scene Define the scene the camera belong to
-         * @param lockedTarget Define the target of the camera
-         */
-        constructor(name: string, position: Vector3, scene: Scene, lockedTarget: Nullable<AbstractMesh> = null) {
-            super(name, position, scene);
-
-            this.lockedTarget = lockedTarget;
-            this.inputs = new FollowCameraInputsManager(this);
-            this.inputs.addKeyboard();
-            // Uncomment the following line when the relevant handlers have been implemented.
-            // this.inputs.addKeyboard().addMouseWheel().addPointers().addVRDeviceOrientation();
+    constructor(name: string, position: Vector3, scene: Scene, lockedTarget: Nullable<AbstractMesh> = null) {
+        super(name, position, scene);
+
+        this.lockedTarget = lockedTarget;
+        this.inputs = new FollowCameraInputsManager(this);
+        this.inputs.addKeyboard();
+        // Uncomment the following line when the relevant handlers have been implemented.
+        // this.inputs.addKeyboard().addMouseWheel().addPointers().addVRDeviceOrientation();
+    }
+
+    private _follow(cameraTarget: AbstractMesh) {
+        if (!cameraTarget) {
+            return;
         }
 
-        private _follow(cameraTarget: AbstractMesh) {
-            if (!cameraTarget) {
-                return;
-            }
-
-            var yRotation;
-            if (cameraTarget.rotationQuaternion) {
-                var rotMatrix = new Matrix();
-                cameraTarget.rotationQuaternion.toRotationMatrix(rotMatrix);
-                yRotation = Math.atan2(rotMatrix.m[8], rotMatrix.m[10]);
-            } else {
-                yRotation = cameraTarget.rotation.y;
-            }
-            var radians = Tools.ToRadians(this.rotationOffset) + yRotation;
-            var targetPosition = cameraTarget.getAbsolutePosition();
-            var targetX: number = targetPosition.x + Math.sin(radians) * this.radius;
-
-            var targetZ: number = targetPosition.z + Math.cos(radians) * this.radius;
-            var dx: number = targetX - this.position.x;
-            var dy: number = (targetPosition.y + this.heightOffset) - this.position.y;
-            var dz: number = (targetZ) - this.position.z;
-            var vx: number = dx * this.cameraAcceleration * 2; //this is set to .05
-            var vy: number = dy * this.cameraAcceleration;
-            var vz: number = dz * this.cameraAcceleration * 2;
-
-            if (vx > this.maxCameraSpeed || vx < -this.maxCameraSpeed) {
-                vx = vx < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
-            }
-
-            if (vy > this.maxCameraSpeed || vy < -this.maxCameraSpeed) {
-                vy = vy < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
-            }
-
-            if (vz > this.maxCameraSpeed || vz < -this.maxCameraSpeed) {
-                vz = vz < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
-            }
-
-            this.position = new Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
-            this.setTarget(targetPosition);
+        var yRotation;
+        if (cameraTarget.rotationQuaternion) {
+            var rotMatrix = new Matrix();
+            cameraTarget.rotationQuaternion.toRotationMatrix(rotMatrix);
+            yRotation = Math.atan2(rotMatrix.m[8], rotMatrix.m[10]);
+        } else {
+            yRotation = cameraTarget.rotation.y;
+        }
+        var radians = Tools.ToRadians(this.rotationOffset) + yRotation;
+        var targetPosition = cameraTarget.getAbsolutePosition();
+        var targetX: number = targetPosition.x + Math.sin(radians) * this.radius;
+
+        var targetZ: number = targetPosition.z + Math.cos(radians) * this.radius;
+        var dx: number = targetX - this.position.x;
+        var dy: number = (targetPosition.y + this.heightOffset) - this.position.y;
+        var dz: number = (targetZ) - this.position.z;
+        var vx: number = dx * this.cameraAcceleration * 2; //this is set to .05
+        var vy: number = dy * this.cameraAcceleration;
+        var vz: number = dz * this.cameraAcceleration * 2;
+
+        if (vx > this.maxCameraSpeed || vx < -this.maxCameraSpeed) {
+            vx = vx < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
         }
 
-        /**
-         * Attached controls to the current camera.
-         * @param element Defines the element the controls should be listened from
-         * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
-         */
-        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
-          this.inputs.attachElement(element, noPreventDefault);
+        if (vy > this.maxCameraSpeed || vy < -this.maxCameraSpeed) {
+            vy = vy < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
+        }
 
-          this._reset = () => {
-          };
+        if (vz > this.maxCameraSpeed || vz < -this.maxCameraSpeed) {
+            vz = vz < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
         }
 
-        /**
-         * Detach the current controls from the camera.
-         * The camera will stop reacting to inputs.
-         * @param element Defines the element to stop listening the inputs from
-         */
-        public detachControl(element: HTMLElement): void {
-          this.inputs.detachElement(element);
+        this.position = new Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
+        this.setTarget(targetPosition);
+    }
+
+    /**
+     * Attached controls to the current camera.
+     * @param element Defines the element the controls should be listened from
+     * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
+     */
+    public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+        this.inputs.attachElement(element, noPreventDefault);
+
+        this._reset = () => {
+        };
+    }
+
+    /**
+     * Detach the current controls from the camera.
+     * The camera will stop reacting to inputs.
+     * @param element Defines the element to stop listening the inputs from
+     */
+    public detachControl(element: HTMLElement): void {
+        this.inputs.detachElement(element);
 
-          if (this._reset) {
+        if (this._reset) {
             this._reset();
-          }
         }
+    }
 
-        /** @hidden */
-        public _checkInputs(): void {
-            this.inputs.checkInputs();
-            super._checkInputs();
-            if (this.lockedTarget) {
-                this._follow(this.lockedTarget);
-            }
+    /** @hidden */
+    public _checkInputs(): void {
+        this.inputs.checkInputs();
+        super._checkInputs();
+        if (this.lockedTarget) {
+            this._follow(this.lockedTarget);
         }
+    }
 
-        /**
-         * Gets the camera class name.
-         * @returns the class name
-         */
-        public getClassName(): string {
-            return "FollowCamera";
-        }
+    /**
+     * Gets the camera class name.
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "FollowCamera";
     }
+}
+
+/**
+ * Arc Rotate version of the follow camera.
+ * It still follows a Defined mesh but in an Arc Rotate Camera fashion.
+ * @see http://doc.babylonjs.com/features/cameras#follow-camera
+ */
+export class ArcFollowCamera extends TargetCamera {
+
+    private _cartesianCoordinates: Vector3 = Vector3.Zero();
 
     /**
-     * Arc Rotate version of the follow camera.
-     * It still follows a Defined mesh but in an Arc Rotate Camera fashion.
+     * Instantiates a new ArcFollowCamera
      * @see http://doc.babylonjs.com/features/cameras#follow-camera
+     * @param name Define the name of the camera
+     * @param alpha Define the rotation angle of the camera around the logitudinal axis
+     * @param beta Define the rotation angle of the camera around the elevation axis
+     * @param radius Define the radius of the camera from its target point
+     * @param target Define the target of the camera
+     * @param scene Define the scene the camera belongs to
      */
-    export class ArcFollowCamera extends TargetCamera {
-
-        private _cartesianCoordinates: Vector3 = Vector3.Zero();
-
-        /**
-         * Instantiates a new ArcFollowCamera
-         * @see http://doc.babylonjs.com/features/cameras#follow-camera
-         * @param name Define the name of the camera
-         * @param alpha Define the rotation angle of the camera around the logitudinal axis
-         * @param beta Define the rotation angle of the camera around the elevation axis
-         * @param radius Define the radius of the camera from its target point
-         * @param target Define the target of the camera
-         * @param scene Define the scene the camera belongs to
-         */
-        constructor(name: string,
-            /** The longitudinal angle of the camera */
-            public alpha: number,
-            /** The latitudinal angle of the camera */
-            public beta: number,
-            /** The radius of the camera from its target */
-            public radius: number,
-            /** Define the camera target (the messh it should follow) */
-            public target: Nullable<AbstractMesh>,
-            scene: Scene) {
-            super(name, Vector3.Zero(), scene);
-            this._follow();
-        }
+    constructor(name: string,
+        /** The longitudinal angle of the camera */
+        public alpha: number,
+        /** The latitudinal angle of the camera */
+        public beta: number,
+        /** The radius of the camera from its target */
+        public radius: number,
+        /** Define the camera target (the messh it should follow) */
+        public target: Nullable<AbstractMesh>,
+        scene: Scene) {
+        super(name, Vector3.Zero(), scene);
+        this._follow();
+    }
 
-        private _follow(): void {
-            if (!this.target) {
-                return;
-            }
-            this._cartesianCoordinates.x = this.radius * Math.cos(this.alpha) * Math.cos(this.beta);
-            this._cartesianCoordinates.y = this.radius * Math.sin(this.beta);
-            this._cartesianCoordinates.z = this.radius * Math.sin(this.alpha) * Math.cos(this.beta);
-
-            var targetPosition = this.target.getAbsolutePosition();
-            this.position = targetPosition.add(this._cartesianCoordinates);
-            this.setTarget(targetPosition);
+    private _follow(): void {
+        if (!this.target) {
+            return;
         }
+        this._cartesianCoordinates.x = this.radius * Math.cos(this.alpha) * Math.cos(this.beta);
+        this._cartesianCoordinates.y = this.radius * Math.sin(this.beta);
+        this._cartesianCoordinates.z = this.radius * Math.sin(this.alpha) * Math.cos(this.beta);
 
-        /** @hidden */
-        public _checkInputs(): void {
-            super._checkInputs();
-            this._follow();
-        }
+        var targetPosition = this.target.getAbsolutePosition();
+        this.position = targetPosition.add(this._cartesianCoordinates);
+        this.setTarget(targetPosition);
+    }
 
-        /**
-         * Returns the class name of the object.
-         * It is mostly used internally for serialization purposes.
-         */
-        public getClassName(): string {
-            return "ArcFollowCamera";
-        }
+    /** @hidden */
+    public _checkInputs(): void {
+        super._checkInputs();
+        this._follow();
+    }
+
+    /**
+     * Returns the class name of the object.
+     * It is mostly used internally for serialization purposes.
+     */
+    public getClassName(): string {
+        return "ArcFollowCamera";
     }
+}

+ 43 - 43
src/Cameras/followCameraInputsManager.ts

@@ -2,53 +2,53 @@ import { CameraInputsManager } from "./cameraInputsManager";
 import { FollowCamera } from "./followCamera";
 import { FollowCameraKeyboardMoveInput } from './Inputs/followCameraKeyboardMoveInput';
 
+/**
+ * Default Inputs manager for the FollowCamera.
+ * It groups all the default supported inputs for ease of use.
+ * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+ */
+export class FollowCameraInputsManager extends CameraInputsManager<FollowCamera> {
     /**
-     * Default Inputs manager for the FollowCamera.
-     * It groups all the default supported inputs for ease of use.
-     * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
+     * Instantiates a new FollowCameraInputsManager.
+     * @param camera Defines the camera the inputs belong to
      */
-    export class FollowCameraInputsManager extends CameraInputsManager<FollowCamera> {
-        /**
-         * Instantiates a new FollowCameraInputsManager.
-         * @param camera Defines the camera the inputs belong to
-         */
-        constructor(camera: FollowCamera) {
-            super(camera);
-        }
+    constructor(camera: FollowCamera) {
+        super(camera);
+    }
 
-        /**
-         * Add keyboard input support to the input manager.
-         * @returns the current input manager
-         */
-        public addKeyboard(): FollowCameraInputsManager {
-            this.add(new FollowCameraKeyboardMoveInput());
-            return this;
-        }
+    /**
+     * Add keyboard input support to the input manager.
+     * @returns the current input manager
+     */
+    public addKeyboard(): FollowCameraInputsManager {
+        this.add(new FollowCameraKeyboardMoveInput());
+        return this;
+    }
 
-        /**
-         * Add mouse wheel input support to the input manager.
-         * @returns the current input manager
-         */
-        public addMouseWheel(): FollowCameraInputsManager {
-            console.warn("MouseWheel support not yet implemented for FollowCamera.");
-            return this;
-        }
+    /**
+     * Add mouse wheel input support to the input manager.
+     * @returns the current input manager
+     */
+    public addMouseWheel(): FollowCameraInputsManager {
+        console.warn("MouseWheel support not yet implemented for FollowCamera.");
+        return this;
+    }
 
-        /**
-         * Add pointers input support to the input manager.
-         * @returns the current input manager
-         */
-        public addPointers(): FollowCameraInputsManager {
-            console.warn("Pointer support not yet implemented for FollowCamera.");
-            return this;
-        }
+    /**
+     * Add pointers input support to the input manager.
+     * @returns the current input manager
+     */
+    public addPointers(): FollowCameraInputsManager {
+        console.warn("Pointer support not yet implemented for FollowCamera.");
+        return this;
+    }
 
-        /**
-         * Add orientation input support to the input manager.
-         * @returns the current input manager
-         */
-        public addVRDeviceOrientation(): FollowCameraInputsManager {
-            console.warn("DeviceOrientation support not yet implemented for FollowCamera.");
-            return this;
-        }
+    /**
+     * Add orientation input support to the input manager.
+     * @returns the current input manager
+     */
+    public addVRDeviceOrientation(): FollowCameraInputsManager {
+        console.warn("DeviceOrientation support not yet implemented for FollowCamera.");
+        return this;
     }
+}

+ 0 - 0
src/Cameras/freeCamera.ts


部分文件因文件數量過多而無法顯示