Forráskód Böngészése

Completing last PR

David Catuhe 9 éve
szülő
commit
e8c76e22db

+ 1 - 1
Tools/Gulp/config.json

@@ -21,7 +21,7 @@
       "../../src/Tools/babylon.database.js",
       "../../src/Tools/babylon.tools.tga.js",
       "../../src/Tools/babylon.smartArray.js",
-      "../../src/Tools/babylon.smartCollection.js",
+      "../../src/Tools/babylon.stringDictionary.js",
       "../../src/Tools/babylon.tools.js",      
       "../../src/States/babylon.alphaCullingState.js",
       "../../src/States/babylon.depthCullingState.js",

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 20 - 20
dist/preview release/babylon.core.js


+ 401 - 349
dist/preview release/babylon.d.ts

@@ -1871,247 +1871,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class Collider {
-        radius: Vector3;
-        retry: number;
-        velocity: Vector3;
-        basePoint: Vector3;
-        epsilon: number;
-        collisionFound: boolean;
-        velocityWorldLength: number;
-        basePointWorld: Vector3;
-        velocityWorld: Vector3;
-        normalizedVelocity: Vector3;
-        initialVelocity: Vector3;
-        initialPosition: Vector3;
-        nearestDistance: number;
-        intersectionPoint: Vector3;
-        collidedMesh: AbstractMesh;
-        private _collisionPoint;
-        private _planeIntersectionPoint;
-        private _tempVector;
-        private _tempVector2;
-        private _tempVector3;
-        private _tempVector4;
-        private _edge;
-        private _baseToVertex;
-        private _destinationPoint;
-        private _slidePlaneNormal;
-        private _displacementVector;
-        _initialize(source: Vector3, dir: Vector3, e: number): void;
-        _checkPointInTriangle(point: Vector3, pa: Vector3, pb: Vector3, pc: Vector3, n: Vector3): boolean;
-        _canDoCollision(sphereCenter: Vector3, sphereRadius: number, vecMin: Vector3, vecMax: Vector3): boolean;
-        _testTriangle(faceIndex: number, trianglePlaneArray: Array<Plane>, p1: Vector3, p2: Vector3, p3: Vector3, hasMaterial: boolean): void;
-        _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: number[] | Int32Array, indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean): void;
-        _getResponse(pos: Vector3, vel: Vector3): void;
-    }
-}
-
-declare module BABYLON {
-    var CollisionWorker: string;
-    interface ICollisionCoordinator {
-        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
-        init(scene: Scene): void;
-        destroy(): void;
-        onMeshAdded(mesh: AbstractMesh): any;
-        onMeshUpdated(mesh: AbstractMesh): any;
-        onMeshRemoved(mesh: AbstractMesh): any;
-        onGeometryAdded(geometry: Geometry): any;
-        onGeometryUpdated(geometry: Geometry): any;
-        onGeometryDeleted(geometry: Geometry): any;
-    }
-    interface SerializedMesh {
-        id: string;
-        name: string;
-        uniqueId: number;
-        geometryId: string;
-        sphereCenter: Array<number>;
-        sphereRadius: number;
-        boxMinimum: Array<number>;
-        boxMaximum: Array<number>;
-        worldMatrixFromCache: any;
-        subMeshes: Array<SerializedSubMesh>;
-        checkCollisions: boolean;
-    }
-    interface SerializedSubMesh {
-        position: number;
-        verticesStart: number;
-        verticesCount: number;
-        indexStart: number;
-        indexCount: number;
-        hasMaterial: boolean;
-        sphereCenter: Array<number>;
-        sphereRadius: number;
-        boxMinimum: Array<number>;
-        boxMaximum: Array<number>;
-    }
-    interface SerializedGeometry {
-        id: string;
-        positions: Float32Array;
-        indices: Int32Array;
-        normals: Float32Array;
-    }
-    interface BabylonMessage {
-        taskType: WorkerTaskType;
-        payload: InitPayload | CollidePayload | UpdatePayload;
-    }
-    interface SerializedColliderToWorker {
-        position: Array<number>;
-        velocity: Array<number>;
-        radius: Array<number>;
-    }
-    enum WorkerTaskType {
-        INIT = 0,
-        UPDATE = 1,
-        COLLIDE = 2,
-    }
-    interface WorkerReply {
-        error: WorkerReplyType;
-        taskType: WorkerTaskType;
-        payload?: any;
-    }
-    interface CollisionReplyPayload {
-        newPosition: Array<number>;
-        collisionId: number;
-        collidedMeshUniqueId: number;
-    }
-    interface InitPayload {
-    }
-    interface CollidePayload {
-        collisionId: number;
-        collider: SerializedColliderToWorker;
-        maximumRetry: number;
-        excludedMeshUniqueId?: number;
-    }
-    interface UpdatePayload {
-        updatedMeshes: {
-            [n: number]: SerializedMesh;
-        };
-        updatedGeometries: {
-            [s: string]: SerializedGeometry;
-        };
-        removedMeshes: Array<number>;
-        removedGeometries: Array<string>;
-    }
-    enum WorkerReplyType {
-        SUCCESS = 0,
-        UNKNOWN_ERROR = 1,
-    }
-    class CollisionCoordinatorWorker implements ICollisionCoordinator {
-        private _scene;
-        private _scaledPosition;
-        private _scaledVelocity;
-        private _collisionsCallbackArray;
-        private _init;
-        private _runningUpdated;
-        private _runningCollisionTask;
-        private _worker;
-        private _addUpdateMeshesList;
-        private _addUpdateGeometriesList;
-        private _toRemoveMeshesArray;
-        private _toRemoveGeometryArray;
-        constructor();
-        static SerializeMesh: (mesh: AbstractMesh) => SerializedMesh;
-        static SerializeGeometry: (geometry: Geometry) => SerializedGeometry;
-        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
-        init(scene: Scene): void;
-        destroy(): void;
-        onMeshAdded(mesh: AbstractMesh): void;
-        onMeshUpdated: (mesh: AbstractMesh) => void;
-        onMeshRemoved(mesh: AbstractMesh): void;
-        onGeometryAdded(geometry: Geometry): void;
-        onGeometryUpdated: (geometry: Geometry) => void;
-        onGeometryDeleted(geometry: Geometry): void;
-        private _afterRender;
-        private _onMessageFromWorker;
-    }
-    class CollisionCoordinatorLegacy implements ICollisionCoordinator {
-        private _scene;
-        private _scaledPosition;
-        private _scaledVelocity;
-        private _finalPosition;
-        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
-        init(scene: Scene): void;
-        destroy(): void;
-        onMeshAdded(mesh: AbstractMesh): void;
-        onMeshUpdated(mesh: AbstractMesh): void;
-        onMeshRemoved(mesh: AbstractMesh): void;
-        onGeometryAdded(geometry: Geometry): void;
-        onGeometryUpdated(geometry: Geometry): void;
-        onGeometryDeleted(geometry: Geometry): void;
-        private _collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh?);
-    }
-}
-
-declare module BABYLON {
-    var WorkerIncluded: boolean;
-    class CollisionCache {
-        private _meshes;
-        private _geometries;
-        getMeshes(): {
-            [n: number]: SerializedMesh;
-        };
-        getGeometries(): {
-            [s: number]: SerializedGeometry;
-        };
-        getMesh(id: any): SerializedMesh;
-        addMesh(mesh: SerializedMesh): void;
-        removeMesh(uniqueId: number): void;
-        getGeometry(id: string): SerializedGeometry;
-        addGeometry(geometry: SerializedGeometry): void;
-        removeGeometry(id: string): void;
-    }
-    class CollideWorker {
-        collider: Collider;
-        private _collisionCache;
-        private finalPosition;
-        private collisionsScalingMatrix;
-        private collisionTranformationMatrix;
-        constructor(collider: Collider, _collisionCache: CollisionCache, finalPosition: Vector3);
-        collideWithWorld(position: Vector3, velocity: Vector3, maximumRetry: number, excludedMeshUniqueId?: number): void;
-        private checkCollision(mesh);
-        private processCollisionsForSubMeshes(transformMatrix, mesh);
-        private collideForSubMesh(subMesh, transformMatrix, meshGeometry);
-        private checkSubmeshCollision(subMesh);
-    }
-    interface ICollisionDetector {
-        onInit(payload: InitPayload): void;
-        onUpdate(payload: UpdatePayload): void;
-        onCollision(payload: CollidePayload): void;
-    }
-    class CollisionDetectorTransferable implements ICollisionDetector {
-        private _collisionCache;
-        onInit(payload: InitPayload): void;
-        onUpdate(payload: UpdatePayload): void;
-        onCollision(payload: CollidePayload): void;
-    }
-}
-
-declare module BABYLON {
-    class IntersectionInfo {
-        bu: number;
-        bv: number;
-        distance: number;
-        faceId: number;
-        subMeshId: number;
-        constructor(bu: number, bv: number, distance: number);
-    }
-    class PickingInfo {
-        hit: boolean;
-        distance: number;
-        pickedPoint: Vector3;
-        pickedMesh: AbstractMesh;
-        bu: number;
-        bv: number;
-        faceId: number;
-        subMeshId: number;
-        pickedSprite: Sprite;
-        getNormal(useWorldCoordinates?: boolean, useVerticesNormals?: boolean): Vector3;
-        getTextureCoordinates(): Vector2;
-    }
-}
-
-declare module BABYLON {
     class ArcRotateCamera extends TargetCamera {
         alpha: number;
         beta: number;
@@ -2506,27 +2265,268 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class TouchCamera extends FreeCamera {
-        touchAngularSensibility: number;
-        touchMoveSensibility: number;
-        constructor(name: string, position: Vector3, scene: Scene);
-        getTypeName(): string;
-        _setupInputs(): void;
+    class TouchCamera extends FreeCamera {
+        touchAngularSensibility: number;
+        touchMoveSensibility: number;
+        constructor(name: string, position: Vector3, scene: Scene);
+        getTypeName(): string;
+        _setupInputs(): void;
+    }
+}
+
+declare module BABYLON {
+    class UniversalCamera extends TouchCamera {
+        gamepadAngularSensibility: number;
+        gamepadMoveSensibility: number;
+        constructor(name: string, position: Vector3, scene: Scene);
+        getTypeName(): string;
+    }
+}
+
+declare module BABYLON {
+    class VirtualJoysticksCamera extends FreeCamera {
+        constructor(name: string, position: Vector3, scene: Scene);
+    }
+}
+
+declare module BABYLON {
+    class Collider {
+        radius: Vector3;
+        retry: number;
+        velocity: Vector3;
+        basePoint: Vector3;
+        epsilon: number;
+        collisionFound: boolean;
+        velocityWorldLength: number;
+        basePointWorld: Vector3;
+        velocityWorld: Vector3;
+        normalizedVelocity: Vector3;
+        initialVelocity: Vector3;
+        initialPosition: Vector3;
+        nearestDistance: number;
+        intersectionPoint: Vector3;
+        collidedMesh: AbstractMesh;
+        private _collisionPoint;
+        private _planeIntersectionPoint;
+        private _tempVector;
+        private _tempVector2;
+        private _tempVector3;
+        private _tempVector4;
+        private _edge;
+        private _baseToVertex;
+        private _destinationPoint;
+        private _slidePlaneNormal;
+        private _displacementVector;
+        _initialize(source: Vector3, dir: Vector3, e: number): void;
+        _checkPointInTriangle(point: Vector3, pa: Vector3, pb: Vector3, pc: Vector3, n: Vector3): boolean;
+        _canDoCollision(sphereCenter: Vector3, sphereRadius: number, vecMin: Vector3, vecMax: Vector3): boolean;
+        _testTriangle(faceIndex: number, trianglePlaneArray: Array<Plane>, p1: Vector3, p2: Vector3, p3: Vector3, hasMaterial: boolean): void;
+        _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: number[] | Int32Array, indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean): void;
+        _getResponse(pos: Vector3, vel: Vector3): void;
+    }
+}
+
+declare module BABYLON {
+    var CollisionWorker: string;
+    interface ICollisionCoordinator {
+        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
+        init(scene: Scene): void;
+        destroy(): void;
+        onMeshAdded(mesh: AbstractMesh): any;
+        onMeshUpdated(mesh: AbstractMesh): any;
+        onMeshRemoved(mesh: AbstractMesh): any;
+        onGeometryAdded(geometry: Geometry): any;
+        onGeometryUpdated(geometry: Geometry): any;
+        onGeometryDeleted(geometry: Geometry): any;
+    }
+    interface SerializedMesh {
+        id: string;
+        name: string;
+        uniqueId: number;
+        geometryId: string;
+        sphereCenter: Array<number>;
+        sphereRadius: number;
+        boxMinimum: Array<number>;
+        boxMaximum: Array<number>;
+        worldMatrixFromCache: any;
+        subMeshes: Array<SerializedSubMesh>;
+        checkCollisions: boolean;
+    }
+    interface SerializedSubMesh {
+        position: number;
+        verticesStart: number;
+        verticesCount: number;
+        indexStart: number;
+        indexCount: number;
+        hasMaterial: boolean;
+        sphereCenter: Array<number>;
+        sphereRadius: number;
+        boxMinimum: Array<number>;
+        boxMaximum: Array<number>;
+    }
+    interface SerializedGeometry {
+        id: string;
+        positions: Float32Array;
+        indices: Int32Array;
+        normals: Float32Array;
+    }
+    interface BabylonMessage {
+        taskType: WorkerTaskType;
+        payload: InitPayload | CollidePayload | UpdatePayload;
+    }
+    interface SerializedColliderToWorker {
+        position: Array<number>;
+        velocity: Array<number>;
+        radius: Array<number>;
+    }
+    enum WorkerTaskType {
+        INIT = 0,
+        UPDATE = 1,
+        COLLIDE = 2,
+    }
+    interface WorkerReply {
+        error: WorkerReplyType;
+        taskType: WorkerTaskType;
+        payload?: any;
+    }
+    interface CollisionReplyPayload {
+        newPosition: Array<number>;
+        collisionId: number;
+        collidedMeshUniqueId: number;
+    }
+    interface InitPayload {
+    }
+    interface CollidePayload {
+        collisionId: number;
+        collider: SerializedColliderToWorker;
+        maximumRetry: number;
+        excludedMeshUniqueId?: number;
+    }
+    interface UpdatePayload {
+        updatedMeshes: {
+            [n: number]: SerializedMesh;
+        };
+        updatedGeometries: {
+            [s: string]: SerializedGeometry;
+        };
+        removedMeshes: Array<number>;
+        removedGeometries: Array<string>;
+    }
+    enum WorkerReplyType {
+        SUCCESS = 0,
+        UNKNOWN_ERROR = 1,
+    }
+    class CollisionCoordinatorWorker implements ICollisionCoordinator {
+        private _scene;
+        private _scaledPosition;
+        private _scaledVelocity;
+        private _collisionsCallbackArray;
+        private _init;
+        private _runningUpdated;
+        private _runningCollisionTask;
+        private _worker;
+        private _addUpdateMeshesList;
+        private _addUpdateGeometriesList;
+        private _toRemoveMeshesArray;
+        private _toRemoveGeometryArray;
+        constructor();
+        static SerializeMesh: (mesh: AbstractMesh) => SerializedMesh;
+        static SerializeGeometry: (geometry: Geometry) => SerializedGeometry;
+        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
+        init(scene: Scene): void;
+        destroy(): void;
+        onMeshAdded(mesh: AbstractMesh): void;
+        onMeshUpdated: (mesh: AbstractMesh) => void;
+        onMeshRemoved(mesh: AbstractMesh): void;
+        onGeometryAdded(geometry: Geometry): void;
+        onGeometryUpdated: (geometry: Geometry) => void;
+        onGeometryDeleted(geometry: Geometry): void;
+        private _afterRender;
+        private _onMessageFromWorker;
+    }
+    class CollisionCoordinatorLegacy implements ICollisionCoordinator {
+        private _scene;
+        private _scaledPosition;
+        private _scaledVelocity;
+        private _finalPosition;
+        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh?: AbstractMesh) => void, collisionIndex: number): void;
+        init(scene: Scene): void;
+        destroy(): void;
+        onMeshAdded(mesh: AbstractMesh): void;
+        onMeshUpdated(mesh: AbstractMesh): void;
+        onMeshRemoved(mesh: AbstractMesh): void;
+        onGeometryAdded(geometry: Geometry): void;
+        onGeometryUpdated(geometry: Geometry): void;
+        onGeometryDeleted(geometry: Geometry): void;
+        private _collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh?);
+    }
+}
+
+declare module BABYLON {
+    var WorkerIncluded: boolean;
+    class CollisionCache {
+        private _meshes;
+        private _geometries;
+        getMeshes(): {
+            [n: number]: SerializedMesh;
+        };
+        getGeometries(): {
+            [s: number]: SerializedGeometry;
+        };
+        getMesh(id: any): SerializedMesh;
+        addMesh(mesh: SerializedMesh): void;
+        removeMesh(uniqueId: number): void;
+        getGeometry(id: string): SerializedGeometry;
+        addGeometry(geometry: SerializedGeometry): void;
+        removeGeometry(id: string): void;
     }
-}
-
-declare module BABYLON {
-    class UniversalCamera extends TouchCamera {
-        gamepadAngularSensibility: number;
-        gamepadMoveSensibility: number;
-        constructor(name: string, position: Vector3, scene: Scene);
-        getTypeName(): string;
+    class CollideWorker {
+        collider: Collider;
+        private _collisionCache;
+        private finalPosition;
+        private collisionsScalingMatrix;
+        private collisionTranformationMatrix;
+        constructor(collider: Collider, _collisionCache: CollisionCache, finalPosition: Vector3);
+        collideWithWorld(position: Vector3, velocity: Vector3, maximumRetry: number, excludedMeshUniqueId?: number): void;
+        private checkCollision(mesh);
+        private processCollisionsForSubMeshes(transformMatrix, mesh);
+        private collideForSubMesh(subMesh, transformMatrix, meshGeometry);
+        private checkSubmeshCollision(subMesh);
+    }
+    interface ICollisionDetector {
+        onInit(payload: InitPayload): void;
+        onUpdate(payload: UpdatePayload): void;
+        onCollision(payload: CollidePayload): void;
+    }
+    class CollisionDetectorTransferable implements ICollisionDetector {
+        private _collisionCache;
+        onInit(payload: InitPayload): void;
+        onUpdate(payload: UpdatePayload): void;
+        onCollision(payload: CollidePayload): void;
     }
 }
 
 declare module BABYLON {
-    class VirtualJoysticksCamera extends FreeCamera {
-        constructor(name: string, position: Vector3, scene: Scene);
+    class IntersectionInfo {
+        bu: number;
+        bv: number;
+        distance: number;
+        faceId: number;
+        subMeshId: number;
+        constructor(bu: number, bv: number, distance: number);
+    }
+    class PickingInfo {
+        hit: boolean;
+        distance: number;
+        pickedPoint: Vector3;
+        pickedMesh: AbstractMesh;
+        bu: number;
+        bv: number;
+        faceId: number;
+        subMeshId: number;
+        pickedSprite: Sprite;
+        getNormal(useWorldCoordinates?: boolean, useVerticesNormals?: boolean): Vector3;
+        getTextureCoordinates(): Vector2;
     }
 }
 
@@ -7228,28 +7228,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class ReflectionProbe {
-        name: string;
-        private _scene;
-        private _renderTargetTexture;
-        private _projectionMatrix;
-        private _viewMatrix;
-        private _target;
-        private _add;
-        private _attachedMesh;
-        invertYAxis: boolean;
-        position: Vector3;
-        constructor(name: string, size: number, scene: Scene, generateMipMaps?: boolean);
-        refreshRate: number;
-        getScene(): Scene;
-        cubeTexture: RenderTargetTexture;
-        renderList: AbstractMesh[];
-        attachToMesh(mesh: AbstractMesh): void;
-        dispose(): void;
-    }
-}
-
-declare module BABYLON {
     class AnaglyphPostProcess extends PostProcess {
         constructor(name: string, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean);
     }
@@ -7828,6 +7806,28 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    class ReflectionProbe {
+        name: string;
+        private _scene;
+        private _renderTargetTexture;
+        private _projectionMatrix;
+        private _viewMatrix;
+        private _target;
+        private _add;
+        private _attachedMesh;
+        invertYAxis: boolean;
+        position: Vector3;
+        constructor(name: string, size: number, scene: Scene, generateMipMaps?: boolean);
+        refreshRate: number;
+        getScene(): Scene;
+        cubeTexture: RenderTargetTexture;
+        renderList: AbstractMesh[];
+        attachToMesh(mesh: AbstractMesh): void;
+        dispose(): void;
+    }
+}
+
+declare module BABYLON {
     class BoundingBoxRenderer {
         frontColor: Color3;
         backColor: Color3;
@@ -8493,22 +8493,73 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class SmartCollection {
+    /**
+     * This class implement a typical dictionary using a string as key and the generic type T as value.
+     * The underlying implemetation relies on an associative array to ensure the best performances.
+     * The value can be anything including 'null' but except 'undefined'
+     */
+    class StringDictionary<T> {
+        /**
+         * Get a value based from its key
+         * @param key the given key to get the matching value from
+         * @return the value if found, otherwise undefined is returned
+         */
+        get(key: string): T;
+        /**
+         * Get a value from its key or add it if it doesn't exist.
+         * This method will ensure you that a given key/data will be present in the dictionary.
+         * @param key the given key to get the matchin value from
+         * @param factory the factory that will create the value if the key is not present in the dictionary.
+         * The factory will only be invoked if there's no data for the given key.
+         * @return the value corresponding to the key.
+         */
+        getOrAddWithFactory(key: string, factory: (key: string) => T): T;
+        /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        getOrAdd(key: string, val: T): T;
+        /**
+         * Check if there's a given key in the dictionary
+         * @param key the key to check for
+         * @return true if the key is present, false otherwise
+         */
+        contains(key: any): boolean;
+        /**
+         * Add a new key and its corresponding value
+         * @param key the key to add
+         * @param value the value corresponding to the key
+         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
+         */
+        add(key: string, value: T): boolean;
+        /**
+         * Remove a key/value from the dictionary.
+         * @param key the key to remove
+         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
+         */
+        remove(key: string): boolean;
+        /**
+         * Clear the whole content of the dictionary
+         */
+        clear(): void;
         count: number;
-        items: any;
-        private _keys;
-        private _initialCapacity;
-        constructor(capacity?: number);
-        add(key: any, item: any): number;
-        remove(key: any): number;
-        removeItemOfIndex(index: number): number;
-        indexOf(key: any): number;
-        item(key: any): any;
-        getAllKeys(): any[];
-        getKeyByIndex(index: number): any;
-        getItemByIndex(index: number): any;
-        empty(): void;
-        forEach(block: (item: any) => void): void;
+        /**
+         * Execute a callback on each key/val of the dictionary.
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute on a given key/value pair
+         */
+        forEach(callback: (key: string, val: T) => void): void;
+        /**
+         * Execute a callback on every occurence of the dictionary until it returns a valid TRes object.
+         * If the callback returns null or undefined the method will iterate to the next key/value pair
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
+         */
+        first<TRes>(callback: (key: string, val: T) => TRes): TRes;
+        private _count;
+        private _data;
     }
 }
 
@@ -8626,6 +8677,7 @@ declare module BABYLON {
         static DumpFramebuffer(width: number, height: number, engine: Engine, successCallback?: (data: string) => void): void;
         static CreateScreenshot(engine: Engine, camera: Camera, size: any, successCallback?: (data: string) => void): void;
         static ValidateXHRData(xhr: XMLHttpRequest, dataType?: number): boolean;
+        static getClassName(obj: any): string;
         private static _NoneLogLevel;
         private static _MessageLogLevel;
         private static _WarningLogLevel;
@@ -8773,6 +8825,61 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    class VRCameraMetrics {
+        hResolution: number;
+        vResolution: number;
+        hScreenSize: number;
+        vScreenSize: number;
+        vScreenCenter: number;
+        eyeToScreenDistance: number;
+        lensSeparationDistance: number;
+        interpupillaryDistance: number;
+        distortionK: number[];
+        chromaAbCorrection: number[];
+        postProcessScaleFactor: number;
+        lensCenterOffset: number;
+        compensateDistortion: boolean;
+        aspectRatio: number;
+        aspectRatioFov: number;
+        leftHMatrix: Matrix;
+        rightHMatrix: Matrix;
+        leftPreViewMatrix: Matrix;
+        rightPreViewMatrix: Matrix;
+        static GetDefault(): VRCameraMetrics;
+    }
+}
+
+declare module BABYLON {
+    class VRDeviceOrientationFreeCamera extends FreeCamera {
+        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion?: boolean);
+        getTypeName(): string;
+    }
+    class VRDeviceOrientationArcRotateCamera extends ArcRotateCamera {
+        constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene, compensateDistortion?: boolean);
+        getTypeName(): string;
+    }
+}
+
+declare var HMDVRDevice: any;
+declare var PositionSensorVRDevice: any;
+declare module BABYLON {
+    class WebVRFreeCamera extends FreeCamera {
+        _hmdDevice: any;
+        _sensorDevice: any;
+        _cacheState: any;
+        _cacheQuaternion: Quaternion;
+        _cacheRotation: Vector3;
+        _vrEnabled: boolean;
+        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion?: boolean);
+        private _getWebVRDevices(devices);
+        _checkInputs(): void;
+        attachControl(element: HTMLElement, noPreventDefault?: boolean): void;
+        detachControl(element: HTMLElement): void;
+        getTypeName(): string;
+    }
+}
+
+declare module BABYLON {
     class ArcRotateCameraGamepadInput implements ICameraInput<ArcRotateCamera> {
         camera: ArcRotateCamera;
         gamepad: Gamepad;
@@ -9002,61 +9109,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class VRCameraMetrics {
-        hResolution: number;
-        vResolution: number;
-        hScreenSize: number;
-        vScreenSize: number;
-        vScreenCenter: number;
-        eyeToScreenDistance: number;
-        lensSeparationDistance: number;
-        interpupillaryDistance: number;
-        distortionK: number[];
-        chromaAbCorrection: number[];
-        postProcessScaleFactor: number;
-        lensCenterOffset: number;
-        compensateDistortion: boolean;
-        aspectRatio: number;
-        aspectRatioFov: number;
-        leftHMatrix: Matrix;
-        rightHMatrix: Matrix;
-        leftPreViewMatrix: Matrix;
-        rightPreViewMatrix: Matrix;
-        static GetDefault(): VRCameraMetrics;
-    }
-}
-
-declare module BABYLON {
-    class VRDeviceOrientationFreeCamera extends FreeCamera {
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion?: boolean);
-        getTypeName(): string;
-    }
-    class VRDeviceOrientationArcRotateCamera extends ArcRotateCamera {
-        constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene, compensateDistortion?: boolean);
-        getTypeName(): string;
-    }
-}
-
-declare var HMDVRDevice: any;
-declare var PositionSensorVRDevice: any;
-declare module BABYLON {
-    class WebVRFreeCamera extends FreeCamera {
-        _hmdDevice: any;
-        _sensorDevice: any;
-        _cacheState: any;
-        _cacheQuaternion: Quaternion;
-        _cacheRotation: Vector3;
-        _vrEnabled: boolean;
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion?: boolean);
-        private _getWebVRDevices(devices);
-        _checkInputs(): void;
-        attachControl(element: HTMLElement, noPreventDefault?: boolean): void;
-        detachControl(element: HTMLElement): void;
-        getTypeName(): string;
-    }
-}
-
-declare module BABYLON {
     interface IOctreeContainer<T> {
         blocks: Array<OctreeBlock<T>>;
     }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 27 - 27
dist/preview release/babylon.js


+ 176 - 125
dist/preview release/babylon.max.js

@@ -4066,103 +4066,140 @@ var BABYLON;
 
 var BABYLON;
 (function (BABYLON) {
-    var SmartCollection = (function () {
-        function SmartCollection(capacity) {
-            if (capacity === void 0) { capacity = 10; }
-            this.count = 0;
-            this._initialCapacity = capacity;
-            this.items = {};
-            this._keys = new Array(this._initialCapacity);
-        }
-        SmartCollection.prototype.add = function (key, item) {
-            if (this.items[key] != undefined) {
-                return -1;
-            }
-            this.items[key] = item;
-            //literal keys are always strings, but we keep source type of key in _keys array
-            this._keys[this.count++] = key;
-            if (this.count > this._keys.length) {
-                this._keys.length *= 2;
-            }
-            return this.count;
-        };
-        SmartCollection.prototype.remove = function (key) {
-            if (this.items[key] == undefined) {
-                return -1;
+    /**
+     * This class implement a typical dictionary using a string as key and the generic type T as value.
+     * The underlying implemetation relies on an associative array to ensure the best performances.
+     * The value can be anything including 'null' but except 'undefined'
+     */
+    var StringDictionary = (function () {
+        function StringDictionary() {
+            this._count = 0;
+            this._data = {};
+        }
+        /**
+         * Get a value based from its key
+         * @param key the given key to get the matching value from
+         * @return the value if found, otherwise undefined is returned
+         */
+        StringDictionary.prototype.get = function (key) {
+            var val = this._data[key];
+            if (val !== undefined) {
+                return val;
             }
-            return this.removeItemOfIndex(this.indexOf(key));
+            return undefined;
         };
-        SmartCollection.prototype.removeItemOfIndex = function (index) {
-            if (index < this.count && index > -1) {
-                delete this.items[this._keys[index]];
-                //here, shifting by hand is better optimised than .splice
-                while (index < this.count) {
-                    this._keys[index] = this._keys[index + 1];
-                    index++;
-                }
+        /**
+         * Get a value from its key or add it if it doesn't exist.
+         * This method will ensure you that a given key/data will be present in the dictionary.
+         * @param key the given key to get the matchin value from
+         * @param factory the factory that will create the value if the key is not present in the dictionary.
+         * The factory will only be invoked if there's no data for the given key.
+         * @return the value corresponding to the key.
+         */
+        StringDictionary.prototype.getOrAddWithFactory = function (key, factory) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
             }
-            else {
-                return -1;
+            val = factory(key);
+            if (val) {
+                this.add(key, val);
             }
-            return --this.count;
+            return val;
         };
-        SmartCollection.prototype.indexOf = function (key) {
-            for (var i = 0; i !== this.count; i++) {
-                if (this._keys[i] === key) {
-                    return i;
-                }
+        /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        StringDictionary.prototype.getOrAdd = function (key, val) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
             }
-            return -1;
+            this.add(key, val);
+            return val;
         };
-        SmartCollection.prototype.item = function (key) {
-            return this.items[key];
+        /**
+         * Check if there's a given key in the dictionary
+         * @param key the key to check for
+         * @return true if the key is present, false otherwise
+         */
+        StringDictionary.prototype.contains = function (key) {
+            return this._data[key] !== undefined;
         };
-        SmartCollection.prototype.getAllKeys = function () {
-            if (this.count > 0) {
-                var keys = new Array(this.count);
-                for (var i = 0; i < this.count; i++) {
-                    keys[i] = this._keys[i];
-                }
-                return keys;
-            }
-            else {
-                return undefined;
+        /**
+         * Add a new key and its corresponding value
+         * @param key the key to add
+         * @param value the value corresponding to the key
+         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
+         */
+        StringDictionary.prototype.add = function (key, value) {
+            if (this._data[key] !== undefined) {
+                return false;
             }
+            this._data[key] = value;
+            ++this._count;
+            return true;
         };
-        SmartCollection.prototype.getKeyByIndex = function (index) {
-            if (index < this.count && index > -1) {
-                return this._keys[index];
-            }
-            else {
-                return undefined;
+        /**
+         * Remove a key/value from the dictionary.
+         * @param key the key to remove
+         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
+         */
+        StringDictionary.prototype.remove = function (key) {
+            if (this.contains(key)) {
+                delete this._data[key];
+                --this._count;
+                return true;
             }
+            return false;
         };
-        SmartCollection.prototype.getItemByIndex = function (index) {
-            if (index < this.count && index > -1) {
-                return this.items[this._keys[index]];
-            }
-            else {
-                return undefined;
-            }
+        /**
+         * Clear the whole content of the dictionary
+         */
+        StringDictionary.prototype.clear = function () {
+            this._data = {};
+            this._count = 0;
         };
-        SmartCollection.prototype.empty = function () {
-            if (this.count > 0) {
-                this.count = 0;
-                this.items = {};
-                this._keys = new Array(this._initialCapacity);
+        Object.defineProperty(StringDictionary.prototype, "count", {
+            get: function () {
+                return this._count;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Execute a callback on each key/val of the dictionary.
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute on a given key/value pair
+         */
+        StringDictionary.prototype.forEach = function (callback) {
+            for (var cur in this._data) {
+                var val = this._data[cur];
+                callback(cur, val);
             }
         };
-        SmartCollection.prototype.forEach = function (block) {
-            var key;
-            for (key in this.items) {
-                if (this.items.hasOwnProperty(key)) {
-                    block(this.items[key]);
+        /**
+         * Execute a callback on every occurence of the dictionary until it returns a valid TRes object.
+         * If the callback returns null or undefined the method will iterate to the next key/value pair
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
+         */
+        StringDictionary.prototype.first = function (callback) {
+            for (var cur in this._data) {
+                var val = this._data[cur];
+                var res = callback(cur, val);
+                if (res) {
+                    return res;
                 }
             }
+            return null;
         };
-        return SmartCollection;
+        return StringDictionary;
     })();
-    BABYLON.SmartCollection = SmartCollection;
+    BABYLON.StringDictionary = StringDictionary;
 })(BABYLON || (BABYLON = {}));
 
 var BABYLON;
@@ -4755,6 +4792,11 @@ var BABYLON;
             }
             return false;
         };
+        Tools.getClassName = function (obj) {
+            var funcNameRegex = /function (.{1,})\(/;
+            var results = (funcNameRegex).exec((obj).constructor.toString());
+            return (results && results.length > 1) ? results[1] : "";
+        };
         Object.defineProperty(Tools, "NoneLogLevel", {
             get: function () {
                 return Tools._NoneLogLevel;
@@ -12415,7 +12457,7 @@ var BABYLON;
             var _this = this;
             var engine = this.camera.getEngine();
             var cacheSoloPointer; // cache pointer object for better perf on camera rotation
-            var pointers = new BABYLON.SmartCollection();
+            var pointA, pointB;
             var previousPinchDistance = 0;
             this._pointerInput = function (p, s) {
                 var evt = p.event;
@@ -12428,8 +12470,13 @@ var BABYLON;
                     // Manage panning with right click
                     _this._isRightClick = evt.button === 2;
                     // manage pointers
-                    pointers.add(evt.pointerId, { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
-                    cacheSoloPointer = pointers.item(evt.pointerId);
+                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                    if (pointA === undefined) {
+                        pointA = cacheSoloPointer;
+                    }
+                    else if (pointB === undefined) {
+                        pointB = cacheSoloPointer;
+                    }
                     if (!noPreventDefault) {
                         evt.preventDefault();
                     }
@@ -12446,7 +12493,7 @@ var BABYLON;
                     //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
-                    pointers.empty();
+                    pointA = pointB = undefined;
                     if (!noPreventDefault) {
                         evt.preventDefault();
                     }
@@ -12455,43 +12502,46 @@ var BABYLON;
                     if (!noPreventDefault) {
                         evt.preventDefault();
                     }
-                    switch (pointers.count) {
-                        case 1:
-                            if (_this.panningSensibility !== 0 && ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) || (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
-                                _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;
-                            }
-                            cacheSoloPointer.x = evt.clientX;
-                            cacheSoloPointer.y = evt.clientY;
-                            break;
-                        case 2:
-                            //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                            pointers.item(evt.pointerId).x = evt.clientX;
-                            pointers.item(evt.pointerId).y = evt.clientY;
-                            var direction = _this.pinchInwards ? 1 : -1;
-                            var distX = pointers.getItemByIndex(0).x - pointers.getItemByIndex(1).x;
-                            var distY = pointers.getItemByIndex(0).y - pointers.getItemByIndex(1).y;
-                            var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                            if (previousPinchDistance === 0) {
-                                previousPinchDistance = pinchSquaredDistance;
-                                return;
-                            }
-                            if (pinchSquaredDistance !== previousPinchDistance) {
-                                _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (_this.pinchPrecision * ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) * direction);
-                                previousPinchDistance = pinchSquaredDistance;
-                            }
-                            break;
-                        default:
-                            if (pointers.item(evt.pointerId)) {
-                                pointers.item(evt.pointerId).x = evt.clientX;
-                                pointers.item(evt.pointerId).y = evt.clientY;
-                            }
+                    // One button down
+                    if (pointA && pointB === undefined) {
+                        if (_this.panningSensibility !== 0 &&
+                            ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) ||
+                                (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
+                            _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;
+                        }
+                        cacheSoloPointer.x = evt.clientX;
+                        cacheSoloPointer.y = evt.clientY;
+                    }
+                    else if (pointA && pointB) {
+                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull 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);
+                        if (previousPinchDistance === 0) {
+                            previousPinchDistance = pinchSquaredDistance;
+                            return;
+                        }
+                        if (pinchSquaredDistance !== previousPinchDistance) {
+                            _this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
+                                (_this.pinchPrecision *
+                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                    direction);
+                            previousPinchDistance = pinchSquaredDistance;
+                        }
                     }
                 }
             };
@@ -12504,7 +12554,7 @@ var BABYLON;
             }
             this._onLostFocus = function () {
                 //this._keys = [];
-                pointers.empty();
+                pointA = pointB = undefined;
                 previousPinchDistance = 0;
                 cacheSoloPointer = null;
             };
@@ -37654,7 +37704,7 @@ var BABYLON;
             this.reverseLeftRight = false;
             this.reverseUpDown = false;
             // collections of pointers
-            this._touches = new BABYLON.SmartCollection();
+            this._touches = new BABYLON.StringDictionary();
             this.deltaPosition = BABYLON.Vector3.Zero();
             this._joystickSensibility = 25;
             this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
@@ -37793,9 +37843,10 @@ var BABYLON;
                 }
             }
             else {
-                if (this._touches.item(e.pointerId.toString())) {
-                    this._touches.item(e.pointerId.toString()).x = e.clientX;
-                    this._touches.item(e.pointerId.toString()).y = e.clientY;
+                var data = this._touches.get(e.pointerId.toString());
+                if (data) {
+                    data.x = e.clientX;
+                    data.y = e.clientY;
                 }
             }
         };
@@ -37807,7 +37858,7 @@ var BABYLON;
                 this.pressed = false;
             }
             else {
-                var touch = this._touches.item(e.pointerId.toString());
+                var touch = this._touches.get(e.pointerId.toString());
                 if (touch) {
                     VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
                 }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 27 - 27
dist/preview release/babylon.noworker.js


+ 219 - 220
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js

@@ -1,220 +1,219 @@
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var BABYLON;
-(function (BABYLON) {
-    var eventPrefix = BABYLON.Tools.GetPointerPrefix();
-    var ArcRotateCameraPointersInput = (function () {
-        function ArcRotateCameraPointersInput() {
-            this.angularSensibilityX = 1000.0;
-            this.angularSensibilityY = 1000.0;
-            this.pinchPrecision = 6.0;
-            this.panningSensibility = 50.0;
-            this._isRightClick = false;
-            this._isCtrlPushed = false;
-            this.pinchInwards = true;
-        }
-        ArcRotateCameraPointersInput.prototype.attachControl = function (element, noPreventDefault) {
-            var _this = this;
-            var engine = this.camera.getEngine();
-            var cacheSoloPointer; // cache pointer object for better perf on camera rotation
-            var pointA, pointB;
-            var previousPinchDistance = 0;
-            this._pointerInput = function (p, s) {
-                var evt = p.event;
-                if (p.type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                    try {
-                        evt.srcElement.setPointerCapture(evt.pointerId);
-                    }
-                    catch (e) {
-                    }
-                    // Manage panning with right click
-                    _this._isRightClick = evt.button === 2;
-                    // manage pointers
-                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
-                    if (pointA === undefined) {
-                        pointA = cacheSoloPointer;
-                    }
-                    else if (pointB === undefined) {
-                        pointB = cacheSoloPointer;
-                    }
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                }
-                else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
-                    try {
-                        evt.srcElement.releasePointerCapture(evt.pointerId);
-                    }
-                    catch (e) {
-                    }
-                    cacheSoloPointer = null;
-                    previousPinchDistance = 0;
-                    //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
-                    pointA = pointB = undefined;
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                }
-                else if (p.type === BABYLON.PointerEventTypes.POINTERMOVE) {
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                    // One button down
-                    if (pointA && pointB === undefined) {
-                        if (_this.panningSensibility !== 0 &&
-                            ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) ||
-                                (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
-                            _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;
-                        }
-                        cacheSoloPointer.x = evt.clientX;
-                        cacheSoloPointer.y = evt.clientY;
-                    }
-                    else if (pointA && pointB) {
-                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull 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);
-                        if (previousPinchDistance === 0) {
-                            previousPinchDistance = pinchSquaredDistance;
-                            return;
-                        }
-                        if (pinchSquaredDistance !== previousPinchDistance) {
-                            _this.camera
-                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
-                                (_this.pinchPrecision *
-                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
-                                    direction);
-                            previousPinchDistance = pinchSquaredDistance;
-                        }
-                    }
-                }
-            };
-            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, BABYLON.PointerEventTypes.POINTERDOWN | BABYLON.PointerEventTypes.POINTERUP | BABYLON.PointerEventTypes.POINTERMOVE);
-            this._onContextMenu = function (evt) {
-                evt.preventDefault();
-            };
-            if (!this.camera._useCtrlForPanning) {
-                element.addEventListener("contextmenu", this._onContextMenu, false);
-            }
-            this._onLostFocus = function () {
-                //this._keys = [];
-                pointA = pointB = undefined;
-                previousPinchDistance = 0;
-                cacheSoloPointer = null;
-            };
-            this._onKeyDown = function (evt) {
-                _this._isCtrlPushed = evt.ctrlKey;
-            };
-            this._onKeyUp = function (evt) {
-                _this._isCtrlPushed = evt.ctrlKey;
-            };
-            this._onMouseMove = function (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;
-                _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
-                _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-            };
-            this._onGestureStart = function (e) {
-                if (window.MSGesture === undefined) {
-                    return;
-                }
-                if (!_this._MSGestureHandler) {
-                    _this._MSGestureHandler = new MSGesture();
-                    _this._MSGestureHandler.target = element;
-                }
-                _this._MSGestureHandler.addPointer(e.pointerId);
-            };
-            this._onGesture = function (e) {
-                _this.camera.radius *= e.scale;
-                if (e.preventDefault) {
-                    if (!noPreventDefault) {
-                        e.stopPropagation();
-                        e.preventDefault();
-                    }
-                }
-            };
-            element.addEventListener("mousemove", this._onMouseMove, false);
-            element.addEventListener("MSPointerDown", this._onGestureStart, false);
-            element.addEventListener("MSGestureChange", this._onGesture, false);
-            BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
-                { name: "blur", handler: this._onLostFocus }
-            ]);
-        };
-        ArcRotateCameraPointersInput.prototype.detachControl = function (element) {
-            if (element && this._observer) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
-                this._observer = null;
-                element.removeEventListener("contextmenu", this._onContextMenu);
-                element.removeEventListener("mousemove", this._onMouseMove);
-                element.removeEventListener("MSPointerDown", this._onGestureStart);
-                element.removeEventListener("MSGestureChange", this._onGesture);
-                this._isRightClick = false;
-                this._isCtrlPushed = false;
-                this.pinchInwards = true;
-                this._onKeyDown = null;
-                this._onKeyUp = null;
-                this._onMouseMove = null;
-                this._onGestureStart = null;
-                this._onGesture = null;
-                this._MSGestureHandler = null;
-                this._onLostFocus = null;
-                this._onContextMenu = null;
-            }
-            BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
-                { name: "blur", handler: this._onLostFocus }
-            ]);
-        };
-        ArcRotateCameraPointersInput.prototype.getTypeName = function () {
-            return "ArcRotateCameraPointersInput";
-        };
-        ArcRotateCameraPointersInput.prototype.getSimpleName = function () {
-            return "pointers";
-        };
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityX", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityY", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "pinchPrecision", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
-        return ArcRotateCameraPointersInput;
-    }());
-    BABYLON.ArcRotateCameraPointersInput = ArcRotateCameraPointersInput;
-    BABYLON.CameraInputTypes["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
-})(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.arcrotatecamera.input.pointers.js.map
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var eventPrefix = BABYLON.Tools.GetPointerPrefix();
+    var ArcRotateCameraPointersInput = (function () {
+        function ArcRotateCameraPointersInput() {
+            this.angularSensibilityX = 1000.0;
+            this.angularSensibilityY = 1000.0;
+            this.pinchPrecision = 6.0;
+            this.panningSensibility = 50.0;
+            this._isRightClick = false;
+            this._isCtrlPushed = false;
+            this.pinchInwards = true;
+        }
+        ArcRotateCameraPointersInput.prototype.attachControl = function (element, noPreventDefault) {
+            var _this = this;
+            var engine = this.camera.getEngine();
+            var cacheSoloPointer; // cache pointer object for better perf on camera rotation
+            var pointA, pointB;
+            var previousPinchDistance = 0;
+            this._pointerInput = function (p, s) {
+                var evt = p.event;
+                if (p.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                    try {
+                        evt.srcElement.setPointerCapture(evt.pointerId);
+                    }
+                    catch (e) {
+                    }
+                    // Manage panning with right click
+                    _this._isRightClick = evt.button === 2;
+                    // manage pointers
+                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                    if (pointA === undefined) {
+                        pointA = cacheSoloPointer;
+                    }
+                    else if (pointB === undefined) {
+                        pointB = cacheSoloPointer;
+                    }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                }
+                else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
+                    try {
+                        evt.srcElement.releasePointerCapture(evt.pointerId);
+                    }
+                    catch (e) {
+                    }
+                    cacheSoloPointer = null;
+                    previousPinchDistance = 0;
+                    //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
+                    pointA = pointB = undefined;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                }
+                else if (p.type === BABYLON.PointerEventTypes.POINTERMOVE) {
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                    // One button down
+                    if (pointA && pointB === undefined) {
+                        if (_this.panningSensibility !== 0 &&
+                            ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) ||
+                                (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
+                            _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;
+                        }
+                        cacheSoloPointer.x = evt.clientX;
+                        cacheSoloPointer.y = evt.clientY;
+                    }
+                    else if (pointA && pointB) {
+                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull 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);
+                        if (previousPinchDistance === 0) {
+                            previousPinchDistance = pinchSquaredDistance;
+                            return;
+                        }
+                        if (pinchSquaredDistance !== previousPinchDistance) {
+                            _this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
+                                (_this.pinchPrecision *
+                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                    direction);
+                            previousPinchDistance = pinchSquaredDistance;
+                        }
+                    }
+                }
+            };
+            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, BABYLON.PointerEventTypes.POINTERDOWN | BABYLON.PointerEventTypes.POINTERUP | BABYLON.PointerEventTypes.POINTERMOVE);
+            this._onContextMenu = function (evt) {
+                evt.preventDefault();
+            };
+            if (!this.camera._useCtrlForPanning) {
+                element.addEventListener("contextmenu", this._onContextMenu, false);
+            }
+            this._onLostFocus = function () {
+                //this._keys = [];
+                pointA = pointB = undefined;
+                previousPinchDistance = 0;
+                cacheSoloPointer = null;
+            };
+            this._onKeyDown = function (evt) {
+                _this._isCtrlPushed = evt.ctrlKey;
+            };
+            this._onKeyUp = function (evt) {
+                _this._isCtrlPushed = evt.ctrlKey;
+            };
+            this._onMouseMove = function (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;
+                _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
+                _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
+                if (!noPreventDefault) {
+                    evt.preventDefault();
+                }
+            };
+            this._onGestureStart = function (e) {
+                if (window.MSGesture === undefined) {
+                    return;
+                }
+                if (!_this._MSGestureHandler) {
+                    _this._MSGestureHandler = new MSGesture();
+                    _this._MSGestureHandler.target = element;
+                }
+                _this._MSGestureHandler.addPointer(e.pointerId);
+            };
+            this._onGesture = function (e) {
+                _this.camera.radius *= e.scale;
+                if (e.preventDefault) {
+                    if (!noPreventDefault) {
+                        e.stopPropagation();
+                        e.preventDefault();
+                    }
+                }
+            };
+            element.addEventListener("mousemove", this._onMouseMove, false);
+            element.addEventListener("MSPointerDown", this._onGestureStart, false);
+            element.addEventListener("MSGestureChange", this._onGesture, false);
+            BABYLON.Tools.RegisterTopRootEvents([
+                { name: "keydown", handler: this._onKeyDown },
+                { name: "keyup", handler: this._onKeyUp },
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        };
+        ArcRotateCameraPointersInput.prototype.detachControl = function (element) {
+            if (element && this._observer) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
+                element.removeEventListener("contextmenu", this._onContextMenu);
+                element.removeEventListener("mousemove", this._onMouseMove);
+                element.removeEventListener("MSPointerDown", this._onGestureStart);
+                element.removeEventListener("MSGestureChange", this._onGesture);
+                this._isRightClick = false;
+                this._isCtrlPushed = false;
+                this.pinchInwards = true;
+                this._onKeyDown = null;
+                this._onKeyUp = null;
+                this._onMouseMove = null;
+                this._onGestureStart = null;
+                this._onGesture = null;
+                this._MSGestureHandler = null;
+                this._onLostFocus = null;
+                this._onContextMenu = null;
+            }
+            BABYLON.Tools.UnregisterTopRootEvents([
+                { name: "keydown", handler: this._onKeyDown },
+                { name: "keyup", handler: this._onKeyUp },
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        };
+        ArcRotateCameraPointersInput.prototype.getTypeName = function () {
+            return "ArcRotateCameraPointersInput";
+        };
+        ArcRotateCameraPointersInput.prototype.getSimpleName = function () {
+            return "pointers";
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityX", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityY", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "pinchPrecision", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
+        return ArcRotateCameraPointersInput;
+    })();
+    BABYLON.ArcRotateCameraPointersInput = ArcRotateCameraPointersInput;
+    BABYLON.CameraInputTypes["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
+})(BABYLON || (BABYLON = {}));

+ 100 - 0
src/Tools/babylon.smartCollection.js

@@ -0,0 +1,100 @@
+var BABYLON;
+(function (BABYLON) {
+    var SmartCollection = (function () {
+        function SmartCollection(capacity) {
+            if (capacity === void 0) { capacity = 10; }
+            this.count = 0;
+            this._initialCapacity = capacity;
+            this.items = {};
+            this._keys = new Array(this._initialCapacity);
+        }
+        SmartCollection.prototype.add = function (key, item) {
+            if (this.items[key] != undefined) {
+                return -1;
+            }
+            this.items[key] = item;
+            //literal keys are always strings, but we keep source type of key in _keys array
+            this._keys[this.count++] = key;
+            if (this.count > this._keys.length) {
+                this._keys.length *= 2;
+            }
+            return this.count;
+        };
+        SmartCollection.prototype.remove = function (key) {
+            if (this.items[key] == undefined) {
+                return -1;
+            }
+            return this.removeItemOfIndex(this.indexOf(key));
+        };
+        SmartCollection.prototype.removeItemOfIndex = function (index) {
+            if (index < this.count && index > -1) {
+                delete this.items[this._keys[index]];
+                //here, shifting by hand is better optimised than .splice
+                while (index < this.count) {
+                    this._keys[index] = this._keys[index + 1];
+                    index++;
+                }
+            }
+            else {
+                return -1;
+            }
+            return --this.count;
+        };
+        SmartCollection.prototype.indexOf = function (key) {
+            for (var i = 0; i !== this.count; i++) {
+                if (this._keys[i] === key) {
+                    return i;
+                }
+            }
+            return -1;
+        };
+        SmartCollection.prototype.item = function (key) {
+            return this.items[key];
+        };
+        SmartCollection.prototype.getAllKeys = function () {
+            if (this.count > 0) {
+                var keys = new Array(this.count);
+                for (var i = 0; i < this.count; i++) {
+                    keys[i] = this._keys[i];
+                }
+                return keys;
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.getKeyByIndex = function (index) {
+            if (index < this.count && index > -1) {
+                return this._keys[index];
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.getItemByIndex = function (index) {
+            if (index < this.count && index > -1) {
+                return this.items[this._keys[index]];
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.empty = function () {
+            if (this.count > 0) {
+                this.count = 0;
+                this.items = {};
+                this._keys = new Array(this._initialCapacity);
+            }
+        };
+        SmartCollection.prototype.forEach = function (block) {
+            var key;
+            for (key in this.items) {
+                if (this.items.hasOwnProperty(key)) {
+                    block(this.items[key]);
+                }
+            }
+        };
+        return SmartCollection;
+    })();
+    BABYLON.SmartCollection = SmartCollection;
+})(BABYLON || (BABYLON = {}));

+ 137 - 138
src/Tools/babylon.stringDictionary.js

@@ -1,138 +1,137 @@
-var BABYLON;
-(function (BABYLON) {
-    /**
-     * This class implement a typical dictionary using a string as key and the generic type T as value.
-     * The underlying implemetation relies on an associative array to ensure the best performances.
-     * The value can be anything including 'null' but except 'undefined'
-     */
-    var StringDictionary = (function () {
-        function StringDictionary() {
-            this._count = 0;
-            this._data = {};
-        }
-        /**
-         * Get a value based from its key
-         * @param key the given key to get the matching value from
-         * @return the value if found, otherwise undefined is returned
-         */
-        StringDictionary.prototype.get = function (key) {
-            var val = this._data[key];
-            if (val !== undefined) {
-                return val;
-            }
-            return undefined;
-        };
-        /**
-         * Get a value from its key or add it if it doesn't exist.
-         * This method will ensure you that a given key/data will be present in the dictionary.
-         * @param key the given key to get the matchin value from
-         * @param factory the factory that will create the value if the key is not present in the dictionary.
-         * The factory will only be invoked if there's no data for the given key.
-         * @return the value corresponding to the key.
-         */
-        StringDictionary.prototype.getOrAddWithFactory = function (key, factory) {
-            var val = this.get(key);
-            if (val !== undefined) {
-                return val;
-            }
-            val = factory(key);
-            if (val) {
-                this.add(key, val);
-            }
-            return val;
-        };
-        /**
-         * Get a value from its key if present in the dictionary otherwise add it
-         * @param key the key to get the value from
-         * @param val if there's no such key/value pair in the dictionary add it with this value
-         * @return the value corresponding to the key
-         */
-        StringDictionary.prototype.getOrAdd = function (key, val) {
-            var val = this.get(key);
-            if (val !== undefined) {
-                return val;
-            }
-            this.add(key, val);
-            return val;
-        };
-        /**
-         * Check if there's a given key in the dictionary
-         * @param key the key to check for
-         * @return true if the key is present, false otherwise
-         */
-        StringDictionary.prototype.contains = function (key) {
-            return this._data[key] !== undefined;
-        };
-        /**
-         * Add a new key and its corresponding value
-         * @param key the key to add
-         * @param value the value corresponding to the key
-         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
-         */
-        StringDictionary.prototype.add = function (key, value) {
-            if (this._data[key] !== undefined) {
-                return false;
-            }
-            this._data[key] = value;
-            ++this._count;
-            return true;
-        };
-        /**
-         * Remove a key/value from the dictionary.
-         * @param key the key to remove
-         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
-         */
-        StringDictionary.prototype.remove = function (key) {
-            if (this.contains(key)) {
-                delete this._data[key];
-                --this._count;
-                return true;
-            }
-            return false;
-        };
-        /**
-         * Clear the whole content of the dictionary
-         */
-        StringDictionary.prototype.clear = function () {
-            this._data = {};
-            this._count = 0;
-        };
-        Object.defineProperty(StringDictionary.prototype, "count", {
-            get: function () {
-                return this._count;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        /**
-         * Execute a callback on each key/val of the dictionary.
-         * Note that you can remove any element in this dictionary in the callback implementation
-         * @param callback the callback to execute on a given key/value pair
-         */
-        StringDictionary.prototype.forEach = function (callback) {
-            for (var cur in this._data) {
-                var val = this._data[cur];
-                callback(cur, val);
-            }
-        };
-        /**
-         * Execute a callback on every occurence of the dictionary until it returns a valid TRes object.
-         * If the callback returns null or undefined the method will iterate to the next key/value pair
-         * Note that you can remove any element in this dictionary in the callback implementation
-         * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
-         */
-        StringDictionary.prototype.first = function (callback) {
-            for (var cur in this._data) {
-                var val = this._data[cur];
-                var res = callback(cur, val);
-                if (res) {
-                    return res;
-                }
-            }
-            return null;
-        };
-        return StringDictionary;
-    }());
-    BABYLON.StringDictionary = StringDictionary;
-})(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.stringDictionary.js.map
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * This class implement a typical dictionary using a string as key and the generic type T as value.
+     * The underlying implemetation relies on an associative array to ensure the best performances.
+     * The value can be anything including 'null' but except 'undefined'
+     */
+    var StringDictionary = (function () {
+        function StringDictionary() {
+            this._count = 0;
+            this._data = {};
+        }
+        /**
+         * Get a value based from its key
+         * @param key the given key to get the matching value from
+         * @return the value if found, otherwise undefined is returned
+         */
+        StringDictionary.prototype.get = function (key) {
+            var val = this._data[key];
+            if (val !== undefined) {
+                return val;
+            }
+            return undefined;
+        };
+        /**
+         * Get a value from its key or add it if it doesn't exist.
+         * This method will ensure you that a given key/data will be present in the dictionary.
+         * @param key the given key to get the matchin value from
+         * @param factory the factory that will create the value if the key is not present in the dictionary.
+         * The factory will only be invoked if there's no data for the given key.
+         * @return the value corresponding to the key.
+         */
+        StringDictionary.prototype.getOrAddWithFactory = function (key, factory) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+            val = factory(key);
+            if (val) {
+                this.add(key, val);
+            }
+            return val;
+        };
+        /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        StringDictionary.prototype.getOrAdd = function (key, val) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+            this.add(key, val);
+            return val;
+        };
+        /**
+         * Check if there's a given key in the dictionary
+         * @param key the key to check for
+         * @return true if the key is present, false otherwise
+         */
+        StringDictionary.prototype.contains = function (key) {
+            return this._data[key] !== undefined;
+        };
+        /**
+         * Add a new key and its corresponding value
+         * @param key the key to add
+         * @param value the value corresponding to the key
+         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
+         */
+        StringDictionary.prototype.add = function (key, value) {
+            if (this._data[key] !== undefined) {
+                return false;
+            }
+            this._data[key] = value;
+            ++this._count;
+            return true;
+        };
+        /**
+         * Remove a key/value from the dictionary.
+         * @param key the key to remove
+         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
+         */
+        StringDictionary.prototype.remove = function (key) {
+            if (this.contains(key)) {
+                delete this._data[key];
+                --this._count;
+                return true;
+            }
+            return false;
+        };
+        /**
+         * Clear the whole content of the dictionary
+         */
+        StringDictionary.prototype.clear = function () {
+            this._data = {};
+            this._count = 0;
+        };
+        Object.defineProperty(StringDictionary.prototype, "count", {
+            get: function () {
+                return this._count;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Execute a callback on each key/val of the dictionary.
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute on a given key/value pair
+         */
+        StringDictionary.prototype.forEach = function (callback) {
+            for (var cur in this._data) {
+                var val = this._data[cur];
+                callback(cur, val);
+            }
+        };
+        /**
+         * Execute a callback on every occurence of the dictionary until it returns a valid TRes object.
+         * If the callback returns null or undefined the method will iterate to the next key/value pair
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
+         */
+        StringDictionary.prototype.first = function (callback) {
+            for (var cur in this._data) {
+                var val = this._data[cur];
+                var res = callback(cur, val);
+                if (res) {
+                    return res;
+                }
+            }
+            return null;
+        };
+        return StringDictionary;
+    })();
+    BABYLON.StringDictionary = StringDictionary;
+})(BABYLON || (BABYLON = {}));

+ 5 - 0
src/Tools/babylon.tools.js

@@ -588,6 +588,11 @@ var BABYLON;
             }
             return false;
         };
+        Tools.getClassName = function (obj) {
+            var funcNameRegex = /function (.{1,})\(/;
+            var results = (funcNameRegex).exec((obj).constructor.toString());
+            return (results && results.length > 1) ? results[1] : "";
+        };
         Object.defineProperty(Tools, "NoneLogLevel", {
             get: function () {
                 return Tools._NoneLogLevel;

+ 297 - 298
src/Tools/babylon.virtualJoystick.js

@@ -1,298 +1,297 @@
-// Mainly based on these 2 articles : 
-// Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
-// & on Seb Lee-Delisle original work: http://seb.ly/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/ 
-var BABYLON;
-(function (BABYLON) {
-    (function (JoystickAxis) {
-        JoystickAxis[JoystickAxis["X"] = 0] = "X";
-        JoystickAxis[JoystickAxis["Y"] = 1] = "Y";
-        JoystickAxis[JoystickAxis["Z"] = 2] = "Z";
-    })(BABYLON.JoystickAxis || (BABYLON.JoystickAxis = {}));
-    var JoystickAxis = BABYLON.JoystickAxis;
-    var VirtualJoystick = (function () {
-        function VirtualJoystick(leftJoystick) {
-            var _this = this;
-            if (leftJoystick) {
-                this._leftJoystick = true;
-            }
-            else {
-                this._leftJoystick = false;
-            }
-            this._joystickIndex = VirtualJoystick._globalJoystickIndex;
-            VirtualJoystick._globalJoystickIndex++;
-            // By default left & right arrow keys are moving the X
-            // and up & down keys are moving the Y
-            this._axisTargetedByLeftAndRight = JoystickAxis.X;
-            this._axisTargetedByUpAndDown = JoystickAxis.Y;
-            this.reverseLeftRight = false;
-            this.reverseUpDown = false;
-            // collections of pointers
-            this._touches = new BABYLON.StringDictionary();
-            this.deltaPosition = BABYLON.Vector3.Zero();
-            this._joystickSensibility = 25;
-            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
-            this._rotationSpeed = 25;
-            this._inverseRotationSpeed = 1 / (this._rotationSpeed / 1000);
-            this._rotateOnAxisRelativeToMesh = false;
-            this._onResize = function (evt) {
-                VirtualJoystick.vjCanvasWidth = window.innerWidth;
-                VirtualJoystick.vjCanvasHeight = window.innerHeight;
-                VirtualJoystick.vjCanvas.width = VirtualJoystick.vjCanvasWidth;
-                VirtualJoystick.vjCanvas.height = VirtualJoystick.vjCanvasHeight;
-                VirtualJoystick.halfWidth = VirtualJoystick.vjCanvasWidth / 2;
-                VirtualJoystick.halfHeight = VirtualJoystick.vjCanvasHeight / 2;
-            };
-            // injecting a canvas element on top of the canvas 3D game
-            if (!VirtualJoystick.vjCanvas) {
-                window.addEventListener("resize", this._onResize, false);
-                VirtualJoystick.vjCanvas = document.createElement("canvas");
-                VirtualJoystick.vjCanvasWidth = window.innerWidth;
-                VirtualJoystick.vjCanvasHeight = window.innerHeight;
-                VirtualJoystick.vjCanvas.width = window.innerWidth;
-                VirtualJoystick.vjCanvas.height = window.innerHeight;
-                VirtualJoystick.vjCanvas.style.width = "100%";
-                VirtualJoystick.vjCanvas.style.height = "100%";
-                VirtualJoystick.vjCanvas.style.position = "absolute";
-                VirtualJoystick.vjCanvas.style.backgroundColor = "transparent";
-                VirtualJoystick.vjCanvas.style.top = "0px";
-                VirtualJoystick.vjCanvas.style.left = "0px";
-                VirtualJoystick.vjCanvas.style.zIndex = "5";
-                VirtualJoystick.vjCanvas.style.msTouchAction = "none";
-                // Support for jQuery PEP polyfill
-                VirtualJoystick.vjCanvas.setAttribute("touch-action", "none");
-                VirtualJoystick.vjCanvasContext = VirtualJoystick.vjCanvas.getContext('2d');
-                VirtualJoystick.vjCanvasContext.strokeStyle = "#ffffff";
-                VirtualJoystick.vjCanvasContext.lineWidth = 2;
-                document.body.appendChild(VirtualJoystick.vjCanvas);
-            }
-            VirtualJoystick.halfWidth = VirtualJoystick.vjCanvas.width / 2;
-            VirtualJoystick.halfHeight = VirtualJoystick.vjCanvas.height / 2;
-            this.pressed = false;
-            // default joystick color
-            this._joystickColor = "cyan";
-            this._joystickPointerID = -1;
-            // current joystick position
-            this._joystickPointerPos = new BABYLON.Vector2(0, 0);
-            this._joystickPreviousPointerPos = new BABYLON.Vector2(0, 0);
-            // origin joystick position
-            this._joystickPointerStartPos = new BABYLON.Vector2(0, 0);
-            this._deltaJoystickVector = new BABYLON.Vector2(0, 0);
-            this._onPointerDownHandlerRef = function (evt) {
-                _this._onPointerDown(evt);
-            };
-            this._onPointerMoveHandlerRef = function (evt) {
-                _this._onPointerMove(evt);
-            };
-            this._onPointerOutHandlerRef = function (evt) {
-                _this._onPointerUp(evt);
-            };
-            this._onPointerUpHandlerRef = function (evt) {
-                _this._onPointerUp(evt);
-            };
-            VirtualJoystick.vjCanvas.addEventListener('pointerdown', this._onPointerDownHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener('pointermove', this._onPointerMoveHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener('pointerup', this._onPointerUpHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener('pointerout', this._onPointerUpHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener("contextmenu", function (evt) {
-                evt.preventDefault(); // Disables system menu
-            }, false);
-            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
-        }
-        VirtualJoystick.prototype.setJoystickSensibility = function (newJoystickSensibility) {
-            this._joystickSensibility = newJoystickSensibility;
-            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
-        };
-        VirtualJoystick.prototype._onPointerDown = function (e) {
-            var positionOnScreenCondition;
-            e.preventDefault();
-            if (this._leftJoystick === true) {
-                positionOnScreenCondition = (e.clientX < VirtualJoystick.halfWidth);
-            }
-            else {
-                positionOnScreenCondition = (e.clientX > VirtualJoystick.halfWidth);
-            }
-            if (positionOnScreenCondition && this._joystickPointerID < 0) {
-                // First contact will be dedicated to the virtual joystick
-                this._joystickPointerID = e.pointerId;
-                this._joystickPointerStartPos.x = e.clientX;
-                this._joystickPointerStartPos.y = e.clientY;
-                this._joystickPointerPos = this._joystickPointerStartPos.clone();
-                this._joystickPreviousPointerPos = this._joystickPointerStartPos.clone();
-                this._deltaJoystickVector.x = 0;
-                this._deltaJoystickVector.y = 0;
-                this.pressed = true;
-                this._touches.add(e.pointerId.toString(), e);
-            }
-            else {
-                // You can only trigger the action buttons with a joystick declared
-                if (VirtualJoystick._globalJoystickIndex < 2 && this._action) {
-                    this._action();
-                    this._touches.add(e.pointerId.toString(), { x: e.clientX, y: e.clientY, prevX: e.clientX, prevY: e.clientY });
-                }
-            }
-        };
-        VirtualJoystick.prototype._onPointerMove = function (e) {
-            // If the current pointer is the one associated to the joystick (first touch contact)
-            if (this._joystickPointerID == e.pointerId) {
-                this._joystickPointerPos.x = e.clientX;
-                this._joystickPointerPos.y = e.clientY;
-                this._deltaJoystickVector = this._joystickPointerPos.clone();
-                this._deltaJoystickVector = this._deltaJoystickVector.subtract(this._joystickPointerStartPos);
-                var directionLeftRight = this.reverseLeftRight ? -1 : 1;
-                var deltaJoystickX = directionLeftRight * this._deltaJoystickVector.x / this._inversedSensibility;
-                switch (this._axisTargetedByLeftAndRight) {
-                    case JoystickAxis.X:
-                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickX));
-                        break;
-                    case JoystickAxis.Y:
-                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickX));
-                        break;
-                    case JoystickAxis.Z:
-                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickX));
-                        break;
-                }
-                var directionUpDown = this.reverseUpDown ? 1 : -1;
-                var deltaJoystickY = directionUpDown * this._deltaJoystickVector.y / this._inversedSensibility;
-                switch (this._axisTargetedByUpAndDown) {
-                    case JoystickAxis.X:
-                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickY));
-                        break;
-                    case JoystickAxis.Y:
-                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickY));
-                        break;
-                    case JoystickAxis.Z:
-                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickY));
-                        break;
-                }
-            }
-            else {
-                var data = this._touches.get(e.pointerId.toString());
-                if (data) {
-                    data.x = e.clientX;
-                    data.y = e.clientY;
-                }
-            }
-        };
-        VirtualJoystick.prototype._onPointerUp = function (e) {
-            if (this._joystickPointerID == e.pointerId) {
-                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);
-                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
-                this._joystickPointerID = -1;
-                this.pressed = false;
-            }
-            else {
-                var touch = this._touches.get(e.pointerId.toString());
-                if (touch) {
-                    VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
-                }
-            }
-            this._deltaJoystickVector.x = 0;
-            this._deltaJoystickVector.y = 0;
-            this._touches.remove(e.pointerId.toString());
-        };
-        /**
-        * Change the color of the virtual joystick
-        * @param newColor a string that must be a CSS color value (like "red") or the hexa value (like "#FF0000")
-        */
-        VirtualJoystick.prototype.setJoystickColor = function (newColor) {
-            this._joystickColor = newColor;
-        };
-        VirtualJoystick.prototype.setActionOnTouch = function (action) {
-            this._action = action;
-        };
-        // Define which axis you'd like to control for left & right 
-        VirtualJoystick.prototype.setAxisForLeftRight = function (axis) {
-            switch (axis) {
-                case JoystickAxis.X:
-                case JoystickAxis.Y:
-                case JoystickAxis.Z:
-                    this._axisTargetedByLeftAndRight = axis;
-                    break;
-                default:
-                    this._axisTargetedByLeftAndRight = JoystickAxis.X;
-                    break;
-            }
-        };
-        // Define which axis you'd like to control for up & down 
-        VirtualJoystick.prototype.setAxisForUpDown = function (axis) {
-            switch (axis) {
-                case JoystickAxis.X:
-                case JoystickAxis.Y:
-                case JoystickAxis.Z:
-                    this._axisTargetedByUpAndDown = axis;
-                    break;
-                default:
-                    this._axisTargetedByUpAndDown = JoystickAxis.Y;
-                    break;
-            }
-        };
-        VirtualJoystick.prototype._clearCanvas = function () {
-            if (this._leftJoystick) {
-                VirtualJoystick.vjCanvasContext.clearRect(0, 0, VirtualJoystick.vjCanvasWidth / 2, VirtualJoystick.vjCanvasHeight);
-            }
-            else {
-                VirtualJoystick.vjCanvasContext.clearRect(VirtualJoystick.vjCanvasWidth / 2, 0, VirtualJoystick.vjCanvasWidth, VirtualJoystick.vjCanvasHeight);
-            }
-        };
-        VirtualJoystick.prototype._drawVirtualJoystick = function () {
-            var _this = this;
-            if (this.pressed) {
-                this._touches.forEach(function (touch) {
-                    if (touch.pointerId === _this._joystickPointerID) {
-                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPointerStartPos.x - 63, _this._joystickPointerStartPos.y - 63, 126, 126);
-                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPreviousPointerPos.x - 41, _this._joystickPreviousPointerPos.y - 41, 82, 82);
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
-                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
-                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 40, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
-                        VirtualJoystick.vjCanvasContext.lineWidth = 2;
-                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 60, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
-                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerPos.x, _this._joystickPointerPos.y, 40, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        _this._joystickPreviousPointerPos = _this._joystickPointerPos.clone();
-                    }
-                    else {
-                        VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.fillStyle = "white";
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.strokeStyle = "red";
-                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
-                        VirtualJoystick.vjCanvasContext.arc(touch.x, touch.y, 40, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        touch.prevX = touch.x;
-                        touch.prevY = touch.y;
-                    }
-                    ;
-                });
-            }
-            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
-        };
-        VirtualJoystick.prototype.releaseCanvas = function () {
-            if (VirtualJoystick.vjCanvas) {
-                VirtualJoystick.vjCanvas.removeEventListener('pointerdown', this._onPointerDownHandlerRef);
-                VirtualJoystick.vjCanvas.removeEventListener('pointermove', this._onPointerMoveHandlerRef);
-                VirtualJoystick.vjCanvas.removeEventListener('pointerup', this._onPointerUpHandlerRef);
-                VirtualJoystick.vjCanvas.removeEventListener('pointerout', this._onPointerUpHandlerRef);
-                window.removeEventListener("resize", this._onResize);
-                document.body.removeChild(VirtualJoystick.vjCanvas);
-                VirtualJoystick.vjCanvas = null;
-            }
-        };
-        // Used to draw the virtual joystick inside a 2D canvas on top of the WebGL rendering canvas
-        VirtualJoystick._globalJoystickIndex = 0;
-        return VirtualJoystick;
-    }());
-    BABYLON.VirtualJoystick = VirtualJoystick;
-})(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.virtualJoystick.js.map
+// Mainly based on these 2 articles : 
+// Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
+// & on Seb Lee-Delisle original work: http://seb.ly/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/ 
+var BABYLON;
+(function (BABYLON) {
+    (function (JoystickAxis) {
+        JoystickAxis[JoystickAxis["X"] = 0] = "X";
+        JoystickAxis[JoystickAxis["Y"] = 1] = "Y";
+        JoystickAxis[JoystickAxis["Z"] = 2] = "Z";
+    })(BABYLON.JoystickAxis || (BABYLON.JoystickAxis = {}));
+    var JoystickAxis = BABYLON.JoystickAxis;
+    var VirtualJoystick = (function () {
+        function VirtualJoystick(leftJoystick) {
+            var _this = this;
+            if (leftJoystick) {
+                this._leftJoystick = true;
+            }
+            else {
+                this._leftJoystick = false;
+            }
+            this._joystickIndex = VirtualJoystick._globalJoystickIndex;
+            VirtualJoystick._globalJoystickIndex++;
+            // By default left & right arrow keys are moving the X
+            // and up & down keys are moving the Y
+            this._axisTargetedByLeftAndRight = JoystickAxis.X;
+            this._axisTargetedByUpAndDown = JoystickAxis.Y;
+            this.reverseLeftRight = false;
+            this.reverseUpDown = false;
+            // collections of pointers
+            this._touches = new BABYLON.StringDictionary();
+            this.deltaPosition = BABYLON.Vector3.Zero();
+            this._joystickSensibility = 25;
+            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
+            this._rotationSpeed = 25;
+            this._inverseRotationSpeed = 1 / (this._rotationSpeed / 1000);
+            this._rotateOnAxisRelativeToMesh = false;
+            this._onResize = function (evt) {
+                VirtualJoystick.vjCanvasWidth = window.innerWidth;
+                VirtualJoystick.vjCanvasHeight = window.innerHeight;
+                VirtualJoystick.vjCanvas.width = VirtualJoystick.vjCanvasWidth;
+                VirtualJoystick.vjCanvas.height = VirtualJoystick.vjCanvasHeight;
+                VirtualJoystick.halfWidth = VirtualJoystick.vjCanvasWidth / 2;
+                VirtualJoystick.halfHeight = VirtualJoystick.vjCanvasHeight / 2;
+            };
+            // injecting a canvas element on top of the canvas 3D game
+            if (!VirtualJoystick.vjCanvas) {
+                window.addEventListener("resize", this._onResize, false);
+                VirtualJoystick.vjCanvas = document.createElement("canvas");
+                VirtualJoystick.vjCanvasWidth = window.innerWidth;
+                VirtualJoystick.vjCanvasHeight = window.innerHeight;
+                VirtualJoystick.vjCanvas.width = window.innerWidth;
+                VirtualJoystick.vjCanvas.height = window.innerHeight;
+                VirtualJoystick.vjCanvas.style.width = "100%";
+                VirtualJoystick.vjCanvas.style.height = "100%";
+                VirtualJoystick.vjCanvas.style.position = "absolute";
+                VirtualJoystick.vjCanvas.style.backgroundColor = "transparent";
+                VirtualJoystick.vjCanvas.style.top = "0px";
+                VirtualJoystick.vjCanvas.style.left = "0px";
+                VirtualJoystick.vjCanvas.style.zIndex = "5";
+                VirtualJoystick.vjCanvas.style.msTouchAction = "none";
+                // Support for jQuery PEP polyfill
+                VirtualJoystick.vjCanvas.setAttribute("touch-action", "none");
+                VirtualJoystick.vjCanvasContext = VirtualJoystick.vjCanvas.getContext('2d');
+                VirtualJoystick.vjCanvasContext.strokeStyle = "#ffffff";
+                VirtualJoystick.vjCanvasContext.lineWidth = 2;
+                document.body.appendChild(VirtualJoystick.vjCanvas);
+            }
+            VirtualJoystick.halfWidth = VirtualJoystick.vjCanvas.width / 2;
+            VirtualJoystick.halfHeight = VirtualJoystick.vjCanvas.height / 2;
+            this.pressed = false;
+            // default joystick color
+            this._joystickColor = "cyan";
+            this._joystickPointerID = -1;
+            // current joystick position
+            this._joystickPointerPos = new BABYLON.Vector2(0, 0);
+            this._joystickPreviousPointerPos = new BABYLON.Vector2(0, 0);
+            // origin joystick position
+            this._joystickPointerStartPos = new BABYLON.Vector2(0, 0);
+            this._deltaJoystickVector = new BABYLON.Vector2(0, 0);
+            this._onPointerDownHandlerRef = function (evt) {
+                _this._onPointerDown(evt);
+            };
+            this._onPointerMoveHandlerRef = function (evt) {
+                _this._onPointerMove(evt);
+            };
+            this._onPointerOutHandlerRef = function (evt) {
+                _this._onPointerUp(evt);
+            };
+            this._onPointerUpHandlerRef = function (evt) {
+                _this._onPointerUp(evt);
+            };
+            VirtualJoystick.vjCanvas.addEventListener('pointerdown', this._onPointerDownHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener('pointermove', this._onPointerMoveHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener('pointerup', this._onPointerUpHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener('pointerout', this._onPointerUpHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener("contextmenu", function (evt) {
+                evt.preventDefault(); // Disables system menu
+            }, false);
+            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
+        }
+        VirtualJoystick.prototype.setJoystickSensibility = function (newJoystickSensibility) {
+            this._joystickSensibility = newJoystickSensibility;
+            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
+        };
+        VirtualJoystick.prototype._onPointerDown = function (e) {
+            var positionOnScreenCondition;
+            e.preventDefault();
+            if (this._leftJoystick === true) {
+                positionOnScreenCondition = (e.clientX < VirtualJoystick.halfWidth);
+            }
+            else {
+                positionOnScreenCondition = (e.clientX > VirtualJoystick.halfWidth);
+            }
+            if (positionOnScreenCondition && this._joystickPointerID < 0) {
+                // First contact will be dedicated to the virtual joystick
+                this._joystickPointerID = e.pointerId;
+                this._joystickPointerStartPos.x = e.clientX;
+                this._joystickPointerStartPos.y = e.clientY;
+                this._joystickPointerPos = this._joystickPointerStartPos.clone();
+                this._joystickPreviousPointerPos = this._joystickPointerStartPos.clone();
+                this._deltaJoystickVector.x = 0;
+                this._deltaJoystickVector.y = 0;
+                this.pressed = true;
+                this._touches.add(e.pointerId.toString(), e);
+            }
+            else {
+                // You can only trigger the action buttons with a joystick declared
+                if (VirtualJoystick._globalJoystickIndex < 2 && this._action) {
+                    this._action();
+                    this._touches.add(e.pointerId.toString(), { x: e.clientX, y: e.clientY, prevX: e.clientX, prevY: e.clientY });
+                }
+            }
+        };
+        VirtualJoystick.prototype._onPointerMove = function (e) {
+            // If the current pointer is the one associated to the joystick (first touch contact)
+            if (this._joystickPointerID == e.pointerId) {
+                this._joystickPointerPos.x = e.clientX;
+                this._joystickPointerPos.y = e.clientY;
+                this._deltaJoystickVector = this._joystickPointerPos.clone();
+                this._deltaJoystickVector = this._deltaJoystickVector.subtract(this._joystickPointerStartPos);
+                var directionLeftRight = this.reverseLeftRight ? -1 : 1;
+                var deltaJoystickX = directionLeftRight * this._deltaJoystickVector.x / this._inversedSensibility;
+                switch (this._axisTargetedByLeftAndRight) {
+                    case JoystickAxis.X:
+                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickX));
+                        break;
+                    case JoystickAxis.Y:
+                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickX));
+                        break;
+                    case JoystickAxis.Z:
+                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickX));
+                        break;
+                }
+                var directionUpDown = this.reverseUpDown ? 1 : -1;
+                var deltaJoystickY = directionUpDown * this._deltaJoystickVector.y / this._inversedSensibility;
+                switch (this._axisTargetedByUpAndDown) {
+                    case JoystickAxis.X:
+                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickY));
+                        break;
+                    case JoystickAxis.Y:
+                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickY));
+                        break;
+                    case JoystickAxis.Z:
+                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickY));
+                        break;
+                }
+            }
+            else {
+                var data = this._touches.get(e.pointerId.toString());
+                if (data) {
+                    data.x = e.clientX;
+                    data.y = e.clientY;
+                }
+            }
+        };
+        VirtualJoystick.prototype._onPointerUp = function (e) {
+            if (this._joystickPointerID == e.pointerId) {
+                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);
+                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
+                this._joystickPointerID = -1;
+                this.pressed = false;
+            }
+            else {
+                var touch = this._touches.get(e.pointerId.toString());
+                if (touch) {
+                    VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
+                }
+            }
+            this._deltaJoystickVector.x = 0;
+            this._deltaJoystickVector.y = 0;
+            this._touches.remove(e.pointerId.toString());
+        };
+        /**
+        * Change the color of the virtual joystick
+        * @param newColor a string that must be a CSS color value (like "red") or the hexa value (like "#FF0000")
+        */
+        VirtualJoystick.prototype.setJoystickColor = function (newColor) {
+            this._joystickColor = newColor;
+        };
+        VirtualJoystick.prototype.setActionOnTouch = function (action) {
+            this._action = action;
+        };
+        // Define which axis you'd like to control for left & right 
+        VirtualJoystick.prototype.setAxisForLeftRight = function (axis) {
+            switch (axis) {
+                case JoystickAxis.X:
+                case JoystickAxis.Y:
+                case JoystickAxis.Z:
+                    this._axisTargetedByLeftAndRight = axis;
+                    break;
+                default:
+                    this._axisTargetedByLeftAndRight = JoystickAxis.X;
+                    break;
+            }
+        };
+        // Define which axis you'd like to control for up & down 
+        VirtualJoystick.prototype.setAxisForUpDown = function (axis) {
+            switch (axis) {
+                case JoystickAxis.X:
+                case JoystickAxis.Y:
+                case JoystickAxis.Z:
+                    this._axisTargetedByUpAndDown = axis;
+                    break;
+                default:
+                    this._axisTargetedByUpAndDown = JoystickAxis.Y;
+                    break;
+            }
+        };
+        VirtualJoystick.prototype._clearCanvas = function () {
+            if (this._leftJoystick) {
+                VirtualJoystick.vjCanvasContext.clearRect(0, 0, VirtualJoystick.vjCanvasWidth / 2, VirtualJoystick.vjCanvasHeight);
+            }
+            else {
+                VirtualJoystick.vjCanvasContext.clearRect(VirtualJoystick.vjCanvasWidth / 2, 0, VirtualJoystick.vjCanvasWidth, VirtualJoystick.vjCanvasHeight);
+            }
+        };
+        VirtualJoystick.prototype._drawVirtualJoystick = function () {
+            var _this = this;
+            if (this.pressed) {
+                this._touches.forEach(function (touch) {
+                    if (touch.pointerId === _this._joystickPointerID) {
+                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPointerStartPos.x - 63, _this._joystickPointerStartPos.y - 63, 126, 126);
+                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPreviousPointerPos.x - 41, _this._joystickPreviousPointerPos.y - 41, 82, 82);
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
+                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
+                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 40, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
+                        VirtualJoystick.vjCanvasContext.lineWidth = 2;
+                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 60, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
+                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerPos.x, _this._joystickPointerPos.y, 40, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        _this._joystickPreviousPointerPos = _this._joystickPointerPos.clone();
+                    }
+                    else {
+                        VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.fillStyle = "white";
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.strokeStyle = "red";
+                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
+                        VirtualJoystick.vjCanvasContext.arc(touch.x, touch.y, 40, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        touch.prevX = touch.x;
+                        touch.prevY = touch.y;
+                    }
+                    ;
+                });
+            }
+            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
+        };
+        VirtualJoystick.prototype.releaseCanvas = function () {
+            if (VirtualJoystick.vjCanvas) {
+                VirtualJoystick.vjCanvas.removeEventListener('pointerdown', this._onPointerDownHandlerRef);
+                VirtualJoystick.vjCanvas.removeEventListener('pointermove', this._onPointerMoveHandlerRef);
+                VirtualJoystick.vjCanvas.removeEventListener('pointerup', this._onPointerUpHandlerRef);
+                VirtualJoystick.vjCanvas.removeEventListener('pointerout', this._onPointerUpHandlerRef);
+                window.removeEventListener("resize", this._onResize);
+                document.body.removeChild(VirtualJoystick.vjCanvas);
+                VirtualJoystick.vjCanvas = null;
+            }
+        };
+        // Used to draw the virtual joystick inside a 2D canvas on top of the WebGL rendering canvas
+        VirtualJoystick._globalJoystickIndex = 0;
+        return VirtualJoystick;
+    })();
+    BABYLON.VirtualJoystick = VirtualJoystick;
+})(BABYLON || (BABYLON = {}));