浏览代码

update from upstream

nockawa 9 年之前
父节点
当前提交
5467eb3226
共有 100 个文件被更改,包括 10282 次插入6486 次删除
  1. 6 4
      Exporters/Unity 5/Unity3D2Babylon/ExporterWindow.cs
  2. 12 1
      Exporters/Unity 5/Unity3D2Babylon/SceneBuilder.cs
  3. 12 12
      Tools/Gulp/config.json
  4. 25 26
      dist/preview release/babylon.core.js
  5. 4054 3607
      dist/preview release/babylon.d.ts
  6. 37 36
      dist/preview release/babylon.js
  7. 3314 2170
      dist/preview release/babylon.max.js
  8. 37 36
      dist/preview release/babylon.noworker.js
  9. 10 4
      dist/preview release/what's new.md
  10. 1 1
      src/Actions/babylon.action.js
  11. 5 2
      src/Actions/babylon.actionManager.js
  12. 4 0
      src/Actions/babylon.actionManager.ts
  13. 4 4
      src/Actions/babylon.condition.js
  14. 18 12
      src/Actions/babylon.directActions.js
  15. 8 0
      src/Actions/babylon.directActions.ts
  16. 1 1
      src/Actions/babylon.interpolateValueAction.js
  17. 1 1
      src/Animations/babylon.animatable.js
  18. 30 0
      src/Animations/babylon.animation.js
  19. 27 0
      src/Animations/babylon.animation.ts
  20. 13 13
      src/Animations/babylon.easing.js
  21. 1 1
      src/Audio/babylon.analyser.js
  22. 1 1
      src/Audio/babylon.audioEngine.js
  23. 1 1
      src/Audio/babylon.sound.js
  24. 1 1
      src/Audio/babylon.soundtrack.js
  25. 1 1
      src/Bones/babylon.bone.js
  26. 1 1
      src/Bones/babylon.skeleton.js
  27. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.gamepad.js
  28. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js
  29. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.mousewheel.js
  30. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js
  31. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js
  32. 41 55
      src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.js
  33. 39 54
      src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.ts
  34. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.gamepad.js
  35. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.keyboard.js
  36. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.mouse.js
  37. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.touch.js
  38. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.virtualjoystick.js
  39. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.js
  40. 1 1
      src/Cameras/VR/babylon.vrCameraMetrics.js
  41. 4 3
      src/Cameras/VR/babylon.vrDeviceOrientationCamera.js
  42. 3 1
      src/Cameras/VR/babylon.vrDeviceOrientationCamera.ts
  43. 1 1
      src/Cameras/VR/babylon.webVRCamera.js
  44. 1 1
      src/Cameras/babylon.arcRotateCamera.js
  45. 1 1
      src/Cameras/babylon.arcRotateCameraInputsManager.js
  46. 1 1
      src/Cameras/babylon.camera.js
  47. 1 1
      src/Cameras/babylon.cameraInputsManager.js
  48. 3 13
      src/Cameras/babylon.deviceOrientationCamera.js
  49. 2 12
      src/Cameras/babylon.deviceOrientationCamera.ts
  50. 2 2
      src/Cameras/babylon.followCamera.js
  51. 1 1
      src/Cameras/babylon.freeCamera.js
  52. 1 1
      src/Cameras/babylon.freeCameraInputsManager.js
  53. 1 1
      src/Cameras/babylon.gamepadCamera.js
  54. 8 8
      src/Cameras/babylon.stereoscopicCameras.js
  55. 30 19
      src/Cameras/babylon.targetCamera.js
  56. 43 26
      src/Cameras/babylon.targetCamera.ts
  57. 1 1
      src/Cameras/babylon.touchCamera.js
  58. 1 1
      src/Cameras/babylon.universalCamera.js
  59. 1 1
      src/Cameras/babylon.virtualJoysticksCamera.js
  60. 43 28
      src/Canvas2d/babylon.bounding2d.js
  61. 40 26
      src/Canvas2d/babylon.bounding2d.ts
  62. 5 5
      src/Canvas2d/babylon.brushes2d.js
  63. 3 3
      src/Canvas2d/babylon.brushes2d.ts
  64. 409 43
      src/Canvas2d/babylon.canvas2d.js
  65. 487 36
      src/Canvas2d/babylon.canvas2d.ts
  66. 13 5
      src/Canvas2d/babylon.group2d.js
  67. 9 2
      src/Canvas2d/babylon.group2d.ts
  68. 2 2
      src/Canvas2d/babylon.modelRenderCache.js
  69. 429 23
      src/Canvas2d/babylon.prim2dBase.js
  70. 563 25
      src/Canvas2d/babylon.prim2dBase.ts
  71. 20 4
      src/Canvas2d/babylon.rectangle2d.js
  72. 16 1
      src/Canvas2d/babylon.rectangle2d.ts
  73. 46 11
      src/Canvas2d/babylon.renderablePrim2d.js
  74. 44 6
      src/Canvas2d/babylon.renderablePrim2d.ts
  75. 2 2
      src/Canvas2d/babylon.shape2d.js
  76. 90 27
      src/Canvas2d/babylon.smartPropertyPrim.js
  77. 102 25
      src/Canvas2d/babylon.smartPropertyPrim.ts
  78. 22 4
      src/Canvas2d/babylon.sprite2d.js
  79. 19 1
      src/Canvas2d/babylon.sprite2d.ts
  80. 36 17
      src/Canvas2d/babylon.text2d.js
  81. 37 16
      src/Canvas2d/babylon.text2d.ts
  82. 1 1
      src/Canvas2d/babylon.worldSpaceCanvas2d.js
  83. 1 1
      src/Collisions/babylon.collider.js
  84. 2 2
      src/Collisions/babylon.collisionCoordinator.js
  85. 3 3
      src/Collisions/babylon.collisionWorker.js
  86. 2 2
      src/Collisions/babylon.pickingInfo.js
  87. 1 1
      src/Culling/Octrees/babylon.octree.js
  88. 1 1
      src/Culling/Octrees/babylon.octreeBlock.js
  89. 1 1
      src/Culling/babylon.boundingBox.js
  90. 1 1
      src/Culling/babylon.boundingInfo.js
  91. 1 1
      src/Culling/babylon.boundingSphere.js
  92. 1 1
      src/Culling/babylon.ray.js
  93. 1 1
      src/Debug/babylon.debugLayer.js
  94. 1 1
      src/Debug/babylon.skeletonViewer.js
  95. 1 1
      src/Layer/babylon.layer.js
  96. 1 1
      src/LensFlare/babylon.lensFlare.js
  97. 1 1
      src/LensFlare/babylon.lensFlareSystem.js
  98. 1 1
      src/Lights/Shadows/babylon.shadowGenerator.js
  99. 1 1
      src/Lights/babylon.directionalLight.js
  100. 0 0
      src/Lights/babylon.hemisphericLight.js

+ 6 - 4
Exporters/Unity 5/Unity3D2Babylon/ExporterWindow.cs

@@ -2,11 +2,10 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
-using JsonFx;
 using UnityEditor;
 using UnityEditor;
 using UnityEngine;
 using UnityEngine;
 using JsonFx.Json;
 using JsonFx.Json;
-using UnityEditor.SceneManagement;
+using UnityEngine.SceneManagement;
 
 
 namespace Unity3D2Babylon
 namespace Unity3D2Babylon
 {
 {
@@ -78,6 +77,9 @@ namespace Unity3D2Babylon
             EditorGUILayout.Space();
             EditorGUILayout.Space();
             GUILayout.Label("Physics options", EditorStyles.boldLabel);
             GUILayout.Label("Physics options", EditorStyles.boldLabel);
             exportationOptions.ExportPhysics = EditorGUILayout.Toggle("Physics", exportationOptions.ExportPhysics);
             exportationOptions.ExportPhysics = EditorGUILayout.Toggle("Physics", exportationOptions.ExportPhysics);
+            EditorGUILayout.Space();
+            GUILayout.Label("Shadows options", EditorStyles.boldLabel);
+            exportationOptions.ExportShadows = EditorGUILayout.Toggle("Shadows", exportationOptions.ExportShadows);
 
 
             EditorGUILayout.Space();
             EditorGUILayout.Space();
             EditorGUILayout.Space();
             EditorGUILayout.Space();
@@ -114,7 +116,7 @@ namespace Unity3D2Babylon
         {
         {
             try
             try
             {
             {
-                string sceneName = EditorSceneManager.GetActiveScene().name;
+                string sceneName = SceneManager.GetActiveScene().name;
 
 
                 exportationOptions.DefaultFolder = EditorUtility.SaveFolderPanel("Please select a folder", exportationOptions.DefaultFolder, "");
                 exportationOptions.DefaultFolder = EditorUtility.SaveFolderPanel("Please select a folder", exportationOptions.DefaultFolder, "");
 
 
@@ -141,7 +143,7 @@ namespace Unity3D2Babylon
                 var outputFile = sceneBuilder.WriteToBabylonFile();
                 var outputFile = sceneBuilder.WriteToBabylonFile();
 
 
                 watch.Stop();
                 watch.Stop();
-                ReportProgress(1, string.Format("Exportation done in {0:0.00}s", watch.Elapsed.TotalSeconds));
+                ReportProgress(1, $"Exportation done in {watch.Elapsed.TotalSeconds:0.00}s");
                 EditorUtility.ClearProgressBar();
                 EditorUtility.ClearProgressBar();
 
 
                 sceneBuilder.GenerateStatus(logs);
                 sceneBuilder.GenerateStatus(logs);

+ 12 - 1
Exporters/Unity 5/Unity3D2Babylon/SceneBuilder.cs

@@ -38,6 +38,14 @@ namespace Unity3D2Babylon
 
 
             babylonScene = new BabylonScene(OutputPath);
             babylonScene = new BabylonScene(OutputPath);
 
 
+            babylonScene.producer = new BabylonProducer
+            {
+                file = Path.GetFileName(outputPath),
+                version = "Unity3D",
+                name = SceneName,
+                exporter_version = "0.8"
+            };
+
             this.exportationOptions = exportationOptions;
             this.exportationOptions = exportationOptions;
         }
         }
 
 
@@ -47,7 +55,10 @@ namespace Unity3D2Babylon
 
 
             var outputFile = Path.Combine(OutputPath, SceneName + ".babylon");
             var outputFile = Path.Combine(OutputPath, SceneName + ".babylon");
 
 
-            var jsWriter = new JsonWriter(new DataWriterSettings(new DataContractResolverStrategy()));
+            var settings = new DataWriterSettings(new DataContractResolverStrategy()) {PrettyPrint = true};
+
+            var jsWriter = new JsonWriter(settings);
+
             string babylonJSformat = jsWriter.Write(babylonScene);
             string babylonJSformat = jsWriter.Write(babylonScene);
             using (var sw = new StreamWriter(outputFile))
             using (var sw = new StreamWriter(outputFile))
             {
             {

+ 12 - 12
Tools/Gulp/config.json

@@ -43,18 +43,18 @@
       "../../src/Collisions/babylon.collider.js",
       "../../src/Collisions/babylon.collider.js",
       "../../src/Collisions/babylon.collisionCoordinator.js",
       "../../src/Collisions/babylon.collisionCoordinator.js",
       "../../src/Cameras/babylon.camera.js",
       "../../src/Cameras/babylon.camera.js",
-      "../../src/cameras/babylon.camerainputsmanager.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.mouse.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.keyboard.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.touch.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.deviceorientation.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.vrdeviceorientation.js",
-      "../../src/cameras/inputs/babylon.freecamera.input.gamepad.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.keyboard.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.mousewheel.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.pointers.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.gamepad.js",
-      "../../src/cameras/inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js",
+      "../../src/Cameras/babylon.camerainputsmanager.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.mouse.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.keyboard.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.touch.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.deviceorientation.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.vrdeviceorientation.js",
+      "../../src/Cameras/inputs/babylon.freecamera.input.gamepad.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.keyboard.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.mousewheel.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.pointers.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.gamepad.js",
+      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js",
       "../../src/Cameras/babylon.targetCamera.js",
       "../../src/Cameras/babylon.targetCamera.js",
       "../../src/Cameras/babylon.freeCamera.js",
       "../../src/Cameras/babylon.freeCamera.js",
       "../../src/Cameras/babylon.freeCameraInputsManager.js",
       "../../src/Cameras/babylon.freeCameraInputsManager.js",

文件差异内容过多而无法显示
+ 25 - 26
dist/preview release/babylon.core.js


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


文件差异内容过多而无法显示
+ 37 - 36
dist/preview release/babylon.js


文件差异内容过多而无法显示
+ 3314 - 2170
dist/preview release/babylon.max.js


文件差异内容过多而无法显示
+ 37 - 36
dist/preview release/babylon.noworker.js


+ 10 - 4
dist/preview release/what's new.md

@@ -5,7 +5,7 @@
     - Support for shaders includes ([deltakosh](https://github.com/deltakosh))
     - Support for shaders includes ([deltakosh](https://github.com/deltakosh))
     - New mesh type : `LineSystem` ([jerome](https://github.com/jbousquie))
     - New mesh type : `LineSystem` ([jerome](https://github.com/jbousquie))
     - SerializationHelper for complex classes using TypeScript decorators ([deltakosh](https://github.com/deltakosh))
     - SerializationHelper for complex classes using TypeScript decorators ([deltakosh](https://github.com/deltakosh))
-    - StandardMaterial now supports Parallax and Parallax Occlusion Mapping ([nockawa](https://github.com/nockawa))
+    - StandardMaterial now supports Parallax and Parallax Occlusion Mapping ([tutorial](http://doc.babylonjs.com/tutorials/Using_parallax_mapping)) ([nockawa](https://github.com/nockawa))
     - Animations blending. See [demo here](http://www.babylonjs-playground.com/#2BLI9T#3). More [info here](http://doc.babylonjs.com/tutorials/Animations#animation-blending) ([deltakosh](https://github.com/deltakosh))
     - Animations blending. See [demo here](http://www.babylonjs-playground.com/#2BLI9T#3). More [info here](http://doc.babylonjs.com/tutorials/Animations#animation-blending) ([deltakosh](https://github.com/deltakosh))
     - New debuger tool: SkeletonViewer. See [demo here](http://www.babylonjs-playground.com/#1BZJVJ#8) (Adam & [deltakosh](https://github.com/deltakosh))
     - New debuger tool: SkeletonViewer. See [demo here](http://www.babylonjs-playground.com/#1BZJVJ#8) (Adam & [deltakosh](https://github.com/deltakosh))
     - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance ([gleborgne](https://github.com/gleborgne))
     - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance ([gleborgne](https://github.com/gleborgne))
@@ -15,8 +15,13 @@
     - Unity3D exporter: Added support for export and run (local webserver) ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Unity3D exporter: Added support for export and run (local webserver) ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Moved PBR Material to core ([deltakosh](https://github.com/deltakosh))
     - Moved PBR Material to core ([deltakosh](https://github.com/deltakosh))
     - StandardMaterial.maxSimultaneousLights can define how many dynamic lights the material can handle ([deltakosh](https://github.com/deltakosh))
     - StandardMaterial.maxSimultaneousLights can define how many dynamic lights the material can handle ([deltakosh](https://github.com/deltakosh))
+	  - Introduced Canvas2D feature: a 2D engine to render primitives, sprites in 2D, text. Canvas2D can be displayed in Screen Space (above the 3D scene) or in World Space to be a part of the Scene. [overview](http://doc.babylonjs.com/overviews/Using_The_Canvas2D), [tutorial](http://doc.babylonjs.com/tutorials/Using_the_Canvas2D) ([nockawa](https://github.com/nockawa))
+	  - Added two new types of Texture: FontTexture and MapTexture ([quick doc](http://www.html5gamedevs.com/topic/22565-two-new-texture-types-fonttexture-and-maptexture/)) ([nockawa](https://github.com/nockawa))
+	  - Added a dynamic [2D Bin Packing Algorithm](http://stackoverflow.com/questions/8762569/how-is-2d-bin-packing-achieved-programmatically), ([more info here](http://www.html5gamedevs.com/topic/22565-two-new-texture-types-fonttexture-and-maptexture/)) ([nockawa](https://github.com/nockawa))
 	  - Introduced Canvas2D feature: a 2D engine to render primitives, sprites in 2D, text. Canvas2D can be displayed in Screen Space (above the 3D scene) or in World Space to be a part of the Scene. [overview](http://doc.babylonjs.com/overviews/Using_The_Canvas2D), [tutorial](http://doc.babylonjs.com/tutorials/Using_the_Canvas2D) ([nockawa](https://github.com/nockawa))	
 	  - Introduced Canvas2D feature: a 2D engine to render primitives, sprites in 2D, text. Canvas2D can be displayed in Screen Space (above the 3D scene) or in World Space to be a part of the Scene. [overview](http://doc.babylonjs.com/overviews/Using_The_Canvas2D), [tutorial](http://doc.babylonjs.com/tutorials/Using_the_Canvas2D) ([nockawa](https://github.com/nockawa))	
   - **Updates**
   - **Updates**
+    - Renderlists can now also be defined using predicates ([deltakosh](https://github.com/deltakosh))
+    - Added support for various normal maps conventions ([deltakosh](https://github.com/deltakosh))
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
     - Enabled other post processes to be used when also using a 3D Rig ([jcpalmer](https://github.com/Palmer-JC))
     - Enabled other post processes to be used when also using a 3D Rig ([jcpalmer](https://github.com/Palmer-JC))
     - Added skeleton.getBoneIndexByName(boneName: string) ([dad72](https://github.com/dad72))
     - Added skeleton.getBoneIndexByName(boneName: string) ([dad72](https://github.com/dad72))
@@ -27,6 +32,7 @@
     - New OnPickTrigger support for spritesManager ([deltakosh](https://github.com/deltakosh))
     - New OnPickTrigger support for spritesManager ([deltakosh](https://github.com/deltakosh))
     - New SPS method `digest()` ([jerome](https://github.com/jbousquie))    
     - New SPS method `digest()` ([jerome](https://github.com/jbousquie))    
     - New SPS property `computeBoundingBox` ([jerome](https://github.com/jbousquie))  
     - New SPS property `computeBoundingBox` ([jerome](https://github.com/jbousquie))  
+    - New SPS particle property `isVisible` ([jerome](https://github.com/jbousquie)) 
     - Added a new OnPickOut trigger fired when you release the pointer button outside of a mesh or sprite. ([deltakosh](https://github.com/deltakosh))
     - Added a new OnPickOut trigger fired when you release the pointer button outside of a mesh or sprite. ([deltakosh](https://github.com/deltakosh))
     - Added support for OnPointerOver and OnPointerOut for sprites. ([deltakosh](https://github.com/deltakosh))
     - Added support for OnPointerOver and OnPointerOut for sprites. ([deltakosh](https://github.com/deltakosh))
     - Added an optional predicate on Node.getDescendants, Node.getChildren to filter out Nodes based on a callback execution. ([nockawa](https://github.com/nockawa))
     - Added an optional predicate on Node.getDescendants, Node.getChildren to filter out Nodes based on a callback execution. ([nockawa](https://github.com/nockawa))
@@ -34,9 +40,9 @@
     - LinesMesh class now supports Intersection. Added the intersectionThreshold property to set a tolerance margin during intersection with wire lines. ([nockawa](https://github.com/nockawa))
     - LinesMesh class now supports Intersection. Added the intersectionThreshold property to set a tolerance margin during intersection with wire lines. ([nockawa](https://github.com/nockawa))
     - Geometry.boundingBias property to enlarge the boundingInfo objects ([nockawa](https://github.com/nockawa))
     - Geometry.boundingBias property to enlarge the boundingInfo objects ([nockawa](https://github.com/nockawa))
     - Tools.ExtractMinAndMax & ExtractMinAndMaxIndexed now supports an optional Bias for Extent computation.
     - Tools.ExtractMinAndMax & ExtractMinAndMaxIndexed now supports an optional Bias for Extent computation.
-	  - Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([nockawa](https://github.com/nockawa))
-	  - Added RectanglePackingMap class to fix several rectangles in a big map in the most optimal way. ([nockawa](https://github.com/nockawa))
-	  - Added DynamicFloatArray class to store float32 based elements of a given size (stride) into one big Float32Array, with allocation/free/pack operations to then access an optimal buffer that can be used to update a WebGLBuffer dynamically. ([nockawa](https://github.com/nockawa))
+	  - Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([quick dock](http://www.html5gamedevs.com/topic/22566-be-efficient-my-friend-use-stringdictionary/)) ([nockawa](https://github.com/nockawa))
+	  - Added RectanglePackingMap class to fit several rectangles in a big map in the most optimal way, dynamically. ([nockawa](https://github.com/nockawa))
+	  - Added DynamicFloatArray class to store float32 based elements of a given size (stride) into one big Float32Array, with allocation/free/pack operations to then access an optimal buffer that can be used to update a WebGLBuffer dynamically.([quick doc](http://www.html5gamedevs.com/topic/22567-dynamicfloatarray-to-the-rescue-for-efficient-instanced-array/)) ([nockawa](https://github.com/nockawa))
 	  - Scene.onPointerObservable property added to enable a unique Observable event for user input (see ArcRotateCamera inputs for examples) ([nockawa](https://github.com/nockawa))
 	  - Scene.onPointerObservable property added to enable a unique Observable event for user input (see ArcRotateCamera inputs for examples) ([nockawa](https://github.com/nockawa))
   - **Exporters**
   - **Exporters**
     - Unity exporter now support skeletons ([sebavan](https://github.com/sebavan))
     - Unity exporter now support skeletons ([sebavan](https://github.com/sebavan))

+ 1 - 1
src/Actions/babylon.action.js

@@ -126,6 +126,6 @@ var BABYLON;
             };
             };
         };
         };
         return Action;
         return Action;
-    }());
+    })();
     BABYLON.Action = Action;
     BABYLON.Action = Action;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 5 - 2
src/Actions/babylon.actionManager.js

@@ -46,8 +46,11 @@ var BABYLON;
         ActionEvent.CreateNewFromScene = function (scene, evt) {
         ActionEvent.CreateNewFromScene = function (scene, evt) {
             return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
             return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
         };
         };
+        ActionEvent.CreateNewFromPrimitive = function (prim, pointerPos, evt, additionalData) {
+            return new ActionEvent(prim, pointerPos.x, pointerPos.y, null, evt, additionalData);
+        };
         return ActionEvent;
         return ActionEvent;
-    }());
+    })();
     BABYLON.ActionEvent = ActionEvent;
     BABYLON.ActionEvent = ActionEvent;
     /**
     /**
      * Action Manager manages all events to be triggered on a given mesh or the global scene.
      * Action Manager manages all events to be triggered on a given mesh or the global scene.
@@ -519,6 +522,6 @@ var BABYLON;
         ActionManager.DragMovementThreshold = 10; // in pixels
         ActionManager.DragMovementThreshold = 10; // in pixels
         ActionManager.LongPressDelay = 500; // in milliseconds
         ActionManager.LongPressDelay = 500; // in milliseconds
         return ActionManager;
         return ActionManager;
-    }());
+    })();
     BABYLON.ActionManager = ActionManager;
     BABYLON.ActionManager = ActionManager;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 4 - 0
src/Actions/babylon.actionManager.ts

@@ -44,6 +44,10 @@
         public static CreateNewFromScene(scene: Scene, evt: Event): ActionEvent {
         public static CreateNewFromScene(scene: Scene, evt: Event): ActionEvent {
             return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
             return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
         }
         }
+
+        public static CreateNewFromPrimitive(prim: any, pointerPos: Vector2, evt?: Event, additionalData?: any): ActionEvent {
+            return new ActionEvent(prim, pointerPos.x, pointerPos.y, null, evt, additionalData);
+        }
     }
     }
 
 
     /**
     /**

+ 4 - 4
src/Actions/babylon.condition.js

@@ -29,7 +29,7 @@ var BABYLON;
             };
             };
         };
         };
         return Condition;
         return Condition;
-    }());
+    })();
     BABYLON.Condition = Condition;
     BABYLON.Condition = Condition;
     var ValueCondition = (function (_super) {
     var ValueCondition = (function (_super) {
         __extends(ValueCondition, _super);
         __extends(ValueCondition, _super);
@@ -117,7 +117,7 @@ var BABYLON;
         ValueCondition._IsGreater = 2;
         ValueCondition._IsGreater = 2;
         ValueCondition._IsLesser = 3;
         ValueCondition._IsLesser = 3;
         return ValueCondition;
         return ValueCondition;
-    }(Condition));
+    })(Condition);
     BABYLON.ValueCondition = ValueCondition;
     BABYLON.ValueCondition = ValueCondition;
     var PredicateCondition = (function (_super) {
     var PredicateCondition = (function (_super) {
         __extends(PredicateCondition, _super);
         __extends(PredicateCondition, _super);
@@ -129,7 +129,7 @@ var BABYLON;
             return this.predicate();
             return this.predicate();
         };
         };
         return PredicateCondition;
         return PredicateCondition;
-    }(Condition));
+    })(Condition);
     BABYLON.PredicateCondition = PredicateCondition;
     BABYLON.PredicateCondition = PredicateCondition;
     var StateCondition = (function (_super) {
     var StateCondition = (function (_super) {
         __extends(StateCondition, _super);
         __extends(StateCondition, _super);
@@ -152,6 +152,6 @@ var BABYLON;
             });
             });
         };
         };
         return StateCondition;
         return StateCondition;
-    }(Condition));
+    })(Condition);
     BABYLON.StateCondition = StateCondition;
     BABYLON.StateCondition = StateCondition;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 18 - 12
src/Actions/babylon.directActions.js

@@ -29,7 +29,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return SwitchBooleanAction;
         return SwitchBooleanAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.SwitchBooleanAction = SwitchBooleanAction;
     BABYLON.SwitchBooleanAction = SwitchBooleanAction;
     var SetStateAction = (function (_super) {
     var SetStateAction = (function (_super) {
         __extends(SetStateAction, _super);
         __extends(SetStateAction, _super);
@@ -51,7 +51,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return SetStateAction;
         return SetStateAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.SetStateAction = SetStateAction;
     BABYLON.SetStateAction = SetStateAction;
     var SetValueAction = (function (_super) {
     var SetValueAction = (function (_super) {
         __extends(SetValueAction, _super);
         __extends(SetValueAction, _super);
@@ -67,6 +67,9 @@ var BABYLON;
         };
         };
         SetValueAction.prototype.execute = function () {
         SetValueAction.prototype.execute = function () {
             this._effectiveTarget[this._property] = this.value;
             this._effectiveTarget[this._property] = this.value;
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this._property);
+            }
         };
         };
         SetValueAction.prototype.serialize = function (parent) {
         SetValueAction.prototype.serialize = function (parent) {
             return _super.prototype._serialize.call(this, {
             return _super.prototype._serialize.call(this, {
@@ -79,7 +82,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return SetValueAction;
         return SetValueAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.SetValueAction = SetValueAction;
     BABYLON.SetValueAction = SetValueAction;
     var IncrementValueAction = (function (_super) {
     var IncrementValueAction = (function (_super) {
         __extends(IncrementValueAction, _super);
         __extends(IncrementValueAction, _super);
@@ -98,6 +101,9 @@ var BABYLON;
         };
         };
         IncrementValueAction.prototype.execute = function () {
         IncrementValueAction.prototype.execute = function () {
             this._effectiveTarget[this._property] += this.value;
             this._effectiveTarget[this._property] += this.value;
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this._property);
+            }
         };
         };
         IncrementValueAction.prototype.serialize = function (parent) {
         IncrementValueAction.prototype.serialize = function (parent) {
             return _super.prototype._serialize.call(this, {
             return _super.prototype._serialize.call(this, {
@@ -110,7 +116,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return IncrementValueAction;
         return IncrementValueAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.IncrementValueAction = IncrementValueAction;
     BABYLON.IncrementValueAction = IncrementValueAction;
     var PlayAnimationAction = (function (_super) {
     var PlayAnimationAction = (function (_super) {
         __extends(PlayAnimationAction, _super);
         __extends(PlayAnimationAction, _super);
@@ -139,7 +145,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return PlayAnimationAction;
         return PlayAnimationAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.PlayAnimationAction = PlayAnimationAction;
     BABYLON.PlayAnimationAction = PlayAnimationAction;
     var StopAnimationAction = (function (_super) {
     var StopAnimationAction = (function (_super) {
         __extends(StopAnimationAction, _super);
         __extends(StopAnimationAction, _super);
@@ -160,7 +166,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return StopAnimationAction;
         return StopAnimationAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.StopAnimationAction = StopAnimationAction;
     BABYLON.StopAnimationAction = StopAnimationAction;
     var DoNothingAction = (function (_super) {
     var DoNothingAction = (function (_super) {
         __extends(DoNothingAction, _super);
         __extends(DoNothingAction, _super);
@@ -177,7 +183,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return DoNothingAction;
         return DoNothingAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.DoNothingAction = DoNothingAction;
     BABYLON.DoNothingAction = DoNothingAction;
     var CombineAction = (function (_super) {
     var CombineAction = (function (_super) {
         __extends(CombineAction, _super);
         __extends(CombineAction, _super);
@@ -208,7 +214,7 @@ var BABYLON;
             return serializationObject;
             return serializationObject;
         };
         };
         return CombineAction;
         return CombineAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.CombineAction = CombineAction;
     BABYLON.CombineAction = CombineAction;
     var ExecuteCodeAction = (function (_super) {
     var ExecuteCodeAction = (function (_super) {
         __extends(ExecuteCodeAction, _super);
         __extends(ExecuteCodeAction, _super);
@@ -220,7 +226,7 @@ var BABYLON;
             this.func(evt);
             this.func(evt);
         };
         };
         return ExecuteCodeAction;
         return ExecuteCodeAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.ExecuteCodeAction = ExecuteCodeAction;
     BABYLON.ExecuteCodeAction = ExecuteCodeAction;
     var SetParentAction = (function (_super) {
     var SetParentAction = (function (_super) {
         __extends(SetParentAction, _super);
         __extends(SetParentAction, _super);
@@ -250,7 +256,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return SetParentAction;
         return SetParentAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.SetParentAction = SetParentAction;
     BABYLON.SetParentAction = SetParentAction;
     var PlaySoundAction = (function (_super) {
     var PlaySoundAction = (function (_super) {
         __extends(PlaySoundAction, _super);
         __extends(PlaySoundAction, _super);
@@ -271,7 +277,7 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return PlaySoundAction;
         return PlaySoundAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.PlaySoundAction = PlaySoundAction;
     BABYLON.PlaySoundAction = PlaySoundAction;
     var StopSoundAction = (function (_super) {
     var StopSoundAction = (function (_super) {
         __extends(StopSoundAction, _super);
         __extends(StopSoundAction, _super);
@@ -292,6 +298,6 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return StopSoundAction;
         return StopSoundAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.StopSoundAction = StopSoundAction;
     BABYLON.StopSoundAction = StopSoundAction;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 8 - 0
src/Actions/babylon.directActions.ts

@@ -69,6 +69,10 @@
 
 
         public execute(): void {
         public execute(): void {
             this._effectiveTarget[this._property] = this.value;
             this._effectiveTarget[this._property] = this.value;
+
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this._property);
+            }
         }
         }
         
         
         public serialize(parent: any): any {
         public serialize(parent: any): any {
@@ -104,6 +108,10 @@
 
 
         public execute(): void {
         public execute(): void {
             this._effectiveTarget[this._property] += this.value;
             this._effectiveTarget[this._property] += this.value;
+
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this._property);
+            }
         }
         }
         
         
         public serialize(parent: any): any {
         public serialize(parent: any): any {

+ 1 - 1
src/Actions/babylon.interpolateValueAction.js

@@ -72,6 +72,6 @@ var BABYLON;
             }, parent);
             }, parent);
         };
         };
         return InterpolateValueAction;
         return InterpolateValueAction;
-    }(BABYLON.Action));
+    })(BABYLON.Action);
     BABYLON.InterpolateValueAction = InterpolateValueAction;
     BABYLON.InterpolateValueAction = InterpolateValueAction;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Animations/babylon.animatable.js

@@ -127,6 +127,6 @@ var BABYLON;
             return running;
             return running;
         };
         };
         return Animatable;
         return Animatable;
-    }());
+    })();
     BABYLON.Animatable = Animatable;
     BABYLON.Animatable = Animatable;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 30 - 0
src/Animations/babylon.animation.js

@@ -120,6 +120,9 @@ var BABYLON;
             else if (from instanceof BABYLON.Color3) {
             else if (from instanceof BABYLON.Color3) {
                 dataType = Animation.ANIMATIONTYPE_COLOR3;
                 dataType = Animation.ANIMATIONTYPE_COLOR3;
             }
             }
+            else if (from instanceof BABYLON.Size) {
+                dataType = Animation.ANIMATIONTYPE_SIZE;
+            }
             if (dataType == undefined) {
             if (dataType == undefined) {
                 return null;
                 return null;
             }
             }
@@ -248,6 +251,9 @@ var BABYLON;
         Animation.prototype.vector2InterpolateFunction = function (startValue, endValue, gradient) {
         Animation.prototype.vector2InterpolateFunction = function (startValue, endValue, gradient) {
             return BABYLON.Vector2.Lerp(startValue, endValue, gradient);
             return BABYLON.Vector2.Lerp(startValue, endValue, gradient);
         };
         };
+        Animation.prototype.sizeInterpolateFunction = function (startValue, endValue, gradient) {
+            return BABYLON.Size.Lerp(startValue, endValue, gradient);
+        };
         Animation.prototype.color3InterpolateFunction = function (startValue, endValue, gradient) {
         Animation.prototype.color3InterpolateFunction = function (startValue, endValue, gradient) {
             return BABYLON.Color3.Lerp(startValue, endValue, gradient);
             return BABYLON.Color3.Lerp(startValue, endValue, gradient);
         };
         };
@@ -342,6 +348,15 @@ var BABYLON;
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:
                                     return this.vector2InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
                                     return this.vector2InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
                             }
                             }
+                        // Size
+                        case Animation.ANIMATIONTYPE_SIZE:
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient);
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                            }
                         // Color3
                         // Color3
                         case Animation.ANIMATIONTYPE_COLOR3:
                         case Animation.ANIMATIONTYPE_COLOR3:
                             switch (loopMode) {
                             switch (loopMode) {
@@ -481,6 +496,9 @@ var BABYLON;
                             // Vector2
                             // Vector2
                             case Animation.ANIMATIONTYPE_VECTOR2:
                             case Animation.ANIMATIONTYPE_VECTOR2:
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
+                            // Size
+                            case Animation.ANIMATIONTYPE_SIZE:
+                                this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                             // Color3
                             // Color3
                             case Animation.ANIMATIONTYPE_COLOR3:
                             case Animation.ANIMATIONTYPE_COLOR3:
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
@@ -511,6 +529,10 @@ var BABYLON;
                     case Animation.ANIMATIONTYPE_VECTOR2:
                     case Animation.ANIMATIONTYPE_VECTOR2:
                         offsetValue = BABYLON.Vector2.Zero();
                         offsetValue = BABYLON.Vector2.Zero();
                         break;
                         break;
+                    // Size
+                    case Animation.ANIMATIONTYPE_SIZE:
+                        offsetValue = BABYLON.Size.Zero();
+                        break;
                     // Color3
                     // Color3
                     case Animation.ANIMATIONTYPE_COLOR3:
                     case Animation.ANIMATIONTYPE_COLOR3:
                         offsetValue = BABYLON.Color3.Black();
                         offsetValue = BABYLON.Color3.Black();
@@ -604,6 +626,13 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Animation, "ANIMATIONTYPE_SIZE", {
+            get: function () {
+                return Animation._ANIMATIONTYPE_SIZE;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animation, "ANIMATIONTYPE_QUATERNION", {
         Object.defineProperty(Animation, "ANIMATIONTYPE_QUATERNION", {
             get: function () {
             get: function () {
                 return Animation._ANIMATIONTYPE_QUATERNION;
                 return Animation._ANIMATIONTYPE_QUATERNION;
@@ -702,6 +731,7 @@ var BABYLON;
         Animation._ANIMATIONTYPE_MATRIX = 3;
         Animation._ANIMATIONTYPE_MATRIX = 3;
         Animation._ANIMATIONTYPE_COLOR3 = 4;
         Animation._ANIMATIONTYPE_COLOR3 = 4;
         Animation._ANIMATIONTYPE_VECTOR2 = 5;
         Animation._ANIMATIONTYPE_VECTOR2 = 5;
+        Animation._ANIMATIONTYPE_SIZE = 6;
         Animation._ANIMATIONLOOPMODE_RELATIVE = 0;
         Animation._ANIMATIONLOOPMODE_RELATIVE = 0;
         Animation._ANIMATIONLOOPMODE_CYCLE = 1;
         Animation._ANIMATIONLOOPMODE_CYCLE = 1;
         Animation._ANIMATIONLOOPMODE_CONSTANT = 2;
         Animation._ANIMATIONLOOPMODE_CONSTANT = 2;

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

@@ -124,6 +124,8 @@
                 dataType = Animation.ANIMATIONTYPE_VECTOR2;
                 dataType = Animation.ANIMATIONTYPE_VECTOR2;
             } else if (from instanceof Color3) {
             } else if (from instanceof Color3) {
                 dataType = Animation.ANIMATIONTYPE_COLOR3;
                 dataType = Animation.ANIMATIONTYPE_COLOR3;
+            } else if (from instanceof Size) {
+                dataType = Animation.ANIMATIONTYPE_SIZE;
             }
             }
 
 
             if (dataType == undefined) {
             if (dataType == undefined) {
@@ -293,6 +295,10 @@
             return Vector2.Lerp(startValue, endValue, gradient);
             return Vector2.Lerp(startValue, endValue, gradient);
         }
         }
 
 
+        public sizeInterpolateFunction(startValue: Size, endValue: Size, gradient: number): Size {
+            return Size.Lerp(startValue, endValue, gradient);
+        }
+
         public color3InterpolateFunction(startValue: Color3, endValue: Color3, gradient: number): Color3 {
         public color3InterpolateFunction(startValue: Color3, endValue: Color3, gradient: number): Color3 {
             return Color3.Lerp(startValue, endValue, gradient);
             return Color3.Lerp(startValue, endValue, gradient);
         }
         }
@@ -405,6 +411,15 @@
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:
                                 case Animation.ANIMATIONLOOPMODE_RELATIVE:
                                     return this.vector2InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
                                     return this.vector2InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
                             }
                             }
+                        // Size
+                        case Animation.ANIMATIONTYPE_SIZE:
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient);
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                            }
                         // Color3
                         // Color3
                         case Animation.ANIMATIONTYPE_COLOR3:
                         case Animation.ANIMATIONTYPE_COLOR3:
                             switch (loopMode) {
                             switch (loopMode) {
@@ -553,6 +568,9 @@
                             // Vector2
                             // Vector2
                             case Animation.ANIMATIONTYPE_VECTOR2:
                             case Animation.ANIMATIONTYPE_VECTOR2:
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
+                            // Size
+                            case Animation.ANIMATIONTYPE_SIZE:
+                                this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                             // Color3
                             // Color3
                             case Animation.ANIMATIONTYPE_COLOR3:
                             case Animation.ANIMATIONTYPE_COLOR3:
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
                                 this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
@@ -586,6 +604,10 @@
                     case Animation.ANIMATIONTYPE_VECTOR2:
                     case Animation.ANIMATIONTYPE_VECTOR2:
                         offsetValue = Vector2.Zero();
                         offsetValue = Vector2.Zero();
                         break;
                         break;
+                    // Size
+                    case Animation.ANIMATIONTYPE_SIZE:
+                        offsetValue = Size.Zero();
+                        break;
                     // Color3
                     // Color3
                     case Animation.ANIMATIONTYPE_COLOR3:
                     case Animation.ANIMATIONTYPE_COLOR3:
                         offsetValue = Color3.Black();
                         offsetValue = Color3.Black();
@@ -676,6 +698,7 @@
         private static _ANIMATIONTYPE_MATRIX = 3;
         private static _ANIMATIONTYPE_MATRIX = 3;
         private static _ANIMATIONTYPE_COLOR3 = 4;
         private static _ANIMATIONTYPE_COLOR3 = 4;
         private static _ANIMATIONTYPE_VECTOR2 = 5;
         private static _ANIMATIONTYPE_VECTOR2 = 5;
+        private static _ANIMATIONTYPE_SIZE = 6;
         private static _ANIMATIONLOOPMODE_RELATIVE = 0;
         private static _ANIMATIONLOOPMODE_RELATIVE = 0;
         private static _ANIMATIONLOOPMODE_CYCLE = 1;
         private static _ANIMATIONLOOPMODE_CYCLE = 1;
         private static _ANIMATIONLOOPMODE_CONSTANT = 2;
         private static _ANIMATIONLOOPMODE_CONSTANT = 2;
@@ -692,6 +715,10 @@
             return Animation._ANIMATIONTYPE_VECTOR2;
             return Animation._ANIMATIONTYPE_VECTOR2;
         }
         }
 
 
+        public static get ANIMATIONTYPE_SIZE(): number {
+            return Animation._ANIMATIONTYPE_SIZE;
+        }
+
         public static get ANIMATIONTYPE_QUATERNION(): number {
         public static get ANIMATIONTYPE_QUATERNION(): number {
             return Animation._ANIMATIONTYPE_QUATERNION;
             return Animation._ANIMATIONTYPE_QUATERNION;
         }
         }

+ 13 - 13
src/Animations/babylon.easing.js

@@ -58,7 +58,7 @@ var BABYLON;
         EasingFunction._EASINGMODE_EASEOUT = 1;
         EasingFunction._EASINGMODE_EASEOUT = 1;
         EasingFunction._EASINGMODE_EASEINOUT = 2;
         EasingFunction._EASINGMODE_EASEINOUT = 2;
         return EasingFunction;
         return EasingFunction;
-    }());
+    })();
     BABYLON.EasingFunction = EasingFunction;
     BABYLON.EasingFunction = EasingFunction;
     var CircleEase = (function (_super) {
     var CircleEase = (function (_super) {
         __extends(CircleEase, _super);
         __extends(CircleEase, _super);
@@ -70,7 +70,7 @@ var BABYLON;
             return (1.0 - Math.sqrt(1.0 - (gradient * gradient)));
             return (1.0 - Math.sqrt(1.0 - (gradient * gradient)));
         };
         };
         return CircleEase;
         return CircleEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.CircleEase = CircleEase;
     BABYLON.CircleEase = CircleEase;
     var BackEase = (function (_super) {
     var BackEase = (function (_super) {
         __extends(BackEase, _super);
         __extends(BackEase, _super);
@@ -84,7 +84,7 @@ var BABYLON;
             return (Math.pow(gradient, 3.0) - ((gradient * num) * Math.sin(3.1415926535897931 * gradient)));
             return (Math.pow(gradient, 3.0) - ((gradient * num) * Math.sin(3.1415926535897931 * gradient)));
         };
         };
         return BackEase;
         return BackEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.BackEase = BackEase;
     BABYLON.BackEase = BackEase;
     var BounceEase = (function (_super) {
     var BounceEase = (function (_super) {
         __extends(BounceEase, _super);
         __extends(BounceEase, _super);
@@ -116,7 +116,7 @@ var BABYLON;
             return (((-Math.pow(1.0 / bounciness, y - num3) / (num2 * num2)) * (num6 - num2)) * (num6 + num2));
             return (((-Math.pow(1.0 / bounciness, y - num3) / (num2 * num2)) * (num6 - num2)) * (num6 + num2));
         };
         };
         return BounceEase;
         return BounceEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.BounceEase = BounceEase;
     BABYLON.BounceEase = BounceEase;
     var CubicEase = (function (_super) {
     var CubicEase = (function (_super) {
         __extends(CubicEase, _super);
         __extends(CubicEase, _super);
@@ -127,7 +127,7 @@ var BABYLON;
             return (gradient * gradient * gradient);
             return (gradient * gradient * gradient);
         };
         };
         return CubicEase;
         return CubicEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.CubicEase = CubicEase;
     BABYLON.CubicEase = CubicEase;
     var ElasticEase = (function (_super) {
     var ElasticEase = (function (_super) {
         __extends(ElasticEase, _super);
         __extends(ElasticEase, _super);
@@ -151,7 +151,7 @@ var BABYLON;
             return (num2 * Math.sin(((6.2831853071795862 * num3) + 1.5707963267948966) * gradient));
             return (num2 * Math.sin(((6.2831853071795862 * num3) + 1.5707963267948966) * gradient));
         };
         };
         return ElasticEase;
         return ElasticEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.ElasticEase = ElasticEase;
     BABYLON.ElasticEase = ElasticEase;
     var ExponentialEase = (function (_super) {
     var ExponentialEase = (function (_super) {
         __extends(ExponentialEase, _super);
         __extends(ExponentialEase, _super);
@@ -167,7 +167,7 @@ var BABYLON;
             return ((Math.exp(this.exponent * gradient) - 1.0) / (Math.exp(this.exponent) - 1.0));
             return ((Math.exp(this.exponent * gradient) - 1.0) / (Math.exp(this.exponent) - 1.0));
         };
         };
         return ExponentialEase;
         return ExponentialEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.ExponentialEase = ExponentialEase;
     BABYLON.ExponentialEase = ExponentialEase;
     var PowerEase = (function (_super) {
     var PowerEase = (function (_super) {
         __extends(PowerEase, _super);
         __extends(PowerEase, _super);
@@ -181,7 +181,7 @@ var BABYLON;
             return Math.pow(gradient, y);
             return Math.pow(gradient, y);
         };
         };
         return PowerEase;
         return PowerEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.PowerEase = PowerEase;
     BABYLON.PowerEase = PowerEase;
     var QuadraticEase = (function (_super) {
     var QuadraticEase = (function (_super) {
         __extends(QuadraticEase, _super);
         __extends(QuadraticEase, _super);
@@ -192,7 +192,7 @@ var BABYLON;
             return (gradient * gradient);
             return (gradient * gradient);
         };
         };
         return QuadraticEase;
         return QuadraticEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.QuadraticEase = QuadraticEase;
     BABYLON.QuadraticEase = QuadraticEase;
     var QuarticEase = (function (_super) {
     var QuarticEase = (function (_super) {
         __extends(QuarticEase, _super);
         __extends(QuarticEase, _super);
@@ -203,7 +203,7 @@ var BABYLON;
             return (gradient * gradient * gradient * gradient);
             return (gradient * gradient * gradient * gradient);
         };
         };
         return QuarticEase;
         return QuarticEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.QuarticEase = QuarticEase;
     BABYLON.QuarticEase = QuarticEase;
     var QuinticEase = (function (_super) {
     var QuinticEase = (function (_super) {
         __extends(QuinticEase, _super);
         __extends(QuinticEase, _super);
@@ -214,7 +214,7 @@ var BABYLON;
             return (gradient * gradient * gradient * gradient * gradient);
             return (gradient * gradient * gradient * gradient * gradient);
         };
         };
         return QuinticEase;
         return QuinticEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.QuinticEase = QuinticEase;
     BABYLON.QuinticEase = QuinticEase;
     var SineEase = (function (_super) {
     var SineEase = (function (_super) {
         __extends(SineEase, _super);
         __extends(SineEase, _super);
@@ -225,7 +225,7 @@ var BABYLON;
             return (1.0 - Math.sin(1.5707963267948966 * (1.0 - gradient)));
             return (1.0 - Math.sin(1.5707963267948966 * (1.0 - gradient)));
         };
         };
         return SineEase;
         return SineEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.SineEase = SineEase;
     BABYLON.SineEase = SineEase;
     var BezierCurveEase = (function (_super) {
     var BezierCurveEase = (function (_super) {
         __extends(BezierCurveEase, _super);
         __extends(BezierCurveEase, _super);
@@ -244,6 +244,6 @@ var BABYLON;
             return BABYLON.BezierCurve.interpolate(gradient, this.x1, this.y1, this.x2, this.y2);
             return BABYLON.BezierCurve.interpolate(gradient, this.x1, this.y1, this.x2, this.y2);
         };
         };
         return BezierCurveEase;
         return BezierCurveEase;
-    }(EasingFunction));
+    })(EasingFunction);
     BABYLON.BezierCurveEase = BezierCurveEase;
     BABYLON.BezierCurveEase = BezierCurveEase;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Audio/babylon.analyser.js

@@ -106,6 +106,6 @@ var BABYLON;
             }
             }
         };
         };
         return Analyser;
         return Analyser;
-    }());
+    })();
     BABYLON.Analyser = Analyser;
     BABYLON.Analyser = Analyser;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Audio/babylon.audioEngine.js

@@ -101,6 +101,6 @@ var BABYLON;
             }
             }
         };
         };
         return AudioEngine;
         return AudioEngine;
-    }());
+    })();
     BABYLON.AudioEngine = AudioEngine;
     BABYLON.AudioEngine = AudioEngine;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Audio/babylon.sound.js

@@ -547,6 +547,6 @@ var BABYLON;
             return newSound;
             return newSound;
         };
         };
         return Sound;
         return Sound;
-    }());
+    })();
     BABYLON.Sound = Sound;
     BABYLON.Sound = Sound;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Audio/babylon.soundtrack.js

@@ -96,6 +96,6 @@ var BABYLON;
             }
             }
         };
         };
         return SoundTrack;
         return SoundTrack;
-    }());
+    })();
     BABYLON.SoundTrack = SoundTrack;
     BABYLON.SoundTrack = SoundTrack;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Bones/babylon.bone.js

@@ -124,6 +124,6 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Bone;
         return Bone;
-    }(BABYLON.Node));
+    })(BABYLON.Node);
     BABYLON.Bone = Bone;
     BABYLON.Bone = Bone;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Bones/babylon.skeleton.js

@@ -329,6 +329,6 @@ var BABYLON;
             return skeleton;
             return skeleton;
         };
         };
         return Skeleton;
         return Skeleton;
-    }());
+    })();
     BABYLON.Skeleton = Skeleton;
     BABYLON.Skeleton = Skeleton;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.arcrotatecamera.input.gamepad.js

@@ -65,7 +65,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], ArcRotateCameraGamepadInput.prototype, "gamepadMoveSensibility", void 0);
         ], ArcRotateCameraGamepadInput.prototype, "gamepadMoveSensibility", void 0);
         return ArcRotateCameraGamepadInput;
         return ArcRotateCameraGamepadInput;
-    }());
+    })();
     BABYLON.ArcRotateCameraGamepadInput = ArcRotateCameraGamepadInput;
     BABYLON.ArcRotateCameraGamepadInput = ArcRotateCameraGamepadInput;
     BABYLON.CameraInputTypes["ArcRotateCameraGamepadInput"] = ArcRotateCameraGamepadInput;
     BABYLON.CameraInputTypes["ArcRotateCameraGamepadInput"] = ArcRotateCameraGamepadInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js

@@ -107,7 +107,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], ArcRotateCameraKeyboardMoveInput.prototype, "keysRight", void 0);
         ], ArcRotateCameraKeyboardMoveInput.prototype, "keysRight", void 0);
         return ArcRotateCameraKeyboardMoveInput;
         return ArcRotateCameraKeyboardMoveInput;
-    }());
+    })();
     BABYLON.ArcRotateCameraKeyboardMoveInput = ArcRotateCameraKeyboardMoveInput;
     BABYLON.ArcRotateCameraKeyboardMoveInput = ArcRotateCameraKeyboardMoveInput;
     BABYLON.CameraInputTypes["ArcRotateCameraKeyboardMoveInput"] = ArcRotateCameraKeyboardMoveInput;
     BABYLON.CameraInputTypes["ArcRotateCameraKeyboardMoveInput"] = ArcRotateCameraKeyboardMoveInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.arcrotatecamera.input.mousewheel.js

@@ -51,7 +51,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], ArcRotateCameraMouseWheelInput.prototype, "wheelPrecision", void 0);
         ], ArcRotateCameraMouseWheelInput.prototype, "wheelPrecision", void 0);
         return ArcRotateCameraMouseWheelInput;
         return ArcRotateCameraMouseWheelInput;
-    }());
+    })();
     BABYLON.ArcRotateCameraMouseWheelInput = ArcRotateCameraMouseWheelInput;
     BABYLON.ArcRotateCameraMouseWheelInput = ArcRotateCameraMouseWheelInput;
     BABYLON.CameraInputTypes["ArcRotateCameraMouseWheelInput"] = ArcRotateCameraMouseWheelInput;
     BABYLON.CameraInputTypes["ArcRotateCameraMouseWheelInput"] = ArcRotateCameraMouseWheelInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

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

@@ -213,7 +213,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
         ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
         return ArcRotateCameraPointersInput;
         return ArcRotateCameraPointersInput;
-    }());
+    })();
     BABYLON.ArcRotateCameraPointersInput = ArcRotateCameraPointersInput;
     BABYLON.ArcRotateCameraPointersInput = ArcRotateCameraPointersInput;
     BABYLON.CameraInputTypes["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
     BABYLON.CameraInputTypes["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js

@@ -42,7 +42,7 @@ var BABYLON;
             return "VRDeviceOrientation";
             return "VRDeviceOrientation";
         };
         };
         return ArcRotateCameraVRDeviceOrientationInput;
         return ArcRotateCameraVRDeviceOrientationInput;
-    }());
+    })();
     BABYLON.ArcRotateCameraVRDeviceOrientationInput = ArcRotateCameraVRDeviceOrientationInput;
     BABYLON.ArcRotateCameraVRDeviceOrientationInput = ArcRotateCameraVRDeviceOrientationInput;
     BABYLON.CameraInputTypes["ArcRotateCameraVRDeviceOrientationInput"] = ArcRotateCameraVRDeviceOrientationInput;
     BABYLON.CameraInputTypes["ArcRotateCameraVRDeviceOrientationInput"] = ArcRotateCameraVRDeviceOrientationInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 41 - 55
src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.js

@@ -1,61 +1,53 @@
-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;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var FreeCameraDeviceOrientationInput = (function () {
     var FreeCameraDeviceOrientationInput = (function () {
         function FreeCameraDeviceOrientationInput() {
         function FreeCameraDeviceOrientationInput() {
-            this._offsetX = null;
-            this._offsetY = null;
-            this._orientationGamma = 0;
-            this._orientationBeta = 0;
-            this._initialOrientationGamma = 0;
-            this._initialOrientationBeta = 0;
-            this.angularSensibility = 10000.0;
-            this.moveSensibility = 50.0;
-            this._resetOrientationGamma = this.resetOrientationGamma.bind(this);
-            this._orientationChanged = this.orientationChanged.bind(this);
+            var _this = this;
+            this._screenOrientationAngle = 0;
+            this._screenQuaternion = new BABYLON.Quaternion();
+            this._alpha = 0;
+            this._beta = 0;
+            this._gamma = 0;
+            this._orientationChanged = function () {
+                _this._screenOrientationAngle = (window.orientation !== undefined ? +window.orientation : (window.screen.orientation && window.screen.orientation['angle'] ? window.screen.orientation.angle : 0));
+                _this._screenOrientationAngle = -BABYLON.Tools.ToRadians(_this._screenOrientationAngle / 2);
+                _this._screenQuaternion.copyFromFloats(0, Math.sin(_this._screenOrientationAngle), 0, Math.cos(_this._screenOrientationAngle));
+            };
+            this._deviceOrientation = function (evt) {
+                _this._alpha = evt.alpha;
+                _this._beta = evt.beta;
+                _this._gamma = evt.gamma;
+            };
+            this._constantTranform = new BABYLON.Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
+            this._orientationChanged();
         }
         }
+        Object.defineProperty(FreeCameraDeviceOrientationInput.prototype, "camera", {
+            get: function () {
+                return this._camera;
+            },
+            set: function (camera) {
+                this._camera = camera;
+                if (!this._camera.rotationQuaternion)
+                    this._camera.rotationQuaternion = new BABYLON.Quaternion();
+            },
+            enumerable: true,
+            configurable: true
+        });
         FreeCameraDeviceOrientationInput.prototype.attachControl = function (element, noPreventDefault) {
         FreeCameraDeviceOrientationInput.prototype.attachControl = function (element, noPreventDefault) {
-            window.addEventListener("resize", this._resetOrientationGamma, false);
-            window.addEventListener("deviceorientation", this._orientationChanged);
-        };
-        FreeCameraDeviceOrientationInput.prototype.resetOrientationGamma = function () {
-            this._initialOrientationGamma = null;
-        };
-        FreeCameraDeviceOrientationInput.prototype.orientationChanged = function (evt) {
-            if (!this._initialOrientationGamma) {
-                this._initialOrientationGamma = evt.gamma;
-                this._initialOrientationBeta = evt.beta;
-            }
-            this._orientationGamma = evt.gamma;
-            this._orientationBeta = evt.beta;
-            this._offsetY = (this._initialOrientationBeta - this._orientationBeta);
-            this._offsetX = (this._initialOrientationGamma - this._orientationGamma);
+            window.addEventListener("orientationchange", this._orientationChanged);
+            window.addEventListener("deviceorientation", this._deviceOrientation);
         };
         };
         FreeCameraDeviceOrientationInput.prototype.detachControl = function (element) {
         FreeCameraDeviceOrientationInput.prototype.detachControl = function (element) {
-            window.removeEventListener("resize", this._resetOrientationGamma);
-            window.removeEventListener("deviceorientation", this._orientationChanged);
-            this._orientationGamma = 0;
-            this._orientationBeta = 0;
-            this._initialOrientationGamma = 0;
-            this._initialOrientationBeta = 0;
-            this._offsetX = null;
-            this._offsetY = null;
+            window.removeEventListener("orientationchange", this._orientationChanged);
+            window.removeEventListener("deviceorientation", this._deviceOrientation);
         };
         };
         FreeCameraDeviceOrientationInput.prototype.checkInputs = function () {
         FreeCameraDeviceOrientationInput.prototype.checkInputs = function () {
-            if (!this._offsetX) {
-                return;
-            }
-            var camera = this.camera;
-            camera.cameraRotation.y -= this._offsetX / this.angularSensibility;
-            var speed = camera._computeLocalCameraSpeed();
-            var direction = new BABYLON.Vector3(0, 0, speed * this._offsetY / this.moveSensibility);
-            BABYLON.Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
-            camera.cameraDirection.addInPlace(BABYLON.Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+            BABYLON.Quaternion.RotationYawPitchRollToRef(BABYLON.Tools.ToRadians(this._alpha), BABYLON.Tools.ToRadians(this._beta), -BABYLON.Tools.ToRadians(this._gamma), this.camera.rotationQuaternion);
+            this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion);
+            this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform);
+            //Mirror on XY Plane
+            this._camera.rotationQuaternion.z *= -1;
+            this._camera.rotationQuaternion.w *= -1;
         };
         };
         FreeCameraDeviceOrientationInput.prototype.getTypeName = function () {
         FreeCameraDeviceOrientationInput.prototype.getTypeName = function () {
             return "FreeCameraDeviceOrientationInput";
             return "FreeCameraDeviceOrientationInput";
@@ -63,14 +55,8 @@ var BABYLON;
         FreeCameraDeviceOrientationInput.prototype.getSimpleName = function () {
         FreeCameraDeviceOrientationInput.prototype.getSimpleName = function () {
             return "deviceOrientation";
             return "deviceOrientation";
         };
         };
-        __decorate([
-            BABYLON.serialize()
-        ], FreeCameraDeviceOrientationInput.prototype, "angularSensibility", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], FreeCameraDeviceOrientationInput.prototype, "moveSensibility", void 0);
         return FreeCameraDeviceOrientationInput;
         return FreeCameraDeviceOrientationInput;
-    }());
+    })();
     BABYLON.FreeCameraDeviceOrientationInput = FreeCameraDeviceOrientationInput;
     BABYLON.FreeCameraDeviceOrientationInput = FreeCameraDeviceOrientationInput;
     BABYLON.CameraInputTypes["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;
     BABYLON.CameraInputTypes["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 39 - 54
src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.ts

@@ -1,84 +1,69 @@
 module BABYLON {
 module BABYLON {
     export class FreeCameraDeviceOrientationInput implements ICameraInput<FreeCamera> {
     export class FreeCameraDeviceOrientationInput implements ICameraInput<FreeCamera> {
-        camera: FreeCamera;
+        private _camera: FreeCamera;
 
 
-        private _offsetX: number = null;
-        private _offsetY: number = null;
-        private _orientationGamma: number = 0;
-        private _orientationBeta: number = 0;
-        private _initialOrientationGamma: number = 0;
-        private _initialOrientationBeta: number = 0;
-        private _orientationChanged: (e: DeviceOrientationEvent) => any;
-        private _resetOrientationGamma: () => any;
+        private _screenOrientationAngle: number = 0;
 
 
-        @serialize()
-        public angularSensibility: number = 10000.0;
+        private _constantTranform: Quaternion;
+        private _screenQuaternion: Quaternion = new Quaternion();
 
 
-        @serialize()
-        public moveSensibility: number = 50.0;
+        private _alpha: number = 0;
+        private _beta: number = 0;
+        private _gamma: number = 0;
 
 
         constructor() {
         constructor() {
-            this._resetOrientationGamma = this.resetOrientationGamma.bind(this);
-            this._orientationChanged = this.orientationChanged.bind(this);
+            this._constantTranform = new Quaternion(- Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
+            this._orientationChanged();
         }
         }
 
 
-        attachControl(element : HTMLElement, noPreventDefault?: boolean) {
-            window.addEventListener("resize", this._resetOrientationGamma, false);
-            window.addEventListener("deviceorientation", this._orientationChanged);
+        public get camera(): FreeCamera {
+            return this._camera;
         }
         }
 
 
-        resetOrientationGamma() {
-            this._initialOrientationGamma = null;
+        public set camera(camera: FreeCamera) {
+            this._camera = camera;
+            if (!this._camera.rotationQuaternion) this._camera.rotationQuaternion = new Quaternion();
         }
         }
 
 
-        orientationChanged(evt) {
-            if (!this._initialOrientationGamma) {
-                this._initialOrientationGamma = evt.gamma;
-                this._initialOrientationBeta = evt.beta;
-            }
+        attachControl(element: HTMLElement, noPreventDefault?: boolean) {
+            window.addEventListener("orientationchange", this._orientationChanged);
+            window.addEventListener("deviceorientation", this._deviceOrientation);
+        }
 
 
-            this._orientationGamma = evt.gamma;
-            this._orientationBeta = evt.beta;
+        private _orientationChanged = () => {
+            this._screenOrientationAngle = (window.orientation !== undefined ? +window.orientation : (window.screen.orientation && window.screen.orientation['angle'] ? (<any>window.screen.orientation).angle : 0));
+            this._screenOrientationAngle = -Tools.ToRadians(this._screenOrientationAngle / 2);
+            this._screenQuaternion.copyFromFloats(0, Math.sin(this._screenOrientationAngle), 0, Math.cos(this._screenOrientationAngle));
+        }
 
 
-            this._offsetY = (this._initialOrientationBeta - this._orientationBeta);
-            this._offsetX = (this._initialOrientationGamma - this._orientationGamma);
+        private _deviceOrientation = (evt: DeviceOrientationEvent) => {
+            this._alpha = evt.alpha;
+            this._beta = evt.beta;
+            this._gamma = evt.gamma;
         }
         }
 
 
-        detachControl(element : HTMLElement) {
-            window.removeEventListener("resize", this._resetOrientationGamma);
-            window.removeEventListener("deviceorientation", this._orientationChanged);
-            
-            this._orientationGamma = 0;
-            this._orientationBeta = 0;
-            this._initialOrientationGamma = 0;
-            this._initialOrientationBeta = 0;
-            this._offsetX = null;
-            this._offsetY = null;
+        detachControl(element: HTMLElement) {
+            window.removeEventListener("orientationchange", this._orientationChanged);
+            window.removeEventListener("deviceorientation", this._deviceOrientation);
         }
         }
 
 
         public checkInputs() {
         public checkInputs() {
-            if (!this._offsetX) {
-                return;
-            }
-            
-            var camera = this.camera;
-            camera.cameraRotation.y -= this._offsetX / this.angularSensibility;
-
-            var speed = camera._computeLocalCameraSpeed();
-            var direction = new Vector3(0, 0, speed * this._offsetY / this.moveSensibility);
-
-            Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
-            camera.cameraDirection.addInPlace(Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+            Quaternion.RotationYawPitchRollToRef(BABYLON.Tools.ToRadians(this._alpha), BABYLON.Tools.ToRadians(this._beta), -BABYLON.Tools.ToRadians(this._gamma), this.camera.rotationQuaternion)
+            this._camera.rotationQuaternion.multiplyInPlace(this._screenQuaternion);
+            this._camera.rotationQuaternion.multiplyInPlace(this._constantTranform);
+            //Mirror on XY Plane
+            this._camera.rotationQuaternion.z *= -1;
+            this._camera.rotationQuaternion.w *= -1;
         }
         }
 
 
         getTypeName(): string {
         getTypeName(): string {
             return "FreeCameraDeviceOrientationInput";
             return "FreeCameraDeviceOrientationInput";
         }
         }
-        
-        getSimpleName(){
+
+        getSimpleName() {
             return "deviceOrientation";
             return "deviceOrientation";
         }
         }
     }
     }
-    
+
     CameraInputTypes["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;
     CameraInputTypes["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;
 }
 }

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.gamepad.js

@@ -60,7 +60,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], FreeCameraGamepadInput.prototype, "gamepadMoveSensibility", void 0);
         ], FreeCameraGamepadInput.prototype, "gamepadMoveSensibility", void 0);
         return FreeCameraGamepadInput;
         return FreeCameraGamepadInput;
-    }());
+    })();
     BABYLON.FreeCameraGamepadInput = FreeCameraGamepadInput;
     BABYLON.FreeCameraGamepadInput = FreeCameraGamepadInput;
     BABYLON.CameraInputTypes["FreeCameraGamepadInput"] = FreeCameraGamepadInput;
     BABYLON.CameraInputTypes["FreeCameraGamepadInput"] = FreeCameraGamepadInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.keyboard.js

@@ -111,7 +111,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], FreeCameraKeyboardMoveInput.prototype, "keysRight", void 0);
         ], FreeCameraKeyboardMoveInput.prototype, "keysRight", void 0);
         return FreeCameraKeyboardMoveInput;
         return FreeCameraKeyboardMoveInput;
-    }());
+    })();
     BABYLON.FreeCameraKeyboardMoveInput = FreeCameraKeyboardMoveInput;
     BABYLON.FreeCameraKeyboardMoveInput = FreeCameraKeyboardMoveInput;
     BABYLON.CameraInputTypes["FreeCameraKeyboardMoveInput"] = FreeCameraKeyboardMoveInput;
     BABYLON.CameraInputTypes["FreeCameraKeyboardMoveInput"] = FreeCameraKeyboardMoveInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.mouse.js

@@ -92,7 +92,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], FreeCameraMouseInput.prototype, "angularSensibility", void 0);
         ], FreeCameraMouseInput.prototype, "angularSensibility", void 0);
         return FreeCameraMouseInput;
         return FreeCameraMouseInput;
-    }());
+    })();
     BABYLON.FreeCameraMouseInput = FreeCameraMouseInput;
     BABYLON.FreeCameraMouseInput = FreeCameraMouseInput;
     BABYLON.CameraInputTypes["FreeCameraMouseInput"] = FreeCameraMouseInput;
     BABYLON.CameraInputTypes["FreeCameraMouseInput"] = FreeCameraMouseInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.touch.js

@@ -116,7 +116,7 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], FreeCameraTouchInput.prototype, "touchMoveSensibility", void 0);
         ], FreeCameraTouchInput.prototype, "touchMoveSensibility", void 0);
         return FreeCameraTouchInput;
         return FreeCameraTouchInput;
-    }());
+    })();
     BABYLON.FreeCameraTouchInput = FreeCameraTouchInput;
     BABYLON.FreeCameraTouchInput = FreeCameraTouchInput;
     BABYLON.CameraInputTypes["FreeCameraTouchInput"] = FreeCameraTouchInput;
     BABYLON.CameraInputTypes["FreeCameraTouchInput"] = FreeCameraTouchInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.virtualjoystick.js

@@ -48,7 +48,7 @@ var BABYLON;
             return "virtualJoystick";
             return "virtualJoystick";
         };
         };
         return FreeCameraVirtualJoystickInput;
         return FreeCameraVirtualJoystickInput;
-    }());
+    })();
     BABYLON.FreeCameraVirtualJoystickInput = FreeCameraVirtualJoystickInput;
     BABYLON.FreeCameraVirtualJoystickInput = FreeCameraVirtualJoystickInput;
     BABYLON.CameraInputTypes["FreeCameraVirtualJoystickInput"] = FreeCameraVirtualJoystickInput;
     BABYLON.CameraInputTypes["FreeCameraVirtualJoystickInput"] = FreeCameraVirtualJoystickInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.js

@@ -47,7 +47,7 @@ var BABYLON;
             return "VRDeviceOrientation";
             return "VRDeviceOrientation";
         };
         };
         return FreeCameraVRDeviceOrientationInput;
         return FreeCameraVRDeviceOrientationInput;
-    }());
+    })();
     BABYLON.FreeCameraVRDeviceOrientationInput = FreeCameraVRDeviceOrientationInput;
     BABYLON.FreeCameraVRDeviceOrientationInput = FreeCameraVRDeviceOrientationInput;
     BABYLON.CameraInputTypes["FreeCameraVRDeviceOrientationInput"] = FreeCameraVRDeviceOrientationInput;
     BABYLON.CameraInputTypes["FreeCameraVRDeviceOrientationInput"] = FreeCameraVRDeviceOrientationInput;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/VR/babylon.vrCameraMetrics.js

@@ -67,6 +67,6 @@ var BABYLON;
             return result;
             return result;
         };
         };
         return VRCameraMetrics;
         return VRCameraMetrics;
-    }());
+    })();
     BABYLON.VRCameraMetrics = VRCameraMetrics;
     BABYLON.VRCameraMetrics = VRCameraMetrics;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 4 - 3
src/Cameras/VR/babylon.vrDeviceOrientationCamera.js

@@ -10,16 +10,17 @@ var BABYLON;
         function VRDeviceOrientationFreeCamera(name, position, scene, compensateDistortion) {
         function VRDeviceOrientationFreeCamera(name, position, scene, compensateDistortion) {
             if (compensateDistortion === void 0) { compensateDistortion = true; }
             if (compensateDistortion === void 0) { compensateDistortion = true; }
             _super.call(this, name, position, scene);
             _super.call(this, name, position, scene);
+            this.rotationQuaternion = new BABYLON.Quaternion();
             var metrics = BABYLON.VRCameraMetrics.GetDefault();
             var metrics = BABYLON.VRCameraMetrics.GetDefault();
             metrics.compensateDistortion = compensateDistortion;
             metrics.compensateDistortion = compensateDistortion;
             this.setCameraRigMode(BABYLON.Camera.RIG_MODE_VR, { vrCameraMetrics: metrics });
             this.setCameraRigMode(BABYLON.Camera.RIG_MODE_VR, { vrCameraMetrics: metrics });
-            this.inputs.addVRDeviceOrientation();
+            this.inputs.addDeviceOrientation();
         }
         }
         VRDeviceOrientationFreeCamera.prototype.getTypeName = function () {
         VRDeviceOrientationFreeCamera.prototype.getTypeName = function () {
             return "VRDeviceOrientationFreeCamera";
             return "VRDeviceOrientationFreeCamera";
         };
         };
         return VRDeviceOrientationFreeCamera;
         return VRDeviceOrientationFreeCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.VRDeviceOrientationFreeCamera = VRDeviceOrientationFreeCamera;
     BABYLON.VRDeviceOrientationFreeCamera = VRDeviceOrientationFreeCamera;
     var VRDeviceOrientationArcRotateCamera = (function (_super) {
     var VRDeviceOrientationArcRotateCamera = (function (_super) {
         __extends(VRDeviceOrientationArcRotateCamera, _super);
         __extends(VRDeviceOrientationArcRotateCamera, _super);
@@ -35,6 +36,6 @@ var BABYLON;
             return "VRDeviceOrientationArcRotateCamera";
             return "VRDeviceOrientationArcRotateCamera";
         };
         };
         return VRDeviceOrientationArcRotateCamera;
         return VRDeviceOrientationArcRotateCamera;
-    }(BABYLON.ArcRotateCamera));
+    })(BABYLON.ArcRotateCamera);
     BABYLON.VRDeviceOrientationArcRotateCamera = VRDeviceOrientationArcRotateCamera;
     BABYLON.VRDeviceOrientationArcRotateCamera = VRDeviceOrientationArcRotateCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 3 - 1
src/Cameras/VR/babylon.vrDeviceOrientationCamera.ts

@@ -4,11 +4,13 @@ module BABYLON {
         constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true) {
         constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true) {
             super(name, position, scene);
             super(name, position, scene);
 
 
+            this.rotationQuaternion = new Quaternion();
+
             var metrics = VRCameraMetrics.GetDefault();
             var metrics = VRCameraMetrics.GetDefault();
             metrics.compensateDistortion = compensateDistortion;
             metrics.compensateDistortion = compensateDistortion;
             this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: metrics });
             this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: metrics });
 
 
-            this.inputs.addVRDeviceOrientation();
+            this.inputs.addDeviceOrientation();
         }
         }
 
 
 
 

+ 1 - 1
src/Cameras/VR/babylon.webVRCamera.js

@@ -72,6 +72,6 @@ var BABYLON;
             return "WebVRFreeCamera";
             return "WebVRFreeCamera";
         };
         };
         return WebVRFreeCamera;
         return WebVRFreeCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.WebVRFreeCamera = WebVRFreeCamera;
     BABYLON.WebVRFreeCamera = WebVRFreeCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.arcRotateCamera.js

@@ -541,6 +541,6 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], ArcRotateCamera.prototype, "allowUpsideDown", void 0);
         ], ArcRotateCamera.prototype, "allowUpsideDown", void 0);
         return ArcRotateCamera;
         return ArcRotateCamera;
-    }(BABYLON.TargetCamera));
+    })(BABYLON.TargetCamera);
     BABYLON.ArcRotateCamera = ArcRotateCamera;
     BABYLON.ArcRotateCamera = ArcRotateCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.arcRotateCameraInputsManager.js

@@ -31,6 +31,6 @@ var BABYLON;
             return this;
             return this;
         };
         };
         return ArcRotateCameraInputsManager;
         return ArcRotateCameraInputsManager;
-    }(BABYLON.CameraInputsManager));
+    })(BABYLON.CameraInputsManager);
     BABYLON.ArcRotateCameraInputsManager = ArcRotateCameraInputsManager;
     BABYLON.ArcRotateCameraInputsManager = ArcRotateCameraInputsManager;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.camera.js

@@ -612,6 +612,6 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], Camera.prototype, "isStereoscopicSideBySide", void 0);
         ], Camera.prototype, "isStereoscopicSideBySide", void 0);
         return Camera;
         return Camera;
-    }(BABYLON.Node));
+    })(BABYLON.Node);
     BABYLON.Camera = Camera;
     BABYLON.Camera = Camera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.cameraInputsManager.js

@@ -128,6 +128,6 @@ var BABYLON;
             }
             }
         };
         };
         return CameraInputsManager;
         return CameraInputsManager;
-    }());
+    })();
     BABYLON.CameraInputsManager = CameraInputsManager;
     BABYLON.CameraInputsManager = CameraInputsManager;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 3 - 13
src/Cameras/babylon.deviceOrientationCamera.js

@@ -16,28 +16,18 @@ var BABYLON;
         Object.defineProperty(DeviceOrientationCamera.prototype, "angularSensibility", {
         Object.defineProperty(DeviceOrientationCamera.prototype, "angularSensibility", {
             //-- Begin properties for backward compatibility for inputs
             //-- Begin properties for backward compatibility for inputs
             get: function () {
             get: function () {
-                var deviceOrientation = this.inputs.attached["deviceOrientation"];
-                if (deviceOrientation)
-                    return deviceOrientation.angularSensibility;
+                return 0;
             },
             },
             set: function (value) {
             set: function (value) {
-                var deviceOrientation = this.inputs.attached["deviceOrientation"];
-                if (deviceOrientation)
-                    deviceOrientation.angularSensibility = value;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(DeviceOrientationCamera.prototype, "moveSensibility", {
         Object.defineProperty(DeviceOrientationCamera.prototype, "moveSensibility", {
             get: function () {
             get: function () {
-                var deviceOrientation = this.inputs.attached["deviceOrientation"];
-                if (deviceOrientation)
-                    return deviceOrientation.moveSensibility;
+                return 0;
             },
             },
             set: function (value) {
             set: function (value) {
-                var deviceOrientation = this.inputs.attached["deviceOrientation"];
-                if (deviceOrientation)
-                    deviceOrientation.moveSensibility = value;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -46,6 +36,6 @@ var BABYLON;
             return "DeviceOrientationCamera";
             return "DeviceOrientationCamera";
         };
         };
         return DeviceOrientationCamera;
         return DeviceOrientationCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.DeviceOrientationCamera = DeviceOrientationCamera;
     BABYLON.DeviceOrientationCamera = DeviceOrientationCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 2 - 12
src/Cameras/babylon.deviceOrientationCamera.ts

@@ -3,27 +3,17 @@ module BABYLON {
     export class DeviceOrientationCamera extends FreeCamera {
     export class DeviceOrientationCamera extends FreeCamera {
         //-- Begin properties for backward compatibility for inputs
         //-- Begin properties for backward compatibility for inputs
         public get angularSensibility() {
         public get angularSensibility() {
-            var deviceOrientation = <FreeCameraDeviceOrientationInput>this.inputs.attached["deviceOrientation"];
-            if (deviceOrientation)
-                return deviceOrientation.angularSensibility;
+            return 0;
         }
         }
         
         
         public set angularSensibility(value) {
         public set angularSensibility(value) {
-            var deviceOrientation = <FreeCameraDeviceOrientationInput>this.inputs.attached["deviceOrientation"];
-            if (deviceOrientation)
-                deviceOrientation.angularSensibility = value;
         }
         }
         
         
         public get moveSensibility() {
         public get moveSensibility() {
-            var deviceOrientation = <FreeCameraDeviceOrientationInput>this.inputs.attached["deviceOrientation"];
-            if (deviceOrientation)
-                return deviceOrientation.moveSensibility;
+                return 0;
         }
         }
         
         
         public set moveSensibility(value) {
         public set moveSensibility(value) {
-            var deviceOrientation = <FreeCameraDeviceOrientationInput>this.inputs.attached["deviceOrientation"];
-            if (deviceOrientation)
-                deviceOrientation.moveSensibility = value;
         }
         }
         //-- end properties for backward compatibility for inputs
         //-- end properties for backward compatibility for inputs
         
         

+ 2 - 2
src/Cameras/babylon.followCamera.js

@@ -84,7 +84,7 @@ var BABYLON;
             BABYLON.serializeAsMeshReference("lockedTargetId")
             BABYLON.serializeAsMeshReference("lockedTargetId")
         ], FollowCamera.prototype, "target", void 0);
         ], FollowCamera.prototype, "target", void 0);
         return FollowCamera;
         return FollowCamera;
-    }(BABYLON.TargetCamera));
+    })(BABYLON.TargetCamera);
     BABYLON.FollowCamera = FollowCamera;
     BABYLON.FollowCamera = FollowCamera;
     var ArcFollowCamera = (function (_super) {
     var ArcFollowCamera = (function (_super) {
         __extends(ArcFollowCamera, _super);
         __extends(ArcFollowCamera, _super);
@@ -112,6 +112,6 @@ var BABYLON;
             return "ArcFollowCamera";
             return "ArcFollowCamera";
         };
         };
         return ArcFollowCamera;
         return ArcFollowCamera;
-    }(BABYLON.TargetCamera));
+    })(BABYLON.TargetCamera);
     BABYLON.ArcFollowCamera = ArcFollowCamera;
     BABYLON.ArcFollowCamera = ArcFollowCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.freeCamera.js

@@ -180,6 +180,6 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], FreeCamera.prototype, "applyGravity", void 0);
         ], FreeCamera.prototype, "applyGravity", void 0);
         return FreeCamera;
         return FreeCamera;
-    }(BABYLON.TargetCamera));
+    })(BABYLON.TargetCamera);
     BABYLON.FreeCamera = FreeCamera;
     BABYLON.FreeCamera = FreeCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.freeCameraInputsManager.js

@@ -40,6 +40,6 @@ var BABYLON;
             return this;
             return this;
         };
         };
         return FreeCameraInputsManager;
         return FreeCameraInputsManager;
-    }(BABYLON.CameraInputsManager));
+    })(BABYLON.CameraInputsManager);
     BABYLON.FreeCameraInputsManager = FreeCameraInputsManager;
     BABYLON.FreeCameraInputsManager = FreeCameraInputsManager;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.gamepadCamera.js

@@ -46,6 +46,6 @@ var BABYLON;
             return "GamepadCamera";
             return "GamepadCamera";
         };
         };
         return GamepadCamera;
         return GamepadCamera;
-    }(BABYLON.UniversalCamera));
+    })(BABYLON.UniversalCamera);
     BABYLON.GamepadCamera = GamepadCamera;
     BABYLON.GamepadCamera = GamepadCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 8 - 8
src/Cameras/babylon.stereoscopicCameras.js

@@ -16,7 +16,7 @@ var BABYLON;
             return "AnaglyphFreeCamera";
             return "AnaglyphFreeCamera";
         };
         };
         return AnaglyphFreeCamera;
         return AnaglyphFreeCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.AnaglyphFreeCamera = AnaglyphFreeCamera;
     BABYLON.AnaglyphFreeCamera = AnaglyphFreeCamera;
     var AnaglyphArcRotateCamera = (function (_super) {
     var AnaglyphArcRotateCamera = (function (_super) {
         __extends(AnaglyphArcRotateCamera, _super);
         __extends(AnaglyphArcRotateCamera, _super);
@@ -29,7 +29,7 @@ var BABYLON;
             return "AnaglyphArcRotateCamera";
             return "AnaglyphArcRotateCamera";
         };
         };
         return AnaglyphArcRotateCamera;
         return AnaglyphArcRotateCamera;
-    }(BABYLON.ArcRotateCamera));
+    })(BABYLON.ArcRotateCamera);
     BABYLON.AnaglyphArcRotateCamera = AnaglyphArcRotateCamera;
     BABYLON.AnaglyphArcRotateCamera = AnaglyphArcRotateCamera;
     var AnaglyphGamepadCamera = (function (_super) {
     var AnaglyphGamepadCamera = (function (_super) {
         __extends(AnaglyphGamepadCamera, _super);
         __extends(AnaglyphGamepadCamera, _super);
@@ -42,7 +42,7 @@ var BABYLON;
             return "AnaglyphGamepadCamera";
             return "AnaglyphGamepadCamera";
         };
         };
         return AnaglyphGamepadCamera;
         return AnaglyphGamepadCamera;
-    }(BABYLON.GamepadCamera));
+    })(BABYLON.GamepadCamera);
     BABYLON.AnaglyphGamepadCamera = AnaglyphGamepadCamera;
     BABYLON.AnaglyphGamepadCamera = AnaglyphGamepadCamera;
     var AnaglyphUniversalCamera = (function (_super) {
     var AnaglyphUniversalCamera = (function (_super) {
         __extends(AnaglyphUniversalCamera, _super);
         __extends(AnaglyphUniversalCamera, _super);
@@ -55,7 +55,7 @@ var BABYLON;
             return "AnaglyphUniversalCamera";
             return "AnaglyphUniversalCamera";
         };
         };
         return AnaglyphUniversalCamera;
         return AnaglyphUniversalCamera;
-    }(BABYLON.UniversalCamera));
+    })(BABYLON.UniversalCamera);
     BABYLON.AnaglyphUniversalCamera = AnaglyphUniversalCamera;
     BABYLON.AnaglyphUniversalCamera = AnaglyphUniversalCamera;
     var StereoscopicFreeCamera = (function (_super) {
     var StereoscopicFreeCamera = (function (_super) {
         __extends(StereoscopicFreeCamera, _super);
         __extends(StereoscopicFreeCamera, _super);
@@ -69,7 +69,7 @@ var BABYLON;
             return "StereoscopicFreeCamera";
             return "StereoscopicFreeCamera";
         };
         };
         return StereoscopicFreeCamera;
         return StereoscopicFreeCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.StereoscopicFreeCamera = StereoscopicFreeCamera;
     BABYLON.StereoscopicFreeCamera = StereoscopicFreeCamera;
     var StereoscopicArcRotateCamera = (function (_super) {
     var StereoscopicArcRotateCamera = (function (_super) {
         __extends(StereoscopicArcRotateCamera, _super);
         __extends(StereoscopicArcRotateCamera, _super);
@@ -83,7 +83,7 @@ var BABYLON;
             return "StereoscopicArcRotateCamera";
             return "StereoscopicArcRotateCamera";
         };
         };
         return StereoscopicArcRotateCamera;
         return StereoscopicArcRotateCamera;
-    }(BABYLON.ArcRotateCamera));
+    })(BABYLON.ArcRotateCamera);
     BABYLON.StereoscopicArcRotateCamera = StereoscopicArcRotateCamera;
     BABYLON.StereoscopicArcRotateCamera = StereoscopicArcRotateCamera;
     var StereoscopicGamepadCamera = (function (_super) {
     var StereoscopicGamepadCamera = (function (_super) {
         __extends(StereoscopicGamepadCamera, _super);
         __extends(StereoscopicGamepadCamera, _super);
@@ -97,7 +97,7 @@ var BABYLON;
             return "StereoscopicGamepadCamera";
             return "StereoscopicGamepadCamera";
         };
         };
         return StereoscopicGamepadCamera;
         return StereoscopicGamepadCamera;
-    }(BABYLON.GamepadCamera));
+    })(BABYLON.GamepadCamera);
     BABYLON.StereoscopicGamepadCamera = StereoscopicGamepadCamera;
     BABYLON.StereoscopicGamepadCamera = StereoscopicGamepadCamera;
     var StereoscopicUniversalCamera = (function (_super) {
     var StereoscopicUniversalCamera = (function (_super) {
         __extends(StereoscopicUniversalCamera, _super);
         __extends(StereoscopicUniversalCamera, _super);
@@ -111,6 +111,6 @@ var BABYLON;
             return "StereoscopicUniversalCamera";
             return "StereoscopicUniversalCamera";
         };
         };
         return StereoscopicUniversalCamera;
         return StereoscopicUniversalCamera;
-    }(BABYLON.UniversalCamera));
+    })(BABYLON.UniversalCamera);
     BABYLON.StereoscopicUniversalCamera = StereoscopicUniversalCamera;
     BABYLON.StereoscopicUniversalCamera = StereoscopicUniversalCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 30 - 19
src/Cameras/babylon.targetCamera.js

@@ -27,6 +27,7 @@ var BABYLON;
             this._cameraTransformMatrix = BABYLON.Matrix.Zero();
             this._cameraTransformMatrix = BABYLON.Matrix.Zero();
             this._cameraRotationMatrix = BABYLON.Matrix.Zero();
             this._cameraRotationMatrix = BABYLON.Matrix.Zero();
             this._referencePoint = new BABYLON.Vector3(0, 0, 1);
             this._referencePoint = new BABYLON.Vector3(0, 0, 1);
+            this._defaultUpVector = new BABYLON.Vector3(0, 1, 0);
             this._transformedReferencePoint = BABYLON.Vector3.Zero();
             this._transformedReferencePoint = BABYLON.Vector3.Zero();
             this._lookAtTemp = BABYLON.Matrix.Zero();
             this._lookAtTemp = BABYLON.Matrix.Zero();
             this._tempMatrix = BABYLON.Matrix.Zero();
             this._tempMatrix = BABYLON.Matrix.Zero();
@@ -48,6 +49,7 @@ var BABYLON;
             _super.prototype._initCache.call(this);
             _super.prototype._initCache.call(this);
             this._cache.lockedTarget = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.lockedTarget = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.rotation = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.rotation = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+            this._cache.rotationQuaternion = new BABYLON.Quaternion(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
         };
         };
         TargetCamera.prototype._updateCache = function (ignoreParentClass) {
         TargetCamera.prototype._updateCache = function (ignoreParentClass) {
             if (!ignoreParentClass) {
             if (!ignoreParentClass) {
@@ -66,6 +68,8 @@ var BABYLON;
                 }
                 }
             }
             }
             this._cache.rotation.copyFrom(this.rotation);
             this._cache.rotation.copyFrom(this.rotation);
+            if (this.rotationQuaternion)
+                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
         };
         };
         // Synchronized
         // Synchronized
         TargetCamera.prototype._isSynchronizedViewMatrix = function () {
         TargetCamera.prototype._isSynchronizedViewMatrix = function () {
@@ -74,7 +78,7 @@ var BABYLON;
             }
             }
             var lockedTargetPosition = this._getLockedTargetPosition();
             var lockedTargetPosition = this._getLockedTargetPosition();
             return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
             return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
-                && this._cache.rotation.equals(this.rotation);
+                && (this.rotationQuaternion ? this.rotationQuaternion.equals(this._cache.rotationQuaternion) : this._cache.rotation.equals(this.rotation));
         };
         };
         // Methods
         // Methods
         TargetCamera.prototype._computeLocalCameraSpeed = function () {
         TargetCamera.prototype._computeLocalCameraSpeed = function () {
@@ -84,7 +88,7 @@ var BABYLON;
         // Target
         // Target
         TargetCamera.prototype.setTarget = function (target) {
         TargetCamera.prototype.setTarget = function (target) {
             this.upVector.normalize();
             this.upVector.normalize();
-            BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
+            BABYLON.Matrix.LookAtLHToRef(this.position, target, this._defaultUpVector, this._camMatrix);
             this._camMatrix.invert();
             this._camMatrix.invert();
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
             var vDir = target.subtract(this.position);
             var vDir = target.subtract(this.position);
@@ -94,7 +98,7 @@ var BABYLON;
             else {
             else {
                 this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
                 this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
             }
             }
-            this.rotation.z = -Math.acos(BABYLON.Vector3.Dot(new BABYLON.Vector3(0, 1.0, 0), this.upVector));
+            this.rotation.z = 0;
             if (isNaN(this.rotation.x)) {
             if (isNaN(this.rotation.x)) {
                 this.rotation.x = 0;
                 this.rotation.x = 0;
             }
             }
@@ -104,6 +108,9 @@ var BABYLON;
             if (isNaN(this.rotation.z)) {
             if (isNaN(this.rotation.z)) {
                 this.rotation.z = 0;
                 this.rotation.z = 0;
             }
             }
+            if (this.rotationQuaternion) {
+                BABYLON.Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
+            }
         };
         };
         TargetCamera.prototype.getTarget = function () {
         TargetCamera.prototype.getTarget = function () {
             return this._currentTarget;
             return this._currentTarget;
@@ -157,19 +164,20 @@ var BABYLON;
             }
             }
             _super.prototype._checkInputs.call(this);
             _super.prototype._checkInputs.call(this);
         };
         };
+        TargetCamera.prototype._updateCameraRotationMatrix = function () {
+            if (this.rotationQuaternion) {
+                this.rotationQuaternion.toRotationMatrix(this._cameraRotationMatrix);
+                //update the up vector!
+                BABYLON.Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this.upVector);
+            }
+            else {
+                BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+            }
+        };
         TargetCamera.prototype._getViewMatrix = function () {
         TargetCamera.prototype._getViewMatrix = function () {
             if (!this.lockedTarget) {
             if (!this.lockedTarget) {
                 // Compute
                 // Compute
-                if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
-                    BABYLON.Matrix.LookAtLHToRef(BABYLON.Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
-                    BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
-                    this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);
-                    this._lookAtTemp.invert();
-                    this._tempMatrix.multiplyToRef(this._lookAtTemp, this._cameraRotationMatrix);
-                }
-                else {
-                    BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
-                }
+                this._updateCameraRotationMatrix();
                 BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
                 BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
                 // Computing target and final matrix
                 // Computing target and final matrix
                 this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
                 this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
@@ -181,9 +189,9 @@ var BABYLON;
             return this._viewMatrix;
             return this._viewMatrix;
         };
         };
         TargetCamera.prototype._getVRViewMatrix = function () {
         TargetCamera.prototype._getVRViewMatrix = function () {
-            BABYLON.Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+            this._updateCameraRotationMatrix();
             BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
             BABYLON.Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
-            BABYLON.Vector3.TransformNormalToRef(this.upVector, this._cameraRotationMatrix, this._cameraRigParams.vrActualUp);
+            BABYLON.Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this._cameraRigParams.vrActualUp);
             // Computing target and final matrix
             // Computing target and final matrix
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
             BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this._cameraRigParams.vrActualUp, this._cameraRigParams.vrWorkMatrix);
             BABYLON.Matrix.LookAtLHToRef(this.position, this._currentTarget, this._cameraRigParams.vrActualUp, this._cameraRigParams.vrWorkMatrix);
@@ -198,9 +206,13 @@ var BABYLON;
             if (this.cameraRigMode !== BABYLON.Camera.RIG_MODE_NONE) {
             if (this.cameraRigMode !== BABYLON.Camera.RIG_MODE_NONE) {
                 var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
                 var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
                 if (this.cameraRigMode === BABYLON.Camera.RIG_MODE_VR) {
                 if (this.cameraRigMode === BABYLON.Camera.RIG_MODE_VR) {
+                    if (!this.rotationQuaternion) {
+                        this.rotationQuaternion = new BABYLON.Quaternion();
+                    }
                     rigCamera._cameraRigParams = {};
                     rigCamera._cameraRigParams = {};
                     rigCamera._cameraRigParams.vrActualUp = new BABYLON.Vector3(0, 0, 0);
                     rigCamera._cameraRigParams.vrActualUp = new BABYLON.Vector3(0, 0, 0);
                     rigCamera._getViewMatrix = rigCamera._getVRViewMatrix;
                     rigCamera._getViewMatrix = rigCamera._getVRViewMatrix;
+                    rigCamera.rotationQuaternion = new BABYLON.Quaternion();
                 }
                 }
                 return rigCamera;
                 return rigCamera;
             }
             }
@@ -227,9 +239,8 @@ var BABYLON;
                     camRight.setTarget(this.getTarget());
                     camRight.setTarget(this.getTarget());
                     break;
                     break;
                 case BABYLON.Camera.RIG_MODE_VR:
                 case BABYLON.Camera.RIG_MODE_VR:
-                    camLeft.rotation.x = camRight.rotation.x = this.rotation.x;
-                    camLeft.rotation.y = camRight.rotation.y = this.rotation.y;
-                    camLeft.rotation.z = camRight.rotation.z = this.rotation.z;
+                    camLeft.rotationQuaternion.copyFrom(this.rotationQuaternion);
+                    camRight.rotationQuaternion.copyFrom(this.rotationQuaternion);
                     camLeft.position.copyFrom(this.position);
                     camLeft.position.copyFrom(this.position);
                     camRight.position.copyFrom(this.position);
                     camRight.position.copyFrom(this.position);
                     break;
                     break;
@@ -258,6 +269,6 @@ var BABYLON;
             BABYLON.serializeAsMeshReference("lockedTargetId")
             BABYLON.serializeAsMeshReference("lockedTargetId")
         ], TargetCamera.prototype, "lockedTarget", void 0);
         ], TargetCamera.prototype, "lockedTarget", void 0);
         return TargetCamera;
         return TargetCamera;
-    }(BABYLON.Camera));
+    })(BABYLON.Camera);
     BABYLON.TargetCamera = TargetCamera;
     BABYLON.TargetCamera = TargetCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 43 - 26
src/Cameras/babylon.targetCamera.ts

@@ -7,6 +7,8 @@
         @serializeAsVector3()
         @serializeAsVector3()
         public rotation = new Vector3(0, 0, 0);
         public rotation = new Vector3(0, 0, 0);
 
 
+        public rotationQuaternion: Quaternion;
+
         @serialize()
         @serialize()
         public speed = 2.0;
         public speed = 2.0;
 
 
@@ -23,6 +25,7 @@
         private _rigCamTransformMatrix: Matrix;
         private _rigCamTransformMatrix: Matrix;
 
 
         public _referencePoint = new Vector3(0, 0, 1);
         public _referencePoint = new Vector3(0, 0, 1);
+        private _defaultUpVector = new Vector3(0, 1, 0);
         public _transformedReferencePoint = Vector3.Zero();
         public _transformedReferencePoint = Vector3.Zero();
         public _lookAtTemp = Matrix.Zero();
         public _lookAtTemp = Matrix.Zero();
         public _tempMatrix = Matrix.Zero();
         public _tempMatrix = Matrix.Zero();
@@ -53,6 +56,7 @@
             super._initCache();
             super._initCache();
             this._cache.lockedTarget = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.lockedTarget = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.rotation = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             this._cache.rotation = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+            this._cache.rotationQuaternion = new Quaternion(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
         }
         }
 
 
         public _updateCache(ignoreParentClass?: boolean): void {
         public _updateCache(ignoreParentClass?: boolean): void {
@@ -74,6 +78,8 @@
             }
             }
 
 
             this._cache.rotation.copyFrom(this.rotation);
             this._cache.rotation.copyFrom(this.rotation);
+            if (this.rotationQuaternion)
+                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
         }
         }
 
 
         // Synchronized
         // Synchronized
@@ -85,7 +91,7 @@
             var lockedTargetPosition = this._getLockedTargetPosition();
             var lockedTargetPosition = this._getLockedTargetPosition();
 
 
             return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
             return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
-                && this._cache.rotation.equals(this.rotation);
+                && (this.rotationQuaternion ? this.rotationQuaternion.equals(this._cache.rotationQuaternion) : this._cache.rotation.equals(this.rotation));
         }
         }
 
 
         // Methods
         // Methods
@@ -98,7 +104,7 @@
         public setTarget(target: Vector3): void {
         public setTarget(target: Vector3): void {
             this.upVector.normalize();
             this.upVector.normalize();
 
 
-            Matrix.LookAtLHToRef(this.position, target, this.upVector, this._camMatrix);
+            Matrix.LookAtLHToRef(this.position, target, this._defaultUpVector, this._camMatrix);
             this._camMatrix.invert();
             this._camMatrix.invert();
 
 
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
             this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
@@ -111,7 +117,7 @@
                 this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
                 this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
             }
             }
 
 
-            this.rotation.z = -Math.acos(Vector3.Dot(new Vector3(0, 1.0, 0), this.upVector));
+            this.rotation.z = 0;
 
 
             if (isNaN(this.rotation.x)) {
             if (isNaN(this.rotation.x)) {
                 this.rotation.x = 0;
                 this.rotation.x = 0;
@@ -124,6 +130,10 @@
             if (isNaN(this.rotation.z)) {
             if (isNaN(this.rotation.z)) {
                 this.rotation.z = 0;
                 this.rotation.z = 0;
             }
             }
+
+            if (this.rotationQuaternion) {
+                Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
+            }
         }
         }
 
 
         public getTarget(): Vector3 {
         public getTarget(): Vector3 {
@@ -194,21 +204,26 @@
             super._checkInputs();
             super._checkInputs();
         }
         }
 
 
+        private _updateCameraRotationMatrix() {
+            if (this.rotationQuaternion) {
+                this.rotationQuaternion.toRotationMatrix(this._cameraRotationMatrix);
+                //update the up vector!
+                BABYLON.Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this.upVector);
+            } else {
+                Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+                //if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
+                //    Matrix.LookAtLHToRef(Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
+                //    this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);
+                //    this._lookAtTemp.invert();
+                //    this._tempMatrix.multiplyToRef(this._lookAtTemp, this._cameraRotationMatrix);
+                //}
+            }
+        }
 
 
         public _getViewMatrix(): Matrix {
         public _getViewMatrix(): Matrix {
             if (!this.lockedTarget) {
             if (!this.lockedTarget) {
                 // Compute
                 // Compute
-                if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
-                    Matrix.LookAtLHToRef(Vector3.Zero(), this._referencePoint, this.upVector, this._lookAtTemp);
-                    Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
-
-
-                    this._lookAtTemp.multiplyToRef(this._cameraRotationMatrix, this._tempMatrix);
-                    this._lookAtTemp.invert();
-                    this._tempMatrix.multiplyToRef(this._lookAtTemp, this._cameraRotationMatrix);
-                } else {
-                    Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
-                }
+                this._updateCameraRotationMatrix();
 
 
                 Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
                 Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
 
 
@@ -223,10 +238,10 @@
         }
         }
 
 
         public _getVRViewMatrix(): Matrix {
         public _getVRViewMatrix(): Matrix {
-            Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
+            this._updateCameraRotationMatrix();
 
 
             Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
             Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
-            Vector3.TransformNormalToRef(this.upVector, this._cameraRotationMatrix, this._cameraRigParams.vrActualUp);
+            Vector3.TransformNormalToRef(this._defaultUpVector, this._cameraRotationMatrix, this._cameraRigParams.vrActualUp);
 
 
             // Computing target and final matrix
             // Computing target and final matrix
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
@@ -236,7 +251,7 @@
             this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._viewMatrix);
             this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._viewMatrix);
             return this._viewMatrix;
             return this._viewMatrix;
         }
         }
-        
+
         /**
         /**
          * @override
          * @override
          * Override Camera.createRigCamera
          * Override Camera.createRigCamera
@@ -245,15 +260,19 @@
             if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
             if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
                 var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
                 var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
                 if (this.cameraRigMode === Camera.RIG_MODE_VR) {
                 if (this.cameraRigMode === Camera.RIG_MODE_VR) {
+                    if (!this.rotationQuaternion) {
+                        this.rotationQuaternion = new Quaternion();
+                    }
                     rigCamera._cameraRigParams = {};
                     rigCamera._cameraRigParams = {};
                     rigCamera._cameraRigParams.vrActualUp = new Vector3(0, 0, 0);
                     rigCamera._cameraRigParams.vrActualUp = new Vector3(0, 0, 0);
                     rigCamera._getViewMatrix = rigCamera._getVRViewMatrix;
                     rigCamera._getViewMatrix = rigCamera._getVRViewMatrix;
+                    rigCamera.rotationQuaternion = new Quaternion();
                 }
                 }
                 return rigCamera;
                 return rigCamera;
             }
             }
             return null;
             return null;
         }
         }
-        
+
         /**
         /**
          * @override
          * @override
          * Override Camera._updateRigCameras
          * Override Camera._updateRigCameras
@@ -268,20 +287,18 @@
                 case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
                 case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
                 case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
                 case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
                     //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
                     //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
-                    var leftSign  = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ?  1 : -1;
-                    var rightSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 :  1;
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * leftSign , camLeft.position);
+                    var leftSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
+                    var rightSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
+                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft.position);
                     this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * rightSign, camRight.position);
                     this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * rightSign, camRight.position);
 
 
                     camLeft.setTarget(this.getTarget());
                     camLeft.setTarget(this.getTarget());
                     camRight.setTarget(this.getTarget());
                     camRight.setTarget(this.getTarget());
                     break;
                     break;
-                    
-                case Camera.RIG_MODE_VR:
-                    camLeft.rotation.x = camRight.rotation.x = this.rotation.x;
-                    camLeft.rotation.y = camRight.rotation.y = this.rotation.y;
-                    camLeft.rotation.z = camRight.rotation.z = this.rotation.z;
 
 
+                case Camera.RIG_MODE_VR:
+                    camLeft.rotationQuaternion.copyFrom(this.rotationQuaternion);
+                    camRight.rotationQuaternion.copyFrom(this.rotationQuaternion);
                     camLeft.position.copyFrom(this.position);
                     camLeft.position.copyFrom(this.position);
                     camRight.position.copyFrom(this.position);
                     camRight.position.copyFrom(this.position);
 
 

+ 1 - 1
src/Cameras/babylon.touchCamera.js

@@ -53,6 +53,6 @@ var BABYLON;
             }
             }
         };
         };
         return TouchCamera;
         return TouchCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.TouchCamera = TouchCamera;
     BABYLON.TouchCamera = TouchCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.universalCamera.js

@@ -46,6 +46,6 @@ var BABYLON;
             return "UniversalCamera";
             return "UniversalCamera";
         };
         };
         return UniversalCamera;
         return UniversalCamera;
-    }(BABYLON.TouchCamera));
+    })(BABYLON.TouchCamera);
     BABYLON.UniversalCamera = UniversalCamera;
     BABYLON.UniversalCamera = UniversalCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Cameras/babylon.virtualJoysticksCamera.js

@@ -13,6 +13,6 @@ var BABYLON;
             this.inputs.addVirtualJoystick();
             this.inputs.addVirtualJoystick();
         }
         }
         return VirtualJoysticksCamera;
         return VirtualJoysticksCamera;
-    }(BABYLON.FreeCamera));
+    })(BABYLON.FreeCamera);
     BABYLON.VirtualJoysticksCamera = VirtualJoysticksCamera;
     BABYLON.VirtualJoysticksCamera = VirtualJoysticksCamera;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 43 - 28
src/Canvas2d/babylon.bounding2d.js

@@ -10,44 +10,58 @@ var BABYLON;
             this.center = BABYLON.Vector2.Zero();
             this.center = BABYLON.Vector2.Zero();
             this.extent = BABYLON.Vector2.Zero();
             this.extent = BABYLON.Vector2.Zero();
         }
         }
-        BoundingInfo2D.CreateFromSize = function (size) {
+        BoundingInfo2D.CreateFromSize = function (size, origin) {
             var r = new BoundingInfo2D();
             var r = new BoundingInfo2D();
-            BoundingInfo2D.CreateFromSizeToRef(size, r);
+            BoundingInfo2D.CreateFromSizeToRef(size, r, origin);
             return r;
             return r;
         };
         };
-        BoundingInfo2D.CreateFromRadius = function (radius) {
+        BoundingInfo2D.CreateFromRadius = function (radius, origin) {
             var r = new BoundingInfo2D();
             var r = new BoundingInfo2D();
-            BoundingInfo2D.CreateFromRadiusToRef(radius, r);
+            BoundingInfo2D.CreateFromRadiusToRef(radius, r, origin);
             return r;
             return r;
         };
         };
-        BoundingInfo2D.CreateFromPoints = function (points) {
+        BoundingInfo2D.CreateFromPoints = function (points, origin) {
             var r = new BoundingInfo2D();
             var r = new BoundingInfo2D();
-            BoundingInfo2D.CreateFromPointsToRef(points, r);
+            BoundingInfo2D.CreateFromPointsToRef(points, r, origin);
             return r;
             return r;
         };
         };
-        BoundingInfo2D.CreateFromSizeToRef = function (size, b) {
+        BoundingInfo2D.CreateFromSizeToRef = function (size, b, origin) {
             b.center = new BABYLON.Vector2(size.width / 2, size.height / 2);
             b.center = new BABYLON.Vector2(size.width / 2, size.height / 2);
             b.extent = b.center.clone();
             b.extent = b.center.clone();
+            if (origin) {
+                b.center.x -= size.width * origin.x;
+                b.center.y -= size.height * origin.y;
+            }
             b.radius = b.extent.length();
             b.radius = b.extent.length();
         };
         };
-        BoundingInfo2D.CreateFromRadiusToRef = function (radius, b) {
+        BoundingInfo2D.CreateFromRadiusToRef = function (radius, b, origin) {
             b.center = BABYLON.Vector2.Zero();
             b.center = BABYLON.Vector2.Zero();
+            if (origin) {
+                b.center.x -= radius * origin.x;
+                b.center.y -= radius * origin.y;
+            }
             b.extent = new BABYLON.Vector2(radius, radius);
             b.extent = new BABYLON.Vector2(radius, radius);
             b.radius = radius;
             b.radius = radius;
         };
         };
-        BoundingInfo2D.CreateFromPointsToRef = function (points, b) {
+        BoundingInfo2D.CreateFromPointsToRef = function (points, b, origin) {
             var xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
             var xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
-            for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
-                var p = points_1[_i];
+            for (var _i = 0; _i < points.length; _i++) {
+                var p = points[_i];
                 xmin = Math.min(p.x, xmin);
                 xmin = Math.min(p.x, xmin);
                 xmax = Math.max(p.x, xmax);
                 xmax = Math.max(p.x, xmax);
                 ymin = Math.min(p.y, ymin);
                 ymin = Math.min(p.y, ymin);
                 ymax = Math.max(p.y, ymax);
                 ymax = Math.max(p.y, ymax);
             }
             }
-            BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, b);
+            BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, b, origin);
         };
         };
-        BoundingInfo2D.CreateFromMinMaxToRef = function (xmin, xmax, ymin, ymax, b) {
-            b.center = new BABYLON.Vector2(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2);
+        BoundingInfo2D.CreateFromMinMaxToRef = function (xmin, xmax, ymin, ymax, b, origin) {
+            var w = xmax - xmin;
+            var h = ymax - ymin;
+            b.center = new BABYLON.Vector2(xmin + w / 2, ymin + h / 2);
+            if (origin) {
+                b.center.x -= w * origin.x;
+                b.center.y -= h * origin.y;
+            }
             b.extent = new BABYLON.Vector2(xmax - b.center.x, ymax - b.center.y);
             b.extent = new BABYLON.Vector2(xmax - b.center.x, ymax - b.center.y);
             b.radius = b.extent.length();
             b.radius = b.extent.length();
         };
         };
@@ -76,10 +90,9 @@ var BABYLON;
          * @param matrix the transformation matrix to apply
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
          */
          */
-        BoundingInfo2D.prototype.transform = function (matrix, origin) {
-            if (origin === void 0) { origin = null; }
+        BoundingInfo2D.prototype.transform = function (matrix) {
             var r = new BoundingInfo2D();
             var r = new BoundingInfo2D();
-            this.transformToRef(matrix, origin, r);
+            this.transformToRef(matrix, r);
             return r;
             return r;
         };
         };
         /**
         /**
@@ -94,24 +107,17 @@ var BABYLON;
         };
         };
         /**
         /**
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
-         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it everytime.
-         * @param origin An optional normalized origin to apply before the transformation. 0;0 is top/left, 0.5;0.5 is center, etc.
+         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
          * @param matrix The matrix to use to compute the transformation
          * @param matrix The matrix to use to compute the transformation
          * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
          * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
          */
          */
-        BoundingInfo2D.prototype.transformToRef = function (matrix, origin, result) {
+        BoundingInfo2D.prototype.transformToRef = function (matrix, result) {
             // Construct a bounding box based on the extent values
             // Construct a bounding box based on the extent values
             var p = new Array(4);
             var p = new Array(4);
             p[0] = new BABYLON.Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
             p[0] = new BABYLON.Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
             p[1] = new BABYLON.Vector2(this.center.x + this.extent.x, this.center.y - this.extent.y);
             p[1] = new BABYLON.Vector2(this.center.x + this.extent.x, this.center.y - this.extent.y);
             p[2] = new BABYLON.Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
             p[2] = new BABYLON.Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
             p[3] = new BABYLON.Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
             p[3] = new BABYLON.Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
-            //if (origin) {
-            //    let off = new Vector2((p[0].x - p[2].x) * origin.x, (p[0].y - p[2].y) * origin.y);
-            //    for (let j = 0; j < 4; j++) {
-            //        p[j].subtractInPlace(off);
-            //    }
-            //}
             // Transform the four points of the bounding box with the matrix
             // Transform the four points of the bounding box with the matrix
             for (var i = 0; i < 4; i++) {
             for (var i = 0; i < 4; i++) {
                 BABYLON.Vector2.TransformToRef(p[i], matrix, p[i]);
                 BABYLON.Vector2.TransformToRef(p[i], matrix, p[i]);
@@ -120,7 +126,7 @@ var BABYLON;
         };
         };
         /**
         /**
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
-         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it everytime.
+         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
          * @param other the second object used to compute the union
          * @param other the second object used to compute the union
          * @param result a VALID BoundingInfo2D instance (i.e. allocated) where the result will be stored
          * @param result a VALID BoundingInfo2D instance (i.e. allocated) where the result will be stored
          */
          */
@@ -131,7 +137,16 @@ var BABYLON;
             var ymin = Math.min(this.center.y - this.extent.y, other.center.y - other.extent.y);
             var ymin = Math.min(this.center.y - this.extent.y, other.center.y - other.extent.y);
             BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, result);
             BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, result);
         };
         };
+        BoundingInfo2D.prototype.doesIntersect = function (pickPosition) {
+            // is it inside the radius?
+            var pickLocal = pickPosition.subtract(this.center);
+            if (pickLocal.lengthSquared() <= (this.radius * this.radius)) {
+                // is it inside the rectangle?
+                return ((Math.abs(pickLocal.x) <= this.extent.x) && (Math.abs(pickLocal.y) <= this.extent.y));
+            }
+            return false;
+        };
         return BoundingInfo2D;
         return BoundingInfo2D;
-    }());
+    })();
     BABYLON.BoundingInfo2D = BoundingInfo2D;
     BABYLON.BoundingInfo2D = BoundingInfo2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 40 - 26
src/Canvas2d/babylon.bounding2d.ts

@@ -28,38 +28,46 @@
             this.extent = Vector2.Zero();
             this.extent = Vector2.Zero();
         }
         }
 
 
-        public static CreateFromSize(size: Size): BoundingInfo2D {
+        public static CreateFromSize(size: Size, origin?: Vector2): BoundingInfo2D {
             let r = new BoundingInfo2D();
             let r = new BoundingInfo2D();
-            BoundingInfo2D.CreateFromSizeToRef(size, r);
+            BoundingInfo2D.CreateFromSizeToRef(size, r, origin);
             return r;
             return r;
         }
         }
 
 
-        public static CreateFromRadius(radius: number): BoundingInfo2D {
+        public static CreateFromRadius(radius: number, origin?: Vector2): BoundingInfo2D {
             let r = new BoundingInfo2D();
             let r = new BoundingInfo2D();
-            BoundingInfo2D.CreateFromRadiusToRef(radius, r);
+            BoundingInfo2D.CreateFromRadiusToRef(radius, r, origin);
             return r;
             return r;
         }
         }
 
 
-        public static CreateFromPoints(points: Vector2[]): BoundingInfo2D {
+        public static CreateFromPoints(points: Vector2[], origin?: Vector2): BoundingInfo2D {
             let r = new BoundingInfo2D();
             let r = new BoundingInfo2D();
-            BoundingInfo2D.CreateFromPointsToRef(points, r);
+            BoundingInfo2D.CreateFromPointsToRef(points, r, origin);
 
 
             return r;
             return r;
         }
         }
 
 
-        public static CreateFromSizeToRef(size: Size, b: BoundingInfo2D) {
+        public static CreateFromSizeToRef(size: Size, b: BoundingInfo2D, origin?: Vector2) {
             b.center = new Vector2(size.width / 2, size.height / 2);
             b.center = new Vector2(size.width / 2, size.height / 2);
             b.extent = b.center.clone();
             b.extent = b.center.clone();
+            if (origin) {
+                b.center.x -= size.width * origin.x;
+                b.center.y -= size.height * origin.y;
+            }
             b.radius = b.extent.length();
             b.radius = b.extent.length();
         }
         }
 
 
-        public static CreateFromRadiusToRef(radius: number, b: BoundingInfo2D) {
+        public static CreateFromRadiusToRef(radius: number, b: BoundingInfo2D, origin?: Vector2) {
             b.center = Vector2.Zero();
             b.center = Vector2.Zero();
+            if (origin) {
+                b.center.x -= radius * origin.x;
+                b.center.y -= radius * origin.y;
+            }
             b.extent = new Vector2(radius, radius);
             b.extent = new Vector2(radius, radius);
             b.radius = radius;
             b.radius = radius;
         }
         }
 
 
-        public static CreateFromPointsToRef(points: Vector2[], b: BoundingInfo2D) {
+        public static CreateFromPointsToRef(points: Vector2[], b: BoundingInfo2D, origin?: Vector2) {
             let xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
             let xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
             for (let p of points) {
             for (let p of points) {
                 xmin = Math.min(p.x, xmin);
                 xmin = Math.min(p.x, xmin);
@@ -67,12 +75,17 @@
                 ymin = Math.min(p.y, ymin);
                 ymin = Math.min(p.y, ymin);
                 ymax = Math.max(p.y, ymax);
                 ymax = Math.max(p.y, ymax);
             }
             }
-            BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, b);
+            BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, b, origin);
         }
         }
 
 
-
-        public static CreateFromMinMaxToRef(xmin: number, xmax: number, ymin: number, ymax: number, b: BoundingInfo2D) {
-            b.center = new Vector2(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2);
+        public static CreateFromMinMaxToRef(xmin: number, xmax: number, ymin: number, ymax: number, b: BoundingInfo2D, origin?: Vector2) {
+            let w = xmax - xmin;
+            let h = ymax - ymin;
+            b.center = new Vector2(xmin + w / 2, ymin + h / 2);
+            if (origin) {
+                b.center.x -= w * origin.x;
+                b.center.y -= h * origin.y;
+            }
             b.extent = new Vector2(xmax - b.center.x, ymax - b.center.y);
             b.extent = new Vector2(xmax - b.center.x, ymax - b.center.y);
             b.radius = b.extent.length();
             b.radius = b.extent.length();
         }
         }
@@ -105,9 +118,9 @@
          * @param matrix the transformation matrix to apply
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
          */
          */
-        public transform(matrix: Matrix, origin: Vector2=null): BoundingInfo2D {
+        public transform(matrix: Matrix): BoundingInfo2D {
             var r = new BoundingInfo2D();
             var r = new BoundingInfo2D();
-            this.transformToRef(matrix, origin, r);
+            this.transformToRef(matrix, r);
             return r;
             return r;
         }
         }
 
 
@@ -124,12 +137,11 @@
 
 
         /**
         /**
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
-         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it everytime.
-         * @param origin An optional normalized origin to apply before the transformation. 0;0 is top/left, 0.5;0.5 is center, etc.
+         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
          * @param matrix The matrix to use to compute the transformation
          * @param matrix The matrix to use to compute the transformation
          * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
          * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
          */
          */
-        public transformToRef(matrix: Matrix, origin: Vector2, result: BoundingInfo2D) {
+        public transformToRef(matrix: Matrix, result: BoundingInfo2D) {
             // Construct a bounding box based on the extent values
             // Construct a bounding box based on the extent values
             let p = new Array<Vector2>(4);
             let p = new Array<Vector2>(4);
             p[0] = new Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
             p[0] = new Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
@@ -137,13 +149,6 @@
             p[2] = new Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
             p[2] = new Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
             p[3] = new Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
             p[3] = new Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
 
 
-            //if (origin) {
-            //    let off = new Vector2((p[0].x - p[2].x) * origin.x, (p[0].y - p[2].y) * origin.y);
-            //    for (let j = 0; j < 4; j++) {
-            //        p[j].subtractInPlace(off);
-            //    }
-            //}
-
             // Transform the four points of the bounding box with the matrix
             // Transform the four points of the bounding box with the matrix
             for (let i = 0; i < 4; i++) {
             for (let i = 0; i < 4; i++) {
                 Vector2.TransformToRef(p[i], matrix, p[i]);
                 Vector2.TransformToRef(p[i], matrix, p[i]);
@@ -153,7 +158,7 @@
 
 
         /**
         /**
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
-         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it everytime.
+         * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
          * @param other the second object used to compute the union
          * @param other the second object used to compute the union
          * @param result a VALID BoundingInfo2D instance (i.e. allocated) where the result will be stored
          * @param result a VALID BoundingInfo2D instance (i.e. allocated) where the result will be stored
          */
          */
@@ -165,5 +170,14 @@
             BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, result);
             BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, result);
         }
         }
 
 
+        doesIntersect(pickPosition: Vector2): boolean {
+            // is it inside the radius?
+            let pickLocal = pickPosition.subtract(this.center);
+            if (pickLocal.lengthSquared() <= (this.radius * this.radius)) {
+                // is it inside the rectangle?
+                return ((Math.abs(pickLocal.x) <= this.extent.x) && (Math.abs(pickLocal.y) <= this.extent.y));
+            }
+            return false;
+        }
     }
     }
 }
 }

+ 5 - 5
src/Canvas2d/babylon.brushes2d.js

@@ -12,7 +12,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     /**
     /**
-     * Base class implemting the ILocable interface.
+     * Base class implementing the ILocable interface.
      * The particularity of this class is to call the protected onLock() method when the instance is about to be locked for good.
      * The particularity of this class is to call the protected onLock() method when the instance is about to be locked for good.
      */
      */
     var LockableBase = (function () {
     var LockableBase = (function () {
@@ -35,10 +35,10 @@ var BABYLON;
         LockableBase.prototype.onLock = function () {
         LockableBase.prototype.onLock = function () {
         };
         };
         return LockableBase;
         return LockableBase;
-    }());
+    })();
     BABYLON.LockableBase = LockableBase;
     BABYLON.LockableBase = LockableBase;
     /**
     /**
-     * This classs implements a Brush that will be drawn with a uniform solid color (i.e. the same color everywhere in the content where the brush is assigned to).
+     * This class implements a Brush that will be drawn with a uniform solid color (i.e. the same color everywhere in the content where the brush is assigned to).
      */
      */
     var SolidColorBrush2D = (function (_super) {
     var SolidColorBrush2D = (function (_super) {
         __extends(SolidColorBrush2D, _super);
         __extends(SolidColorBrush2D, _super);
@@ -82,7 +82,7 @@ var BABYLON;
             BABYLON.className("SolidColorBrush2D")
             BABYLON.className("SolidColorBrush2D")
         ], SolidColorBrush2D);
         ], SolidColorBrush2D);
         return SolidColorBrush2D;
         return SolidColorBrush2D;
-    }(LockableBase));
+    })(LockableBase);
     BABYLON.SolidColorBrush2D = SolidColorBrush2D;
     BABYLON.SolidColorBrush2D = SolidColorBrush2D;
     var GradientColorBrush2D = (function (_super) {
     var GradientColorBrush2D = (function (_super) {
         __extends(GradientColorBrush2D, _super);
         __extends(GradientColorBrush2D, _super);
@@ -179,6 +179,6 @@ var BABYLON;
             BABYLON.className("GradientColorBrush2D")
             BABYLON.className("GradientColorBrush2D")
         ], GradientColorBrush2D);
         ], GradientColorBrush2D);
         return GradientColorBrush2D;
         return GradientColorBrush2D;
-    }(LockableBase));
+    })(LockableBase);
     BABYLON.GradientColorBrush2D = GradientColorBrush2D;
     BABYLON.GradientColorBrush2D = GradientColorBrush2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 3 - 3
src/Canvas2d/babylon.brushes2d.ts

@@ -25,7 +25,7 @@
      */
      */
     export interface IBrush2D extends ILockable {
     export interface IBrush2D extends ILockable {
         /**
         /**
-         * Define if the brush will use transparency/alphablending
+         * Define if the brush will use transparency / alpha blending
          * @returns true if the brush use transparency
          * @returns true if the brush use transparency
          */
          */
         isTransparent(): boolean;
         isTransparent(): boolean;
@@ -38,7 +38,7 @@
     }
     }
 
 
     /**
     /**
-     * Base class implemting the ILocable interface.
+     * Base class implementing the ILocable interface.
      * The particularity of this class is to call the protected onLock() method when the instance is about to be locked for good.
      * The particularity of this class is to call the protected onLock() method when the instance is about to be locked for good.
      */
      */
     export class LockableBase implements ILockable {
     export class LockableBase implements ILockable {
@@ -67,7 +67,7 @@
     }
     }
 
 
     /**
     /**
-     * This classs implements a Brush that will be drawn with a uniform solid color (i.e. the same color everywhere in the content where the brush is assigned to).
+     * This class implements a Brush that will be drawn with a uniform solid color (i.e. the same color everywhere in the content where the brush is assigned to).
      */
      */
     @className("SolidColorBrush2D")
     @className("SolidColorBrush2D")
     export class SolidColorBrush2D extends LockableBase implements IBrush2D {
     export class SolidColorBrush2D extends LockableBase implements IBrush2D {

+ 409 - 43
src/Canvas2d/babylon.canvas2d.js

@@ -26,29 +26,33 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Canvas2DEngineBoundData;
         return Canvas2DEngineBoundData;
-    }());
+    })();
     BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
     BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
     var Canvas2D = (function (_super) {
     var Canvas2D = (function (_super) {
         __extends(Canvas2D, _super);
         __extends(Canvas2D, _super);
         function Canvas2D() {
         function Canvas2D() {
             _super.apply(this, arguments);
             _super.apply(this, arguments);
+            this._notifDebugMode = false;
             this._mapCounter = 0;
             this._mapCounter = 0;
         }
         }
         /**
         /**
          * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the top/left corner of the screen.
          * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the top/left corner of the screen.
          * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
          * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
          * All caching strategies will be available.
          * All caching strategies will be available.
+         * PLEASE NOTE: the origin of a Screen Space Canvas is set to [0;0] (bottom/left) which is different than the default origin of a Primitive which is centered [0.5;0.5]
          * @param scene the Scene that owns the Canvas
          * @param scene the Scene that owns the Canvas
          * @param name the name of the Canvas, for information purpose only
          * @param name the name of the Canvas, for information purpose only
          * @param pos the position of the canvas, relative from the bottom/left of the scene's viewport
          * @param pos the position of the canvas, relative from the bottom/left of the scene's viewport
          * @param size the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
          * @param size the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
          * @param cachingStrategy either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information.
          * @param cachingStrategy either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information.
          */
          */
-        Canvas2D.CreateScreenSpace = function (scene, name, pos, size, cachingStrategy) {
+        Canvas2D.CreateScreenSpace = function (scene, name, pos, size, cachingStrategy, enableInteraction) {
             if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
             if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
+            if (enableInteraction === void 0) { enableInteraction = true; }
             var c = new Canvas2D();
             var c = new Canvas2D();
-            c.setupCanvas(scene, name, size, true, cachingStrategy);
+            c.setupCanvas(scene, name, size, true, cachingStrategy, enableInteraction);
             c.position = pos;
             c.position = pos;
+            c.origin = BABYLON.Vector2.Zero();
             return c;
             return c;
         };
         };
         /**
         /**
@@ -65,9 +69,10 @@ var BABYLON;
          * @param sideOrientation Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one.
          * @param sideOrientation Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one.
          * @param cachingStrategy Must be CACHESTRATEGY_CANVAS for now
          * @param cachingStrategy Must be CACHESTRATEGY_CANVAS for now
          */
          */
-        Canvas2D.CreateWorldSpace = function (scene, name, position, rotation, size, renderScaleFactor, sideOrientation, cachingStrategy) {
+        Canvas2D.CreateWorldSpace = function (scene, name, position, rotation, size, renderScaleFactor, sideOrientation, cachingStrategy, enableInteraction) {
             if (renderScaleFactor === void 0) { renderScaleFactor = 1; }
             if (renderScaleFactor === void 0) { renderScaleFactor = 1; }
             if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
             if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
+            if (enableInteraction === void 0) { enableInteraction = true; }
             if (cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
             if (cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
             }
             }
@@ -78,7 +83,7 @@ var BABYLON;
                 sideOrientation = BABYLON.Mesh.DEFAULTSIDE;
                 sideOrientation = BABYLON.Mesh.DEFAULTSIDE;
             }
             }
             var c = new Canvas2D();
             var c = new Canvas2D();
-            c.setupCanvas(scene, name, new BABYLON.Size(size.width * renderScaleFactor, size.height * renderScaleFactor), false, cachingStrategy);
+            c.setupCanvas(scene, name, new BABYLON.Size(size.width * renderScaleFactor, size.height * renderScaleFactor), false, cachingStrategy, enableInteraction);
             var plane = new BABYLON.WorldSpaceCanvas2d(name, scene, c);
             var plane = new BABYLON.WorldSpaceCanvas2d(name, scene, c);
             var vertexData = BABYLON.VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: sideOrientation });
             var vertexData = BABYLON.VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: sideOrientation });
             var mtl = new BABYLON.StandardMaterial(name + "_Material", scene);
             var mtl = new BABYLON.StandardMaterial(name + "_Material", scene);
@@ -93,10 +98,8 @@ var BABYLON;
             c._worldSpaceNode = plane;
             c._worldSpaceNode = plane;
             return c;
             return c;
         };
         };
-        Canvas2D.prototype.setupCanvas = function (scene, name, size, isScreenSpace, cachingstrategy) {
+        Canvas2D.prototype.setupCanvas = function (scene, name, size, isScreenSpace, cachingstrategy, enableInteraction) {
             var _this = this;
             var _this = this;
-            if (isScreenSpace === void 0) { isScreenSpace = true; }
-            if (cachingstrategy === void 0) { cachingstrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
             var engine = scene.getEngine();
             var engine = scene.getEngine();
             this._fitRenderingDevice = !size;
             this._fitRenderingDevice = !size;
             if (!size) {
             if (!size) {
@@ -104,12 +107,13 @@ var BABYLON;
             }
             }
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
             this._cachingStrategy = cachingstrategy;
             this._cachingStrategy = cachingstrategy;
-            this._depthLevel = 0;
-            this._hierarchyMaxDepth = 100;
-            this._hierarchyLevelZFactor = 1 / this._hierarchyMaxDepth;
-            this._hierarchyLevelMaxSiblingCount = 1000;
-            this._hierarchySiblingZDelta = this._hierarchyLevelZFactor / this._hierarchyLevelMaxSiblingCount;
+            this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
+            this._capturedPointers = new BABYLON.StringDictionary();
+            this._pickStartingPosition = BABYLON.Vector2.Zero();
             this.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), size, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
             this.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), size, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
+            this._hierarchyLevelMaxSiblingCount = 100;
+            this._hierarchyDepthOffset = 0;
+            this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
             this._scene = scene;
             this._scene = scene;
             this._engine = engine;
             this._engine = engine;
             this._renderingSize = new BABYLON.Size(0, 0);
             this._renderingSize = new BABYLON.Size(0, 0);
@@ -119,6 +123,7 @@ var BABYLON;
             });
             });
             if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
             if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
                 this._background = BABYLON.Rectangle2D.Create(this, "###CANVAS BACKGROUND###", 0, 0, size.width, size.height);
                 this._background = BABYLON.Rectangle2D.Create(this, "###CANVAS BACKGROUND###", 0, 0, size.width, size.height);
+                this._background.isPickable = false;
                 this._background.origin = BABYLON.Vector2.Zero();
                 this._background.origin = BABYLON.Vector2.Zero();
                 this._background.levelVisible = false;
                 this._background.levelVisible = false;
             }
             }
@@ -136,6 +141,360 @@ var BABYLON;
             }
             }
             this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
             this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
             //            this._supprtInstancedArray = false; // TODO REMOVE!!!
             //            this._supprtInstancedArray = false; // TODO REMOVE!!!
+            this._setupInteraction(enableInteraction);
+        };
+        Object.defineProperty(Canvas2D.prototype, "hierarchyLevelMaxSiblingCount", {
+            get: function () {
+                return this._hierarchyLevelMaxSiblingCount;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Canvas2D.prototype._setupInteraction = function (enable) {
+            var _this = this;
+            // No change detection
+            if (enable === this._interactionEnabled) {
+                return;
+            }
+            // Set the new state
+            this._interactionEnabled = enable;
+            // Disable interaction
+            if (!enable) {
+                if (this._scenePrePointerObserver) {
+                    this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
+                    this._scenePrePointerObserver = null;
+                }
+                return;
+            }
+            // Enable Interaction
+            // Register the observable
+            this.scene.onPrePointerObservable.add(function (e, s) { return _this._handlePointerEventForInteraction(e, s); });
+        };
+        /**
+         * Internal method, you should use the Prim2DBase version instead
+         */
+        Canvas2D.prototype._setPointerCapture = function (pointerId, primitive) {
+            if (this.isPointerCaptured(pointerId)) {
+                return false;
+            }
+            // Try to capture the pointer on the HTML side
+            try {
+                this.engine.getRenderingCanvas().setPointerCapture(pointerId);
+            }
+            catch (e) {
+            }
+            this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
+            this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerGotCapture, null);
+            this._capturedPointers.add(pointerId.toString(), primitive);
+            return true;
+        };
+        /**
+         * Internal method, you should use the Prim2DBase version instead
+         */
+        Canvas2D.prototype._releasePointerCapture = function (pointerId, primitive) {
+            if (this._capturedPointers.get(pointerId.toString()) !== primitive) {
+                return false;
+            }
+            // Try to release the pointer on the HTML side
+            try {
+                this.engine.getRenderingCanvas().releasePointerCapture(pointerId);
+            }
+            catch (e) {
+            }
+            this._primPointerInfo.updateRelatedTarget(primitive, BABYLON.Vector2.Zero());
+            this._bubbleNotifyPrimPointerObserver(primitive, BABYLON.PrimitivePointerInfo.PointerLostCapture, null);
+            this._capturedPointers.remove(pointerId.toString());
+            return true;
+        };
+        /**
+         * Determine if the given pointer is captured or not
+         * @param pointerId the Id of the pointer
+         * @return true if it's captured, false otherwise
+         */
+        Canvas2D.prototype.isPointerCaptured = function (pointerId) {
+            return this._capturedPointers.contains(pointerId.toString());
+        };
+        Canvas2D.prototype.getCapturedPrimitive = function (pointerId) {
+            // Avoid unnecessary lookup
+            if (this._capturedPointers.count === 0) {
+                return null;
+            }
+            return this._capturedPointers.get(pointerId.toString());
+        };
+        Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, eventState) {
+            // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
+            this._updatePointerInfo(eventData);
+            var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
+            // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
+            // Why before rendering the canvas? because some primitives may move and get away/under the mouse cursor (which is not moving). So we need to update at both location in order to always have an accurate list, which is needed for the hover state change.
+            this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, capturedPrim !== null);
+            // Update the over status, same as above, it's could be done here or during rendering, but will be performed only once per render frame
+            this._updateOverStatus();
+            // Check if we have nothing to raise
+            if (!this._actualOverPrimitive && !capturedPrim) {
+                return;
+            }
+            // Update the relatedTarget info with the over primitive or the captured one (if any)
+            var targetPrim = capturedPrim || this._actualOverPrimitive.prim;
+            var targetPointerPos = capturedPrim ? this._primPointerInfo.canvasPointerPos.subtract(new BABYLON.Vector2(targetPrim.globalTransform.m[12], targetPrim.globalTransform.m[13])) : this._actualOverPrimitive.intersectionLocation;
+            this._primPointerInfo.updateRelatedTarget(targetPrim, targetPointerPos);
+            // Analyze the pointer event type and fire proper events on the primitive
+            if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMouseWheel, eventData.event);
+            }
+            else if (eventData.type === BABYLON.PointerEventTypes.POINTERMOVE) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerMove, eventData.event);
+            }
+            else if (eventData.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerDown, eventData.event);
+            }
+            else if (eventData.type === BABYLON.PointerEventTypes.POINTERUP) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData.event);
+            }
+        };
+        Canvas2D.prototype._updatePointerInfo = function (eventData) {
+            var pii = this._primPointerInfo;
+            if (!pii.canvasPointerPos) {
+                pii.canvasPointerPos = BABYLON.Vector2.Zero();
+            }
+            pii.canvasPointerPos.x = eventData.localPosition.x - this.position.x;
+            pii.canvasPointerPos.y = (this.engine.getRenderHeight() - eventData.localPosition.y) - this.position.y;
+            pii.mouseWheelDelta = 0;
+            if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
+                var event = eventData.event;
+                if (event.wheelDelta) {
+                    pii.mouseWheelDelta = event.wheelDelta / (BABYLON.PrimitivePointerInfo.MouseWheelPrecision * 40);
+                }
+                else if (event.detail) {
+                    pii.mouseWheelDelta = -event.detail / BABYLON.PrimitivePointerInfo.MouseWheelPrecision;
+                }
+            }
+            else {
+                var pe = eventData.event;
+                pii.ctrlKey = pe.ctrlKey;
+                pii.altKey = pe.altKey;
+                pii.shiftKey = pe.shiftKey;
+                pii.metaKey = pe.metaKey;
+                pii.button = pe.button;
+                pii.buttons = pe.buttons;
+                pii.pointerId = pe.pointerId;
+                pii.width = pe.width;
+                pii.height = pe.height;
+                pii.presssure = pe.pressure;
+                pii.tilt.x = pe.tiltX;
+                pii.tilt.y = pe.tiltY;
+                pii.isCaptured = this.getCapturedPrimitive(pe.pointerId) !== null;
+            }
+        };
+        Canvas2D.prototype._updateIntersectionList = function (mouseLocalPos, isCapture) {
+            if (this.scene.getRenderId() === this._intersectionRenderId) {
+                return;
+            }
+            var ii = Canvas2D._interInfo;
+            ii.pickPosition.x = mouseLocalPos.x;
+            ii.pickPosition.y = mouseLocalPos.y;
+            ii.findFirstOnly = false;
+            // Fast rejection: test if the mouse pointer is outside the canvas's bounding Info
+            if (!isCapture && !this.boundingInfo.doesIntersect(ii.pickPosition)) {
+                this._previousIntersectionList = this._actualIntersectionList;
+                this._actualIntersectionList = null;
+                this._previousOverPrimitive = this._actualOverPrimitive;
+                this._actualOverPrimitive = null;
+                return;
+            }
+            this._updateCanvasState();
+            this.intersect(ii);
+            this._previousIntersectionList = this._actualIntersectionList;
+            this._actualIntersectionList = ii.intersectedPrimitives;
+            this._previousOverPrimitive = this._actualOverPrimitive;
+            this._actualOverPrimitive = ii.topMostIntersectedPrimitive;
+            this._intersectionRenderId = this.scene.getRenderId();
+        };
+        // Based on the previousIntersectionList and the actualInstersectionList we can determined which primitives are being hover state or loosing it
+        Canvas2D.prototype._updateOverStatus = function () {
+            if ((this.scene.getRenderId() === this._hoverStatusRenderId) || !this._previousIntersectionList || !this._actualIntersectionList) {
+                return;
+            }
+            // Detect a change of over
+            var prevPrim = this._previousOverPrimitive ? this._previousOverPrimitive.prim : null;
+            var actualPrim = this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
+            if (prevPrim !== actualPrim) {
+                // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
+                var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
+                // Notify the previous "over" prim that the pointer is no longer over it
+                if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim)) {
+                    this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
+                    this._bubbleNotifyPrimPointerObserver(prevPrim, BABYLON.PrimitivePointerInfo.PointerOut, null);
+                }
+                // Notify the new "over" prim that the pointer is over it
+                if ((capturedPrim && capturedPrim === actualPrim) || (!capturedPrim && actualPrim)) {
+                    this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
+                    this._bubbleNotifyPrimPointerObserver(actualPrim, BABYLON.PrimitivePointerInfo.PointerOver, null);
+                }
+            }
+            this._hoverStatusRenderId = this.scene.getRenderId();
+        };
+        Canvas2D.prototype._updatePrimPointerPos = function (prim) {
+            if (this._primPointerInfo.isCaptured) {
+                this._primPointerInfo.primitivePointerPos = this._primPointerInfo.relatedTargetPointerPos;
+            }
+            else {
+                for (var _i = 0, _a = this._actualIntersectionList; _i < _a.length; _i++) {
+                    var pii = _a[_i];
+                    if (pii.prim === prim) {
+                        this._primPointerInfo.primitivePointerPos = pii.intersectionLocation;
+                        return;
+                    }
+                }
+            }
+        };
+        Canvas2D.prototype._debugExecObserver = function (prim, mask) {
+            if (!this._notifDebugMode) {
+                return;
+            }
+            var debug = "";
+            for (var i = 0; i < prim.hierarchyDepth; i++) {
+                debug += "  ";
+            }
+            var pii = this._primPointerInfo;
+            debug += "[RID:" + this.scene.getRenderId() + "] [" + prim.hierarchyDepth + "] event:" + BABYLON.PrimitivePointerInfo.getEventTypeName(mask) + ", id: " + prim.id + " (" + BABYLON.Tools.getClassName(prim) + "), primPos: " + pii.primitivePointerPos.toString() + ", canvasPos: " + pii.canvasPointerPos.toString();
+            console.log(debug);
+        };
+        Canvas2D.prototype._bubbleNotifyPrimPointerObserver = function (prim, mask, eventData) {
+            var ppi = this._primPointerInfo;
+            // In case of PointerOver/Out we will first notify the children (but the deepest to the closest) with PointerEnter/Leave
+            if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) !== 0) {
+                this._notifChildren(prim, mask);
+            }
+            var bubbleCancelled = false;
+            var cur = prim;
+            while (cur) {
+                // Only trigger the observers if the primitive is intersected (except for out)
+                if (!bubbleCancelled) {
+                    this._updatePrimPointerPos(cur);
+                    // Exec the observers
+                    this._debugExecObserver(cur, mask);
+                    cur._pointerEventObservable.notifyObservers(ppi, mask);
+                    this._triggerActionManager(cur, ppi, mask, eventData);
+                    // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
+                    // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
+                    if (ppi.cancelBubble) {
+                        if ((mask & (BABYLON.PrimitivePointerInfo.PointerOver | BABYLON.PrimitivePointerInfo.PointerOut)) === 0) {
+                            return;
+                        }
+                        // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
+                        bubbleCancelled = true;
+                    }
+                }
+                // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
+                if (bubbleCancelled) {
+                    this._updatePrimPointerPos(cur);
+                }
+                // Trigger a PointerEnter corresponding to the PointerOver
+                if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
+                    this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerEnter);
+                    cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerEnter);
+                }
+                else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
+                    this._debugExecObserver(cur, BABYLON.PrimitivePointerInfo.PointerLeave);
+                    cur._pointerEventObservable.notifyObservers(ppi, BABYLON.PrimitivePointerInfo.PointerLeave);
+                }
+                // Loop to the parent
+                cur = cur.parent;
+            }
+        };
+        Canvas2D.prototype._triggerActionManager = function (prim, ppi, mask, eventData) {
+            var _this = this;
+            // Process Trigger related to PointerDown
+            if ((mask & BABYLON.PrimitivePointerInfo.PointerDown) !== 0) {
+                // On pointer down, record the current position and time to be able to trick PickTrigger and LongPressTrigger
+                this._pickStartingPosition = ppi.primitivePointerPos.clone();
+                this._pickStartingTime = new Date().getTime();
+                this._pickedDownPrim = null;
+                if (prim.actionManager) {
+                    this._pickedDownPrim = prim;
+                    if (prim.actionManager.hasPickTriggers) {
+                        var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                        switch (eventData.button) {
+                            case 0:
+                                prim.actionManager.processTrigger(BABYLON.ActionManager.OnLeftPickTrigger, actionEvent);
+                                break;
+                            case 1:
+                                prim.actionManager.processTrigger(BABYLON.ActionManager.OnCenterPickTrigger, actionEvent);
+                                break;
+                            case 2:
+                                prim.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, actionEvent);
+                                break;
+                        }
+                        prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, actionEvent);
+                    }
+                    if (prim.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnLongPressTrigger)) {
+                        window.setTimeout(function () {
+                            var ppi = _this._primPointerInfo;
+                            var capturedPrim = _this.getCapturedPrimitive(ppi.pointerId);
+                            _this._updateIntersectionList(ppi.canvasPointerPos, capturedPrim !== null);
+                            var ii = new BABYLON.IntersectInfo2D();
+                            ii.pickPosition = ppi.canvasPointerPos.clone();
+                            ii.findFirstOnly = false;
+                            _this.intersect(ii);
+                            if (ii.isPrimIntersected(prim) !== null) {
+                                if (prim.actionManager) {
+                                    if (_this._pickStartingTime !== 0 && ((new Date().getTime() - _this._pickStartingTime) > BABYLON.ActionManager.LongPressDelay) && (Math.abs(_this._pickStartingPosition.x - ii.pickPosition.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(_this._pickStartingPosition.y - ii.pickPosition.y) < BABYLON.ActionManager.DragMovementThreshold)) {
+                                        _this._pickStartingTime = 0;
+                                        prim.actionManager.processTrigger(BABYLON.ActionManager.OnLongPressTrigger, BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData));
+                                    }
+                                }
+                            }
+                        }, BABYLON.ActionManager.LongPressDelay);
+                    }
+                }
+            }
+            else if ((mask & BABYLON.PrimitivePointerInfo.PointerUp) !== 0) {
+                this._pickStartingTime = 0;
+                var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                if (prim.actionManager) {
+                    // OnPickUpTrigger
+                    prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickUpTrigger, actionEvent);
+                    // OnPickTrigger
+                    if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < BABYLON.ActionManager.DragMovementThreshold) {
+                        prim.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, actionEvent);
+                    }
+                }
+                // OnPickOutTrigger
+                if (this._pickedDownPrim && this._pickedDownPrim.actionManager && (this._pickedDownPrim !== prim)) {
+                    this._pickedDownPrim.actionManager.processTrigger(BABYLON.ActionManager.OnPickOutTrigger, actionEvent);
+                }
+            }
+            else if ((mask & BABYLON.PrimitivePointerInfo.PointerOver) !== 0) {
+                if (prim.actionManager) {
+                    var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                    prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOverTrigger, actionEvent);
+                }
+            }
+            else if ((mask & BABYLON.PrimitivePointerInfo.PointerOut) !== 0) {
+                if (prim.actionManager) {
+                    var actionEvent = BABYLON.ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                    prim.actionManager.processTrigger(BABYLON.ActionManager.OnPointerOutTrigger, actionEvent);
+                }
+            }
+        };
+        Canvas2D.prototype._notifChildren = function (prim, mask) {
+            var _this = this;
+            var pii = this._primPointerInfo;
+            prim.children.forEach(function (curChild) {
+                // Recurse first, we want the deepest to be notified first
+                _this._notifChildren(curChild, mask);
+                _this._updatePrimPointerPos(curChild);
+                // Fire the proper notification
+                if (mask === BABYLON.PrimitivePointerInfo.PointerOver) {
+                    _this._debugExecObserver(curChild, BABYLON.PrimitivePointerInfo.PointerEnter);
+                    curChild._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerEnter);
+                }
+                else if (mask === BABYLON.PrimitivePointerInfo.PointerOut) {
+                    _this._debugExecObserver(curChild, BABYLON.PrimitivePointerInfo.PointerLeave);
+                    curChild._pointerEventObservable.notifyObservers(pii, BABYLON.PrimitivePointerInfo.PointerLeave);
+                }
+            });
         };
         };
         /**
         /**
          * Don't forget to call the dispose method when you're done with the Canvas instance.
          * Don't forget to call the dispose method when you're done with the Canvas instance.
@@ -145,6 +504,9 @@ var BABYLON;
             if (!_super.prototype.dispose.call(this)) {
             if (!_super.prototype.dispose.call(this)) {
                 return false;
                 return false;
             }
             }
+            if (this.interactionEnabled) {
+                this._setupInteraction(false);
+            }
             if (this._beforeRenderObserver) {
             if (this._beforeRenderObserver) {
                 this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
                 this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
                 this._beforeRenderObserver = null;
                 this._beforeRenderObserver = null;
@@ -280,46 +642,37 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        Object.defineProperty(Canvas2D.prototype, "_engineData", {
-            get: function () {
-                return this.__engineData;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Canvas2D.prototype.checkBackgroundAvailability = function () {
-            if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
-                throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
-            }
-        };
-        Object.defineProperty(Canvas2D.prototype, "hierarchySiblingZDelta", {
+        Object.defineProperty(Canvas2D.prototype, "interactionEnabled", {
             /**
             /**
-             * Read-only property that return the Z delta to apply for each sibling primitives inside of a given one.
-             * Sibling Primitives are defined in a specific order, the first ones will be draw below the next ones.
-             * This property define the Z value to apply between each sibling Primitive. Current implementation allows 1000 Siblings Primitives per level.
-             * @returns The Z Delta
+             * Enable/Disable interaction for this Canvas
+             * When enabled the Prim2DBase.pointerEventObservable property will notified when appropriate events occur
              */
              */
             get: function () {
             get: function () {
-                return this._hierarchySiblingZDelta;
+                return this._interactionEnabled;
+            },
+            set: function (enable) {
+                this._setupInteraction(enable);
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        Object.defineProperty(Canvas2D.prototype, "hierarchyLevelZFactor", {
-            /**
-             * Return the Z Factor that will be applied for each new hierarchy level.
-             * @returns The Z Factor
-             */
+        Object.defineProperty(Canvas2D.prototype, "_engineData", {
             get: function () {
             get: function () {
-                return this._hierarchyLevelZFactor;
+                return this.__engineData;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        /**
-         * Method that renders the Canvas, you should not invoke
-         */
-        Canvas2D.prototype._render = function () {
+        Canvas2D.prototype.checkBackgroundAvailability = function () {
+            if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
+                throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
+            }
+        };
+        Canvas2D.prototype._updateCanvasState = function () {
+            // Check if the update has already been made for this render Frame
+            if (this.scene.getRenderId() === this._updateRenderId) {
+                return;
+            }
             this._renderingSize.width = this.engine.getRenderWidth();
             this._renderingSize.width = this.engine.getRenderWidth();
             this._renderingSize.height = this.engine.getRenderHeight();
             this._renderingSize.height = this.engine.getRenderHeight();
             if (this._fitRenderingDevice) {
             if (this._fitRenderingDevice) {
@@ -333,6 +686,18 @@ var BABYLON;
             ++this._globalTransformProcessStep;
             ++this._globalTransformProcessStep;
             this.updateGlobalTransVis(false);
             this.updateGlobalTransVis(false);
             this._prepareGroupRender(context);
             this._prepareGroupRender(context);
+            this._updateRenderId = this.scene.getRenderId();
+        };
+        /**
+         * Method that renders the Canvas, you should not invoke
+         */
+        Canvas2D.prototype._render = function () {
+            this._updateCanvasState();
+            if (this._primPointerInfo.canvasPointerPos) {
+                this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
+                this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
+            }
+            var context = new BABYLON.Render2DContext();
             this._groupRender(context);
             this._groupRender(context);
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
@@ -393,7 +758,7 @@ var BABYLON;
                 }
                 }
                 else {
                 else {
                     var sprite = BABYLON.Sprite2D.Create(parent, "__cachedSpriteOfGroup__" + group.id, group.position.x, group.position.y, map, node.contentSize, node.pos, false);
                     var sprite = BABYLON.Sprite2D.Create(parent, "__cachedSpriteOfGroup__" + group.id, group.position.x, group.position.y, map, node.contentSize, node.pos, false);
-                    sprite.origin = BABYLON.Vector2.Zero();
+                    sprite.origin = group.origin.clone();
                     res.sprite = sprite;
                     res.sprite = sprite;
                 }
                 }
             }
             }
@@ -444,6 +809,7 @@ var BABYLON;
          * Note that you can't use this strategy for WorldSpace Canvas, they need at least a top level group caching.
          * Note that you can't use this strategy for WorldSpace Canvas, they need at least a top level group caching.
          */
          */
         Canvas2D.CACHESTRATEGY_DONTCACHE = 4;
         Canvas2D.CACHESTRATEGY_DONTCACHE = 4;
+        Canvas2D._interInfo = new BABYLON.IntersectInfo2D();
         /**
         /**
          * Define the default size used for both the width and height of a MapTexture to allocate.
          * Define the default size used for both the width and height of a MapTexture to allocate.
          * Note that some MapTexture might be bigger than this size if the first node to allocate is bigger in width or height
          * Note that some MapTexture might be bigger than this size if the first node to allocate is bigger in width or height
@@ -455,6 +821,6 @@ var BABYLON;
             BABYLON.className("Canvas2D")
             BABYLON.className("Canvas2D")
         ], Canvas2D);
         ], Canvas2D);
         return Canvas2D;
         return Canvas2D;
-    }(BABYLON.Group2D));
+    })(BABYLON.Group2D);
     BABYLON.Canvas2D = Canvas2D;
     BABYLON.Canvas2D = Canvas2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 487 - 36
src/Canvas2d/babylon.canvas2d.ts

@@ -51,21 +51,22 @@
          * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the top/left corner of the screen.
          * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the top/left corner of the screen.
          * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
          * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
          * All caching strategies will be available.
          * All caching strategies will be available.
+         * PLEASE NOTE: the origin of a Screen Space Canvas is set to [0;0] (bottom/left) which is different than the default origin of a Primitive which is centered [0.5;0.5]
          * @param scene the Scene that owns the Canvas
          * @param scene the Scene that owns the Canvas
          * @param name the name of the Canvas, for information purpose only
          * @param name the name of the Canvas, for information purpose only
          * @param pos the position of the canvas, relative from the bottom/left of the scene's viewport
          * @param pos the position of the canvas, relative from the bottom/left of the scene's viewport
          * @param size the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
          * @param size the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
          * @param cachingStrategy either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information.
          * @param cachingStrategy either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information.
          */
          */
-        static CreateScreenSpace(scene: Scene, name: string, pos: Vector2, size: Size, cachingStrategy: number = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS): Canvas2D {
+        static CreateScreenSpace(scene: Scene, name: string, pos: Vector2, size: Size, cachingStrategy: number = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, enableInteraction: boolean = true): Canvas2D {
             let c = new Canvas2D();
             let c = new Canvas2D();
-            c.setupCanvas(scene, name, size, true, cachingStrategy);
+            c.setupCanvas(scene, name, size, true, cachingStrategy, enableInteraction);
             c.position = pos;
             c.position = pos;
+            c.origin = Vector2.Zero();
 
 
             return c;
             return c;
         }
         }
 
 
-
         /**
         /**
          * Create a new 2D WorldSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a world transformation information to place it in the world space.
          * Create a new 2D WorldSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a world transformation information to place it in the world space.
          * This kind of canvas can't have its Primitives directly drawn in the Viewport, they need to be cached in a bitmap at some point, as a consequence the DONT_CACHE strategy is unavailable. For now only CACHESTRATEGY_CANVAS is supported, but the remaining strategies will be soon.
          * This kind of canvas can't have its Primitives directly drawn in the Viewport, they need to be cached in a bitmap at some point, as a consequence the DONT_CACHE strategy is unavailable. For now only CACHESTRATEGY_CANVAS is supported, but the remaining strategies will be soon.
@@ -80,7 +81,7 @@
          * @param sideOrientation Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one.
          * @param sideOrientation Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one.
          * @param cachingStrategy Must be CACHESTRATEGY_CANVAS for now
          * @param cachingStrategy Must be CACHESTRATEGY_CANVAS for now
          */
          */
-        static CreateWorldSpace(scene: Scene, name: string, position: Vector3, rotation: Quaternion, size: Size, renderScaleFactor: number=1, sideOrientation?: number, cachingStrategy: number = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS): Canvas2D {
+        static CreateWorldSpace(scene: Scene, name: string, position: Vector3, rotation: Quaternion, size: Size, renderScaleFactor: number = 1, sideOrientation?: number, cachingStrategy: number = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, enableInteraction: boolean = true): Canvas2D {
             if (cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
             if (cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
             }
             }
@@ -94,7 +95,7 @@
             }
             }
 
 
             let c = new Canvas2D();
             let c = new Canvas2D();
-            c.setupCanvas(scene, name, new Size(size.width*renderScaleFactor, size.height*renderScaleFactor), false, cachingStrategy);
+            c.setupCanvas(scene, name, new Size(size.width*renderScaleFactor, size.height*renderScaleFactor), false, cachingStrategy, enableInteraction);
 
 
             let plane = new WorldSpaceCanvas2d(name, scene, c);
             let plane = new WorldSpaceCanvas2d(name, scene, c);
             let vertexData = VertexData.CreatePlane({ width: size.width/2, height: size.height/2, sideOrientation: sideOrientation });
             let vertexData = VertexData.CreatePlane({ width: size.width/2, height: size.height/2, sideOrientation: sideOrientation });
@@ -114,7 +115,7 @@
             return c;
             return c;
         }
         }
 
 
-        protected setupCanvas(scene: Scene, name: string, size: Size, isScreenSpace: boolean = true, cachingstrategy: number = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
+        protected setupCanvas(scene: Scene, name: string, size: Size, isScreenSpace: boolean, cachingstrategy: number, enableInteraction: boolean) {
             let engine = scene.getEngine();
             let engine = scene.getEngine();
             this._fitRenderingDevice = !size;
             this._fitRenderingDevice = !size;
             if (!size) {
             if (!size) {
@@ -122,14 +123,15 @@
             }
             }
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", k => new Canvas2DEngineBoundData());
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", k => new Canvas2DEngineBoundData());
             this._cachingStrategy = cachingstrategy;
             this._cachingStrategy = cachingstrategy;
-            this._depthLevel = 0;
-            this._hierarchyMaxDepth = 100;
-            this._hierarchyLevelZFactor = 1 / this._hierarchyMaxDepth;
-            this._hierarchyLevelMaxSiblingCount = 1000;
-            this._hierarchySiblingZDelta = this._hierarchyLevelZFactor / this._hierarchyLevelMaxSiblingCount;
+            this._primPointerInfo = new PrimitivePointerInfo();
+            this._capturedPointers = new StringDictionary<Prim2DBase>();
+            this._pickStartingPosition = Vector2.Zero();
 
 
             this.setupGroup2D(this, null, name, Vector2.Zero(), size, this._cachingStrategy===Canvas2D.CACHESTRATEGY_ALLGROUPS ? Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
             this.setupGroup2D(this, null, name, Vector2.Zero(), size, this._cachingStrategy===Canvas2D.CACHESTRATEGY_ALLGROUPS ? Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
 
 
+            this._hierarchyLevelMaxSiblingCount = 100;
+            this._hierarchyDepthOffset = 0;
+            this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
             this._scene = scene;
             this._scene = scene;
             this._engine = engine;
             this._engine = engine;
             this._renderingSize = new Size(0, 0);
             this._renderingSize = new Size(0, 0);
@@ -141,6 +143,7 @@
 
 
             if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
             if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
                 this._background = Rectangle2D.Create(this, "###CANVAS BACKGROUND###", 0, 0, size.width, size.height);
                 this._background = Rectangle2D.Create(this, "###CANVAS BACKGROUND###", 0, 0, size.width, size.height);
+                this._background.isPickable = false;
                 this._background.origin = Vector2.Zero();
                 this._background.origin = Vector2.Zero();
                 this._background.levelVisible = false;
                 this._background.levelVisible = false;
             }
             }
@@ -159,6 +162,425 @@
 
 
             this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
             this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
 //            this._supprtInstancedArray = false; // TODO REMOVE!!!
 //            this._supprtInstancedArray = false; // TODO REMOVE!!!
+
+            this._setupInteraction(enableInteraction);
+        }
+
+        public get hierarchyLevelMaxSiblingCount(): number {
+            return this._hierarchyLevelMaxSiblingCount;
+        }
+
+        private _setupInteraction(enable: boolean) {
+            // No change detection
+            if (enable === this._interactionEnabled) {
+                return;
+            }
+
+            // Set the new state
+            this._interactionEnabled = enable;
+
+            // Disable interaction
+            if (!enable) {
+                if (this._scenePrePointerObserver) {
+                    this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
+                    this._scenePrePointerObserver = null;
+                }
+
+                return;
+            }
+
+            // Enable Interaction
+
+            // Register the observable
+            this.scene.onPrePointerObservable.add((e, s) => this._handlePointerEventForInteraction(e, s));
+        }
+
+        /**
+         * Internal method, you should use the Prim2DBase version instead
+         */
+        public _setPointerCapture(pointerId: number, primitive: Prim2DBase): boolean {
+            if (this.isPointerCaptured(pointerId)) {
+                return false;
+            }
+
+            // Try to capture the pointer on the HTML side
+            try {
+                this.engine.getRenderingCanvas().setPointerCapture(pointerId);
+            } catch (e) {
+                //Nothing to do with the error. Execution will continue.
+            }
+
+            this._primPointerInfo.updateRelatedTarget(primitive, Vector2.Zero());
+            this._bubbleNotifyPrimPointerObserver(primitive, PrimitivePointerInfo.PointerGotCapture, null);
+
+            this._capturedPointers.add(pointerId.toString(), primitive);
+            return true;
+        }
+
+        /**
+         * Internal method, you should use the Prim2DBase version instead
+         */
+        public _releasePointerCapture(pointerId: number, primitive: Prim2DBase): boolean {
+            if (this._capturedPointers.get(pointerId.toString()) !== primitive) {
+                return false;
+            }
+
+            // Try to release the pointer on the HTML side
+            try {
+                this.engine.getRenderingCanvas().releasePointerCapture(pointerId);
+            } catch (e) {
+                //Nothing to do with the error. Execution will continue.
+            }
+
+            this._primPointerInfo.updateRelatedTarget(primitive, Vector2.Zero());
+            this._bubbleNotifyPrimPointerObserver(primitive, PrimitivePointerInfo.PointerLostCapture, null);
+            this._capturedPointers.remove(pointerId.toString());
+            return true;
+        }
+
+        /**
+         * Determine if the given pointer is captured or not
+         * @param pointerId the Id of the pointer
+         * @return true if it's captured, false otherwise
+         */
+        public isPointerCaptured(pointerId: number): boolean {
+            return this._capturedPointers.contains(pointerId.toString());
+        }
+
+        private getCapturedPrimitive(pointerId: number): Prim2DBase {
+            // Avoid unnecessary lookup
+            if (this._capturedPointers.count === 0) {
+                return null;
+            }
+            return this._capturedPointers.get(pointerId.toString());
+        }
+           
+        private static _interInfo = new IntersectInfo2D();
+        private _handlePointerEventForInteraction(eventData: PointerInfoPre, eventState: EventState) {
+            // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
+            this._updatePointerInfo(eventData);
+
+            let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
+
+            // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
+            // Why before rendering the canvas? because some primitives may move and get away/under the mouse cursor (which is not moving). So we need to update at both location in order to always have an accurate list, which is needed for the hover state change.
+            this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, capturedPrim!==null);
+
+            // Update the over status, same as above, it's could be done here or during rendering, but will be performed only once per render frame
+            this._updateOverStatus();
+
+            // Check if we have nothing to raise
+            if (!this._actualOverPrimitive && !capturedPrim) {
+                return;
+            }
+
+            // Update the relatedTarget info with the over primitive or the captured one (if any)
+            let targetPrim = capturedPrim || this._actualOverPrimitive.prim;
+
+            let targetPointerPos = capturedPrim ? this._primPointerInfo.canvasPointerPos.subtract(new Vector2(targetPrim.globalTransform.m[12], targetPrim.globalTransform.m[13])) : this._actualOverPrimitive.intersectionLocation;
+
+            this._primPointerInfo.updateRelatedTarget(targetPrim, targetPointerPos);
+
+            // Analyze the pointer event type and fire proper events on the primitive
+
+            if (eventData.type === PointerEventTypes.POINTERWHEEL) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, PrimitivePointerInfo.PointerMouseWheel, <MouseWheelEvent>eventData.event);
+            } else if (eventData.type === PointerEventTypes.POINTERMOVE) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, PrimitivePointerInfo.PointerMove, <PointerEvent>eventData.event);
+            } else if (eventData.type === PointerEventTypes.POINTERDOWN) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, PrimitivePointerInfo.PointerDown, <PointerEvent>eventData.event);
+            } else if (eventData.type === PointerEventTypes.POINTERUP) {
+                this._bubbleNotifyPrimPointerObserver(targetPrim, PrimitivePointerInfo.PointerUp, <PointerEvent>eventData.event);
+            }
+        }
+
+        private _updatePointerInfo(eventData: PointerInfoPre) {
+            let pii = this._primPointerInfo;
+            if (!pii.canvasPointerPos) {
+                pii.canvasPointerPos = Vector2.Zero();
+            }
+            pii.canvasPointerPos.x = eventData.localPosition.x - this.position.x;
+            pii.canvasPointerPos.y = (this.engine.getRenderHeight() - eventData.localPosition.y) - this.position.y;
+            pii.mouseWheelDelta = 0;
+
+            if (eventData.type === PointerEventTypes.POINTERWHEEL) {
+                var event = <MouseWheelEvent>eventData.event;
+                if (event.wheelDelta) {
+                    pii.mouseWheelDelta = event.wheelDelta / (PrimitivePointerInfo.MouseWheelPrecision * 40);
+                } else if (event.detail) {
+                    pii.mouseWheelDelta = -event.detail / PrimitivePointerInfo.MouseWheelPrecision;
+                }
+            } else {
+                var pe         = <PointerEvent>eventData.event;
+                pii.ctrlKey    = pe.ctrlKey;
+                pii.altKey     = pe.altKey;
+                pii.shiftKey   = pe.shiftKey;
+                pii.metaKey    = pe.metaKey;
+                pii.button     = pe.button;
+                pii.buttons    = pe.buttons;
+                pii.pointerId  = pe.pointerId;
+                pii.width      = pe.width;
+                pii.height     = pe.height;
+                pii.presssure  = pe.pressure;
+                pii.tilt.x     = pe.tiltX;
+                pii.tilt.y     = pe.tiltY;
+                pii.isCaptured = this.getCapturedPrimitive(pe.pointerId)!==null;
+            }
+        }
+
+        private _updateIntersectionList(mouseLocalPos: Vector2, isCapture: boolean) {
+            if (this.scene.getRenderId() === this._intersectionRenderId) {
+                return;
+            }
+
+            let ii = Canvas2D._interInfo;
+            ii.pickPosition.x = mouseLocalPos.x;
+            ii.pickPosition.y = mouseLocalPos.y;
+            ii.findFirstOnly = false;
+
+            // Fast rejection: test if the mouse pointer is outside the canvas's bounding Info
+            if (!isCapture && !this.boundingInfo.doesIntersect(ii.pickPosition)) {
+                this._previousIntersectionList = this._actualIntersectionList;
+                this._actualIntersectionList   = null;
+                this._previousOverPrimitive    = this._actualOverPrimitive;
+                this._actualOverPrimitive      = null;
+                return;
+            }
+
+            this._updateCanvasState();
+
+            this.intersect(ii);
+
+            this._previousIntersectionList = this._actualIntersectionList;
+            this._actualIntersectionList   = ii.intersectedPrimitives;
+            this._previousOverPrimitive    = this._actualOverPrimitive;
+            this._actualOverPrimitive      = ii.topMostIntersectedPrimitive;
+
+            this._intersectionRenderId = this.scene.getRenderId();
+        }
+
+        // Based on the previousIntersectionList and the actualInstersectionList we can determined which primitives are being hover state or loosing it
+        private _updateOverStatus() {
+            if ((this.scene.getRenderId() === this._hoverStatusRenderId) || !this._previousIntersectionList || !this._actualIntersectionList) {
+                return;
+            }
+
+            // Detect a change of over
+            let prevPrim = this._previousOverPrimitive ? this._previousOverPrimitive.prim : null;
+            let actualPrim = this._actualOverPrimitive ? this._actualOverPrimitive.prim   : null;
+
+            if (prevPrim !== actualPrim) {
+                // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
+                let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
+
+                // Notify the previous "over" prim that the pointer is no longer over it
+                if ((capturedPrim && capturedPrim===prevPrim) || (!capturedPrim && prevPrim)) {
+                    this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
+                    this._bubbleNotifyPrimPointerObserver(prevPrim, PrimitivePointerInfo.PointerOut, null);
+                }
+
+                // Notify the new "over" prim that the pointer is over it
+                if ((capturedPrim && capturedPrim === actualPrim) || (!capturedPrim && actualPrim)) {
+                    this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
+                    this._bubbleNotifyPrimPointerObserver(actualPrim, PrimitivePointerInfo.PointerOver, null);
+                }
+            }
+
+            this._hoverStatusRenderId = this.scene.getRenderId();
+        }
+
+        private _updatePrimPointerPos(prim: Prim2DBase) {
+            if (this._primPointerInfo.isCaptured) {
+                this._primPointerInfo.primitivePointerPos = this._primPointerInfo.relatedTargetPointerPos;
+            } else {
+                for (let pii of this._actualIntersectionList) {
+                    if (pii.prim === prim) {
+                        this._primPointerInfo.primitivePointerPos = pii.intersectionLocation;
+                        return;
+                    }
+                }
+            }
+        }
+
+        private _notifDebugMode = false;
+        private _debugExecObserver(prim: Prim2DBase, mask: number) {
+            if (!this._notifDebugMode) {
+                return;
+            }
+
+            let debug = "";
+            for (let i = 0; i < prim.hierarchyDepth; i++) {
+                debug += "  ";
+            }
+
+            let pii = this._primPointerInfo;
+            debug += `[RID:${this.scene.getRenderId()}] [${prim.hierarchyDepth}] event:${PrimitivePointerInfo.getEventTypeName(mask)}, id: ${prim.id} (${Tools.getClassName(prim)}), primPos: ${pii.primitivePointerPos.toString()}, canvasPos: ${pii.canvasPointerPos.toString()}`;
+            console.log(debug);
+        }
+
+        private _bubbleNotifyPrimPointerObserver(prim: Prim2DBase, mask: number, eventData: any) {
+            let ppi = this._primPointerInfo;
+
+            // In case of PointerOver/Out we will first notify the children (but the deepest to the closest) with PointerEnter/Leave
+            if ((mask & (PrimitivePointerInfo.PointerOver | PrimitivePointerInfo.PointerOut)) !== 0) {
+                this._notifChildren(prim, mask);
+            }
+
+            let bubbleCancelled = false;
+            let cur = prim;
+            while (cur) {
+                // Only trigger the observers if the primitive is intersected (except for out)
+                if (!bubbleCancelled) {
+                    this._updatePrimPointerPos(cur);
+
+                    // Exec the observers
+                    this._debugExecObserver(cur, mask);
+                    cur._pointerEventObservable.notifyObservers(ppi, mask);
+                    this._triggerActionManager(cur, ppi, mask, eventData);
+
+                    // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
+                    // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
+                    if (ppi.cancelBubble) {
+                        if ((mask & (PrimitivePointerInfo.PointerOver | PrimitivePointerInfo.PointerOut)) === 0) {
+                            return;
+                        }
+
+                        // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
+                        bubbleCancelled = true;
+                    }
+                }
+
+                // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
+                if (bubbleCancelled) {
+                    this._updatePrimPointerPos(cur);
+                }
+
+                // Trigger a PointerEnter corresponding to the PointerOver
+                if (mask === PrimitivePointerInfo.PointerOver) {
+                    this._debugExecObserver(cur, PrimitivePointerInfo.PointerEnter);
+                    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerEnter);
+                }
+
+                // Trigger a PointerLeave corresponding to the PointerOut
+                else if (mask === PrimitivePointerInfo.PointerOut) {
+                    this._debugExecObserver(cur, PrimitivePointerInfo.PointerLeave);
+                    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerLeave);
+                }
+
+                // Loop to the parent
+                cur = cur.parent;
+            }
+        }
+
+        private _triggerActionManager(prim: Prim2DBase, ppi: PrimitivePointerInfo, mask: number, eventData) {
+
+            // Process Trigger related to PointerDown
+            if ((mask & PrimitivePointerInfo.PointerDown) !== 0) {
+                // On pointer down, record the current position and time to be able to trick PickTrigger and LongPressTrigger
+                this._pickStartingPosition = ppi.primitivePointerPos.clone();
+                this._pickStartingTime = new Date().getTime();
+                this._pickedDownPrim = null;
+
+                if (prim.actionManager) {
+                    this._pickedDownPrim = prim;
+                    if (prim.actionManager.hasPickTriggers) {
+                        let actionEvent = ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+
+                        switch (eventData.button) {
+                        case 0:
+                            prim.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, actionEvent);
+                            break;
+                        case 1:
+                            prim.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, actionEvent);
+                            break;
+                        case 2:
+                            prim.actionManager.processTrigger(ActionManager.OnRightPickTrigger, actionEvent);
+                            break;
+                        }
+                        prim.actionManager.processTrigger(ActionManager.OnPickDownTrigger, actionEvent);
+                    }
+
+                    if (prim.actionManager.hasSpecificTrigger(ActionManager.OnLongPressTrigger)) {
+                        window.setTimeout(() => {
+                            let ppi = this._primPointerInfo;
+                            let capturedPrim = this.getCapturedPrimitive(ppi.pointerId);
+                            this._updateIntersectionList(ppi.canvasPointerPos, capturedPrim !== null);
+
+                            let ii = new IntersectInfo2D();
+                            ii.pickPosition = ppi.canvasPointerPos.clone();
+                            ii.findFirstOnly = false;
+                            this.intersect(ii);
+
+                            if (ii.isPrimIntersected(prim) !== null) {
+                                if (prim.actionManager) {
+                                    if (this._pickStartingTime !== 0 && ((new Date().getTime() - this._pickStartingTime) > ActionManager.LongPressDelay) && (Math.abs(this._pickStartingPosition.x - ii.pickPosition.x) < ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ii.pickPosition.y) < ActionManager.DragMovementThreshold)) {
+                                        this._pickStartingTime = 0;
+                                        prim.actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData));
+                                    }
+                                }
+                            }
+                        }, ActionManager.LongPressDelay);
+                    }
+                }
+            }
+
+            // Process Triggers related to Pointer Up
+            else if ((mask & PrimitivePointerInfo.PointerUp) !== 0) {
+                this._pickStartingTime = 0;
+
+                let actionEvent = ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                if (prim.actionManager) {
+                    // OnPickUpTrigger
+                    prim.actionManager.processTrigger(ActionManager.OnPickUpTrigger, actionEvent);
+
+                    // OnPickTrigger
+                    if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < ActionManager.DragMovementThreshold) {
+                        prim.actionManager.processTrigger(ActionManager.OnPickTrigger, actionEvent);
+                    }
+                }
+
+                // OnPickOutTrigger
+                if (this._pickedDownPrim && this._pickedDownPrim.actionManager && (this._pickedDownPrim !== prim)) {
+                    this._pickedDownPrim.actionManager.processTrigger(ActionManager.OnPickOutTrigger, actionEvent);
+                }
+            }
+
+            else if ((mask & PrimitivePointerInfo.PointerOver) !== 0) {
+                if (prim.actionManager) {
+                    let actionEvent = ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                    prim.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, actionEvent);
+                }
+            }
+
+            else if ((mask & PrimitivePointerInfo.PointerOut) !== 0) {
+                if (prim.actionManager) {
+                    let actionEvent = ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData);
+                    prim.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, actionEvent);
+                }
+            }
+        }
+
+        _notifChildren(prim: Prim2DBase, mask: number) {
+            let pii = this._primPointerInfo;
+
+            prim.children.forEach(curChild => {
+                // Recurse first, we want the deepest to be notified first
+                this._notifChildren(curChild, mask);
+
+                this._updatePrimPointerPos(curChild);
+
+                // Fire the proper notification
+                if (mask === PrimitivePointerInfo.PointerOver) {
+                    this._debugExecObserver(curChild, PrimitivePointerInfo.PointerEnter);
+                    curChild._pointerEventObservable.notifyObservers(pii, PrimitivePointerInfo.PointerEnter);
+                }
+
+                // Trigger a PointerLeave corresponding to the PointerOut
+                else if (mask === PrimitivePointerInfo.PointerOut) {
+                    this._debugExecObserver(curChild, PrimitivePointerInfo.PointerLeave);
+                    curChild._pointerEventObservable.notifyObservers(pii, PrimitivePointerInfo.PointerLeave);
+                }
+            });
         }
         }
 
 
         /**
         /**
@@ -170,6 +592,10 @@
                 return false;
                 return false;
             }
             }
 
 
+            if (this.interactionEnabled) {
+                this._setupInteraction(false);
+            }
+
             if (this._beforeRenderObserver) {
             if (this._beforeRenderObserver) {
                 this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
                 this._scene.onBeforeRenderObservable.remove(this._beforeRenderObserver);
                 this._beforeRenderObserver = null;
                 this._beforeRenderObserver = null;
@@ -293,6 +719,18 @@
             this._background.levelVisible = true;
             this._background.levelVisible = true;
         }
         }
 
 
+        /**
+         * Enable/Disable interaction for this Canvas
+         * When enabled the Prim2DBase.pointerEventObservable property will notified when appropriate events occur
+         */
+        public get interactionEnabled(): boolean {
+            return this._interactionEnabled;
+        }
+
+        public set interactionEnabled(enable: boolean) {
+            this._setupInteraction(enable);
+        }
+
         public get _engineData(): Canvas2DEngineBoundData {
         public get _engineData(): Canvas2DEngineBoundData {
             return this.__engineData;
             return this.__engineData;
         }
         }
@@ -303,25 +741,22 @@
             }
             }
         }
         }
 
 
-        /**
-         * Read-only property that return the Z delta to apply for each sibling primitives inside of a given one.
-         * Sibling Primitives are defined in a specific order, the first ones will be draw below the next ones.
-         * This property define the Z value to apply between each sibling Primitive. Current implementation allows 1000 Siblings Primitives per level.
-         * @returns The Z Delta
-         */
-        public get hierarchySiblingZDelta(): number {
-            return this._hierarchySiblingZDelta;
-        }
-
-        /**
-         * Return the Z Factor that will be applied for each new hierarchy level.
-         * @returns The Z Factor
-         */
-        public get hierarchyLevelZFactor(): number {
-            return this._hierarchyLevelZFactor;
-        }
 
 
         private __engineData: Canvas2DEngineBoundData;
         private __engineData: Canvas2DEngineBoundData;
+        private _interactionEnabled: boolean;
+        private _primPointerInfo: PrimitivePointerInfo;
+        private _updateRenderId: number;
+        private _intersectionRenderId: number;
+        private _hoverStatusRenderId: number;
+        private _pickStartingPosition: Vector2;
+        private _pickedDownPrim: Prim2DBase;
+        private _pickStartingTime: number;
+        private _previousIntersectionList: Array<PrimitiveIntersectedInfo>;
+        private _actualIntersectionList: Array<PrimitiveIntersectedInfo>;
+        private _previousOverPrimitive: PrimitiveIntersectedInfo;
+        private _actualOverPrimitive: PrimitiveIntersectedInfo;
+        private _capturedPointers: StringDictionary<Prim2DBase>;
+        private _scenePrePointerObserver: Observer<PointerInfoPre>;
         private _worldSpaceNode: WorldSpaceCanvas2d;
         private _worldSpaceNode: WorldSpaceCanvas2d;
         private _mapCounter = 0;
         private _mapCounter = 0;
         private _background: Rectangle2D;
         private _background: Rectangle2D;
@@ -331,10 +766,7 @@
         private _isScreeSpace: boolean;
         private _isScreeSpace: boolean;
         private _cachedCanvasGroup: Group2D;
         private _cachedCanvasGroup: Group2D;
         private _cachingStrategy: number;
         private _cachingStrategy: number;
-        private _hierarchyMaxDepth: number;
-        private _hierarchyLevelZFactor: number;
         private _hierarchyLevelMaxSiblingCount: number;
         private _hierarchyLevelMaxSiblingCount: number;
-        private _hierarchySiblingZDelta: number;
         private _groupCacheMaps: MapTexture[];
         private _groupCacheMaps: MapTexture[];
         private _beforeRenderObserver: Observer<Scene>;
         private _beforeRenderObserver: Observer<Scene>;
         private _afterRenderObserver: Observer<Scene>;
         private _afterRenderObserver: Observer<Scene>;
@@ -342,10 +774,12 @@
 
 
         public _renderingSize: Size;
         public _renderingSize: Size;
 
 
-        /**
-         * Method that renders the Canvas, you should not invoke
-         */
-        private _render() {
+        private _updateCanvasState() {
+            // Check if the update has already been made for this render Frame
+            if (this.scene.getRenderId() === this._updateRenderId) {
+                return;
+            }
+
             this._renderingSize.width = this.engine.getRenderWidth();
             this._renderingSize.width = this.engine.getRenderWidth();
             this._renderingSize.height = this.engine.getRenderHeight();
             this._renderingSize.height = this.engine.getRenderHeight();
 
 
@@ -363,6 +797,23 @@
             this.updateGlobalTransVis(false);
             this.updateGlobalTransVis(false);
 
 
             this._prepareGroupRender(context);
             this._prepareGroupRender(context);
+
+            this._updateRenderId = this.scene.getRenderId();
+        }
+
+        /**
+         * Method that renders the Canvas, you should not invoke
+         */
+        private _render() {
+
+            this._updateCanvasState();
+
+            if (this._primPointerInfo.canvasPointerPos) {
+                this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
+                this._updateOverStatus();   // TODO this._primPointerInfo may not be up to date!
+            }
+
+            var context = new Render2DContext();
             this._groupRender(context);
             this._groupRender(context);
 
 
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
@@ -434,7 +885,7 @@
                 // Create a Sprite that will be used to render this cache, the "__cachedSpriteOfGroup__" starting id is a hack to bypass exception throwing in case of the Canvas doesn't normally allows direct primitives
                 // Create a Sprite that will be used to render this cache, the "__cachedSpriteOfGroup__" starting id is a hack to bypass exception throwing in case of the Canvas doesn't normally allows direct primitives
                 else {
                 else {
                     let sprite = Sprite2D.Create(parent, `__cachedSpriteOfGroup__${group.id}`, group.position.x, group.position.y, map, node.contentSize, node.pos, false);
                     let sprite = Sprite2D.Create(parent, `__cachedSpriteOfGroup__${group.id}`, group.position.x, group.position.y, map, node.contentSize, node.pos, false);
-                    sprite.origin = Vector2.Zero();
+                    sprite.origin = group.origin.clone();
                     res.sprite = sprite;
                     res.sprite = sprite;
                 }
                 }
             }
             }

+ 13 - 5
src/Canvas2d/babylon.group2d.js

@@ -32,6 +32,7 @@ var BABYLON;
         Group2D._createCachedCanvasGroup = function (owner) {
         Group2D._createCachedCanvasGroup = function (owner) {
             var g = new Group2D();
             var g = new Group2D();
             g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero());
             g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero());
+            g.origin = BABYLON.Vector2.Zero();
             return g;
             return g;
         };
         };
         Group2D.prototype.applyCachedTexture = function (vertexData, material) {
         Group2D.prototype.applyCachedTexture = function (vertexData, material) {
@@ -165,6 +166,10 @@ var BABYLON;
             this._prepareGroupRender(context);
             this._prepareGroupRender(context);
             this._groupRender(context);
             this._groupRender(context);
         };
         };
+        Group2D.prototype.levelIntersect = function (intersectInfo) {
+            // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
+            return true;
+        };
         Group2D.prototype.updateLevelBoundingInfo = function () {
         Group2D.prototype.updateLevelBoundingInfo = function () {
             var size;
             var size;
             // If the size is set by the user, the boundingInfo is computed from this value
             // If the size is set by the user, the boundingInfo is computed from this value
@@ -237,7 +242,7 @@ var BABYLON;
                     sortedDirtyList.forEach(function (p) {
                     sortedDirtyList.forEach(function (p) {
                         // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                         // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                         // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
                         // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
-                        if (!p.isDisposed && p.needPrepare()) {
+                        if (!p.isDisposed && p._needPrepare()) {
                             p._prepareRender(context);
                             p._prepareRender(context);
                         }
                         }
                     });
                     });
@@ -268,7 +273,7 @@ var BABYLON;
                     var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                     var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                 }
                 }
                 // For each different model of primitive to render
                 // For each different model of primitive to render
-                var totalRenderCount_1 = 0;
+                var totalRenderCount = 0;
                 this._renderGroupInstancesInfo.forEach(function (k, v) {
                 this._renderGroupInstancesInfo.forEach(function (k, v) {
                     // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
                     // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
                     // Skip it if instanced arrays are not supported
                     // Skip it if instanced arrays are not supported
@@ -277,7 +282,7 @@ var BABYLON;
                             // If the instances of the model was changed, pack the data
                             // If the instances of the model was changed, pack the data
                             var array = v._instancesPartsData[i];
                             var array = v._instancesPartsData[i];
                             var instanceData_1 = array.pack();
                             var instanceData_1 = array.pack();
-                            totalRenderCount_1 += array.usedElementCount;
+                            totalRenderCount += array.usedElementCount;
                             // Compute the size the instance buffer should have
                             // Compute the size the instance buffer should have
                             var neededSize = array.usedElementCount * array.stride * 4;
                             var neededSize = array.usedElementCount * array.stride * 4;
                             // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
                             // Check if we have to (re)create the instancesBuffer because there's none or the size is too small
@@ -300,7 +305,7 @@ var BABYLON;
                         }
                         }
                     }
                     }
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
-                    if (!_this.owner.supportInstancedArray || totalRenderCount_1 > 0) {
+                    if (!_this.owner.supportInstancedArray || totalRenderCount > 0) {
                         // render all the instances of this model, if the render method returns true then our instances are no longer dirty
                         // render all the instances of this model, if the render method returns true then our instances are no longer dirty
                         var renderFailed = !v._modelCache.render(v, context);
                         var renderFailed = !v._modelCache.render(v, context);
                         // Update dirty flag/related
                         // Update dirty flag/related
@@ -367,6 +372,9 @@ var BABYLON;
             else if (prop.id === BABYLON.Prim2DBase.scaleProperty.id) {
             else if (prop.id === BABYLON.Prim2DBase.scaleProperty.id) {
                 this._cacheRenderSprite.scale = this.scale;
                 this._cacheRenderSprite.scale = this.scale;
             }
             }
+            else if (prop.id === BABYLON.Prim2DBase.originProperty.id) {
+                this._cacheRenderSprite.origin = this.origin.clone();
+            }
             else if (prop.id === Group2D.actualSizeProperty.id) {
             else if (prop.id === Group2D.actualSizeProperty.id) {
                 this._cacheRenderSprite.spriteSize = this.actualSize.clone();
                 this._cacheRenderSprite.spriteSize = this.actualSize.clone();
             }
             }
@@ -452,6 +460,6 @@ var BABYLON;
             BABYLON.className("Group2D")
             BABYLON.className("Group2D")
         ], Group2D);
         ], Group2D);
         return Group2D;
         return Group2D;
-    }(BABYLON.Prim2DBase));
+    })(BABYLON.Prim2DBase);
     BABYLON.Group2D = Group2D;
     BABYLON.Group2D = Group2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 9 - 2
src/Canvas2d/babylon.group2d.ts

@@ -43,7 +43,7 @@
         static _createCachedCanvasGroup(owner: Canvas2D): Group2D {
         static _createCachedCanvasGroup(owner: Canvas2D): Group2D {
             var g = new Group2D();
             var g = new Group2D();
             g.setupGroup2D(owner, null, "__cachedCanvasGroup__", Vector2.Zero());
             g.setupGroup2D(owner, null, "__cachedCanvasGroup__", Vector2.Zero());
-
+            g.origin = Vector2.Zero();
             return g;
             return g;
             
             
         }
         }
@@ -184,6 +184,11 @@
             this._groupRender(context);
             this._groupRender(context);
         }
         }
 
 
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
+            return true;
+        }
+
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
             let size: Size;
             let size: Size;
 
 
@@ -267,7 +272,7 @@
 
 
                         // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                         // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                         // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
                         // For instance: a Rect's position change, the position of its children primitives will also change so a prepare will be call on them. If a child was in the dirtyList we will avoid a second prepare by making this check.
-                        if (!p.isDisposed && p.needPrepare()) {
+                        if (!p.isDisposed && p._needPrepare()) {
                             p._prepareRender(context);
                             p._prepareRender(context);
                         }
                         }
                     });
                     });
@@ -412,6 +417,8 @@
                 this._cacheRenderSprite.rotation = this.rotation;
                 this._cacheRenderSprite.rotation = this.rotation;
             } else if (prop.id === Prim2DBase.scaleProperty.id) {
             } else if (prop.id === Prim2DBase.scaleProperty.id) {
                 this._cacheRenderSprite.scale = this.scale;
                 this._cacheRenderSprite.scale = this.scale;
+            } else if (prop.id === Prim2DBase.originProperty.id) {
+                this._cacheRenderSprite.origin = this.origin.clone();
             } else if (prop.id === Group2D.actualSizeProperty.id) {
             } else if (prop.id === Group2D.actualSizeProperty.id) {
                 this._cacheRenderSprite.spriteSize = this.actualSize.clone();
                 this._cacheRenderSprite.spriteSize = this.actualSize.clone();
                 //console.log(`[${this._globalTransformProcessStep}] Sync Sprite ${this.id}, width: ${this.actualSize.width}, height: ${this.actualSize.height}`);
                 //console.log(`[${this._globalTransformProcessStep}] Sync Sprite ${this.id}, width: ${this.actualSize.width}, height: ${this.actualSize.height}`);

+ 2 - 2
src/Canvas2d/babylon.modelRenderCache.js

@@ -31,7 +31,7 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return GroupInstanceInfo;
         return GroupInstanceInfo;
-    }());
+    })();
     BABYLON.GroupInstanceInfo = GroupInstanceInfo;
     BABYLON.GroupInstanceInfo = GroupInstanceInfo;
     var ModelRenderCache = (function () {
     var ModelRenderCache = (function () {
         function ModelRenderCache(engine, modelKey, isTransparent) {
         function ModelRenderCache(engine, modelKey, isTransparent) {
@@ -178,6 +178,6 @@ var BABYLON;
         ModelRenderCache.v3 = BABYLON.Vector3.Zero();
         ModelRenderCache.v3 = BABYLON.Vector3.Zero();
         ModelRenderCache.v4 = BABYLON.Vector4.Zero();
         ModelRenderCache.v4 = BABYLON.Vector4.Zero();
         return ModelRenderCache;
         return ModelRenderCache;
-    }());
+    })();
     BABYLON.ModelRenderCache = ModelRenderCache;
     BABYLON.ModelRenderCache = ModelRenderCache;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 429 - 23
src/Canvas2d/babylon.prim2dBase.js

@@ -15,8 +15,216 @@ var BABYLON;
         function Render2DContext() {
         function Render2DContext() {
         }
         }
         return Render2DContext;
         return Render2DContext;
-    }());
+    })();
     BABYLON.Render2DContext = Render2DContext;
     BABYLON.Render2DContext = Render2DContext;
+    /**
+     * This class store information for the pointerEventObservable Observable.
+     * The Observable is divided into many sub events (using the Mask feature of the Observable pattern): PointerOver, PointerEnter, PointerDown, PointerMouseWheel, PointerMove, PointerUp, PointerDown, PointerLeave, PointerGotCapture and PointerLostCapture.
+     */
+    var PrimitivePointerInfo = (function () {
+        function PrimitivePointerInfo() {
+            this.primitivePointerPos = BABYLON.Vector2.Zero();
+            this.tilt = BABYLON.Vector2.Zero();
+            this.cancelBubble = false;
+        }
+        Object.defineProperty(PrimitivePointerInfo, "PointerOver", {
+            // The behavior is based on the HTML specifications of the Pointer Events (https://www.w3.org/TR/pointerevents/#list-of-pointer-events). This is not 100% compliant and not meant to be, but still, it's based on these specs for most use cases to be programmed the same way (as closest as possible) as it would have been in HTML.
+            /**
+             * This event type is raised when a pointing device is moved into the hit test boundaries of a primitive.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerOver;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerEnter", {
+            /**
+             * This event type is raised when a pointing device is moved into the hit test boundaries of a primitive or one of its descendants.
+             * Bubbles: no
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerEnter;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerDown", {
+            /**
+             * This event type is raised when a pointer enters the active button state (non-zero value in the buttons property). For mouse it's when the device transitions from no buttons depressed to at least one button depressed. For touch/pen this is when a physical contact is made.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerDown;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerMouseWheel", {
+            /**
+             * This event type is raised when the pointer is a mouse and it's wheel is rolling
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerMouseWheel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerMove", {
+            /**
+             * This event type is raised when a pointer change coordinates or when a pointer changes button state, pressure, tilt, or contact geometry and the circumstances produce no other pointers events.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerMove;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerUp", {
+            /**
+             * This event type is raised when the pointer leaves the active buttons states (zero value in the buttons property). For mouse, this is when the device transitions from at least one button depressed to no buttons depressed. For touch/pen, this is when physical contact is removed.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerUp;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerOut", {
+            /**
+             * This event type is raised when a pointing device is moved out of the hit test the boundaries of a primitive.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerOut;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerLeave", {
+            /**
+             * This event type is raised when a pointing device is moved out of the hit test boundaries of a primitive and all its descendants.
+             * Bubbles: no
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerLeave;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerGotCapture", {
+            /**
+             * This event type is raised when a primitive receives the pointer capture. This event is fired at the element that is receiving pointer capture. Subsequent events for that pointer will be fired at this element.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerGotCapture;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "PointerLostCapture", {
+            /**
+             * This event type is raised after pointer capture is released for a pointer.
+             * Bubbles: yes
+             */
+            get: function () {
+                return PrimitivePointerInfo._pointerLostCapture;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitivePointerInfo, "MouseWheelPrecision", {
+            get: function () {
+                return PrimitivePointerInfo._mouseWheelPrecision;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        PrimitivePointerInfo.prototype.updateRelatedTarget = function (prim, primPointerPos) {
+            this.relatedTarget = prim;
+            this.relatedTargetPointerPos = primPointerPos;
+        };
+        PrimitivePointerInfo.getEventTypeName = function (mask) {
+            switch (mask) {
+                case PrimitivePointerInfo.PointerOver: return "PointerOver";
+                case PrimitivePointerInfo.PointerEnter: return "PointerEnter";
+                case PrimitivePointerInfo.PointerDown: return "PointerDown";
+                case PrimitivePointerInfo.PointerMouseWheel: return "PointerMouseWheel";
+                case PrimitivePointerInfo.PointerMove: return "PointerMove";
+                case PrimitivePointerInfo.PointerUp: return "PointerUp";
+                case PrimitivePointerInfo.PointerOut: return "PointerOut";
+                case PrimitivePointerInfo.PointerLeave: return "PointerLeave";
+                case PrimitivePointerInfo.PointerGotCapture: return "PointerGotCapture";
+                case PrimitivePointerInfo.PointerLostCapture: return "PointerLostCapture";
+            }
+        };
+        PrimitivePointerInfo._pointerOver = 0x0001;
+        PrimitivePointerInfo._pointerEnter = 0x0002;
+        PrimitivePointerInfo._pointerDown = 0x0004;
+        PrimitivePointerInfo._pointerMouseWheel = 0x0008;
+        PrimitivePointerInfo._pointerMove = 0x0010;
+        PrimitivePointerInfo._pointerUp = 0x0020;
+        PrimitivePointerInfo._pointerOut = 0x0040;
+        PrimitivePointerInfo._pointerLeave = 0x0080;
+        PrimitivePointerInfo._pointerGotCapture = 0x0100;
+        PrimitivePointerInfo._pointerLostCapture = 0x0200;
+        PrimitivePointerInfo._mouseWheelPrecision = 3.0;
+        return PrimitivePointerInfo;
+    })();
+    BABYLON.PrimitivePointerInfo = PrimitivePointerInfo;
+    /**
+     * Stores information about a Primitive that was intersected
+     */
+    var PrimitiveIntersectedInfo = (function () {
+        function PrimitiveIntersectedInfo(prim, intersectionLocation) {
+            this.prim = prim;
+            this.intersectionLocation = intersectionLocation;
+        }
+        return PrimitiveIntersectedInfo;
+    })();
+    BABYLON.PrimitiveIntersectedInfo = PrimitiveIntersectedInfo;
+    /**
+     * Main class used for the Primitive Intersection API
+     */
+    var IntersectInfo2D = (function () {
+        function IntersectInfo2D() {
+            this.findFirstOnly = false;
+            this.intersectHidden = false;
+            this.pickPosition = BABYLON.Vector2.Zero();
+        }
+        Object.defineProperty(IntersectInfo2D.prototype, "isIntersected", {
+            /**
+             * true if at least one primitive intersected during the test
+             */
+            get: function () {
+                return this.intersectedPrimitives && this.intersectedPrimitives.length > 0;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        IntersectInfo2D.prototype.isPrimIntersected = function (prim) {
+            for (var _i = 0, _a = this.intersectedPrimitives; _i < _a.length; _i++) {
+                var cur = _a[_i];
+                if (cur.prim === prim) {
+                    return cur.intersectionLocation;
+                }
+            }
+            return null;
+        };
+        // Internals, don't use
+        IntersectInfo2D.prototype._exit = function (firstLevel) {
+            if (firstLevel) {
+                this._globalPickPosition = null;
+            }
+        };
+        return IntersectInfo2D;
+    })();
+    BABYLON.IntersectInfo2D = IntersectInfo2D;
     var Prim2DBase = (function (_super) {
     var Prim2DBase = (function (_super) {
         __extends(Prim2DBase, _super);
         __extends(Prim2DBase, _super);
         function Prim2DBase() {
         function Prim2DBase() {
@@ -28,6 +236,9 @@ var BABYLON;
                 throw new Error("Can't create a primitive with the canvas as direct parent when the caching strategy is TOPLEVELGROUPS. You need to create a Group below the canvas and use it as the parent for the primitive");
                 throw new Error("Can't create a primitive with the canvas as direct parent when the caching strategy is TOPLEVELGROUPS. You need to create a Group below the canvas and use it as the parent for the primitive");
             }
             }
             this.setupSmartPropertyPrim();
             this.setupSmartPropertyPrim();
+            this._pointerEventObservable = new BABYLON.Observable();
+            this._isPickable = true;
+            this._siblingDepthOffset = this._hierarchyDepthOffset = 0;
             this._boundingInfoDirty = true;
             this._boundingInfoDirty = true;
             this._boundingInfo = new BABYLON.BoundingInfo2D();
             this._boundingInfo = new BABYLON.BoundingInfo2D();
             this._owner = owner;
             this._owner = owner;
@@ -56,6 +267,21 @@ var BABYLON;
             this.levelVisible = isVisible;
             this.levelVisible = isVisible;
             this.origin = new BABYLON.Vector2(0.5, 0.5);
             this.origin = new BABYLON.Vector2(0.5, 0.5);
         };
         };
+        Object.defineProperty(Prim2DBase.prototype, "actionManager", {
+            get: function () {
+                if (!this._actionManager) {
+                    this._actionManager = new BABYLON.ActionManager(this.owner.scene);
+                }
+                return this._actionManager;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * From 'this' primitive, traverse up (from parent to parent) until the given predicate is true
+         * @param predicate the predicate to test on each parent
+         * @return the first primitive where the predicate was successful
+         */
         Prim2DBase.prototype.traverseUp = function (predicate) {
         Prim2DBase.prototype.traverseUp = function (predicate) {
             var p = this;
             var p = this;
             while (p != null) {
             while (p != null) {
@@ -67,6 +293,9 @@ var BABYLON;
             return null;
             return null;
         };
         };
         Object.defineProperty(Prim2DBase.prototype, "owner", {
         Object.defineProperty(Prim2DBase.prototype, "owner", {
+            /**
+             * Retrieve the owner Canvas2D
+             */
             get: function () {
             get: function () {
                 return this._owner;
                 return this._owner;
             },
             },
@@ -74,13 +303,29 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(Prim2DBase.prototype, "parent", {
         Object.defineProperty(Prim2DBase.prototype, "parent", {
+            /**
+             * Get the parent primitive (can be the Canvas, only the Canvas has no parent)
+             */
             get: function () {
             get: function () {
                 return this._parent;
                 return this._parent;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Prim2DBase.prototype, "children", {
+            /**
+             * The array of direct children primitives
+             */
+            get: function () {
+                return this._children;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "id", {
         Object.defineProperty(Prim2DBase.prototype, "id", {
+            /**
+             * The identifier of this primitive, may not be unique, it's for information purpose only
+             */
             get: function () {
             get: function () {
                 return this._id;
                 return this._id;
             },
             },
@@ -117,14 +362,25 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Prim2DBase.prototype, "actualSize", {
+            /**
+             * this method must be implemented by the primitive type to return its size
+             * @returns The size of the primitive
+             */
+            get: function () {
+                return undefined;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "origin", {
         Object.defineProperty(Prim2DBase.prototype, "origin", {
             /**
             /**
              * The origin defines the normalized coordinate of the center of the primitive, from the top/left corner.
              * The origin defines the normalized coordinate of the center of the primitive, from the top/left corner.
              * The origin is used only to compute transformation of the primitive, it has no meaning in the primitive local frame of reference
              * The origin is used only to compute transformation of the primitive, it has no meaning in the primitive local frame of reference
              * For instance:
              * For instance:
-             * 0,0 means the center is top/left
-             * 0.5,0.5 means the center is at the center of the primitive
-             * 0,1 means the center is bottom/left
+             * 0,0 means the center is bottom/left. Which is the default for Canvas2D instances
+             * 0.5,0.5 means the center is at the center of the primitive, which is default of all types of Primitives
+             * 0,1 means the center is top/left
              * @returns The normalized center.
              * @returns The normalized center.
              */
              */
             get: function () {
             get: function () {
@@ -166,7 +422,24 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Prim2DBase.prototype, "isPickable", {
+            /**
+             * Define if the Primitive can be subject to intersection test or not (default is true)
+             */
+            get: function () {
+                return this._isPickable;
+            },
+            set: function (value) {
+                this._isPickable = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "hierarchyDepth", {
         Object.defineProperty(Prim2DBase.prototype, "hierarchyDepth", {
+            /**
+             * Return the depth level of the Primitive into the Canvas' Graph. A Canvas will be 0, its direct children 1, and so on.
+             * @returns {}
+             */
             get: function () {
             get: function () {
                 return this._hierarchyDepth;
                 return this._hierarchyDepth;
             },
             },
@@ -174,6 +447,10 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(Prim2DBase.prototype, "renderGroup", {
         Object.defineProperty(Prim2DBase.prototype, "renderGroup", {
+            /**
+             * Retrieve the Group that is responsible to render this primitive
+             * @returns {}
+             */
             get: function () {
             get: function () {
                 return this._renderGroup;
                 return this._renderGroup;
             },
             },
@@ -181,6 +458,9 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(Prim2DBase.prototype, "globalTransform", {
         Object.defineProperty(Prim2DBase.prototype, "globalTransform", {
+            /**
+             * Get the global transformation matrix of the primitive
+             */
             get: function () {
             get: function () {
                 return this._globalTransform;
                 return this._globalTransform;
             },
             },
@@ -188,13 +468,32 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(Prim2DBase.prototype, "invGlobalTransform", {
         Object.defineProperty(Prim2DBase.prototype, "invGlobalTransform", {
+            /**
+             * Get invert of the global transformation matrix of the primitive
+             * @returns {}
+             */
             get: function () {
             get: function () {
                 return this._invGlobalTransform;
                 return this._invGlobalTransform;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Prim2DBase.prototype, "localTransform", {
+            /**
+             * Get the local transformation of the primitive
+             */
+            get: function () {
+                this._updateLocalTransform();
+                return this._localTransform;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "boundingInfo", {
         Object.defineProperty(Prim2DBase.prototype, "boundingInfo", {
+            /**
+             * Get the boundingInfo associated to the primitive.
+             * The value is supposed to be always up to date
+             */
             get: function () {
             get: function () {
                 if (this._boundingInfoDirty) {
                 if (this._boundingInfoDirty) {
                     this._boundingInfo = this.levelBoundingInfo.clone();
                     this._boundingInfo = this.levelBoundingInfo.clone();
@@ -202,8 +501,7 @@ var BABYLON;
                     var tps = new BABYLON.BoundingInfo2D();
                     var tps = new BABYLON.BoundingInfo2D();
                     for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
                     for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
                         var curChild = _a[_i];
                         var curChild = _a[_i];
-                        var t = curChild.globalTransform.multiply(this.invGlobalTransform);
-                        curChild.boundingInfo.transformToRef(t, curChild.origin, tps);
+                        curChild.boundingInfo.transformToRef(curChild.localTransform, tps);
                         bi.unionToRef(tps, bi);
                         bi.unionToRef(tps, bi);
                     }
                     }
                     this._boundingInfoDirty = false;
                     this._boundingInfoDirty = false;
@@ -213,6 +511,95 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Prim2DBase.prototype, "pointerEventObservable", {
+            /**
+             * Interaction with the primitive can be create using this Observable. See the PrimitivePointerInfo class for more information
+             */
+            get: function () {
+                return this._pointerEventObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Prim2DBase.prototype.levelIntersect = function (intersectInfo) {
+            return false;
+        };
+        /**
+         * Capture all the Events of the given PointerId for this primitive.
+         * Don't forget to call releasePointerEventsCapture when done.
+         * @param pointerId the Id of the pointer to capture the events from.
+         */
+        Prim2DBase.prototype.setPointerEventCapture = function (pointerId) {
+            return this.owner._setPointerCapture(pointerId, this);
+        };
+        /**
+         * Release a captured pointer made with setPointerEventCapture.
+         * @param pointerId the Id of the pointer to release the capture from.
+         */
+        Prim2DBase.prototype.releasePointerEventsCapture = function (pointerId) {
+            return this.owner._releasePointerCapture(pointerId, this);
+        };
+        /**
+         * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
+         * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
+         */
+        Prim2DBase.prototype.intersect = function (intersectInfo) {
+            if (!intersectInfo) {
+                return false;
+            }
+            // If this is null it means this method is call for the first level, initialize stuffs
+            var firstLevel = !intersectInfo._globalPickPosition;
+            if (firstLevel) {
+                // Compute the pickPosition in global space and use it to find the local position for each level down, always relative from the world to get the maximum accuracy (and speed). The other way would have been to compute in local every level down relative to its parent's local, which wouldn't be as accurate (even if javascript number is 80bits accurate).
+                intersectInfo._globalPickPosition = BABYLON.Vector2.Zero();
+                BABYLON.Vector2.TransformToRef(intersectInfo.pickPosition, this.globalTransform, intersectInfo._globalPickPosition);
+                intersectInfo._localPickPosition = intersectInfo.pickPosition.clone();
+                intersectInfo.intersectedPrimitives = new Array();
+                intersectInfo.topMostIntersectedPrimitive = null;
+            }
+            if (!intersectInfo.intersectHidden && !this.isVisible) {
+                return false;
+            }
+            // Fast rejection test with boundingInfo
+            if (!this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
+                // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
+                intersectInfo._exit(firstLevel);
+                return false;
+            }
+            // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
+            var levelIntersectRes = this.levelIntersect(intersectInfo);
+            if (levelIntersectRes) {
+                var pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
+                intersectInfo.intersectedPrimitives.push(pii);
+                if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.getActualZOffset() > pii.prim.getActualZOffset())) {
+                    intersectInfo.topMostIntersectedPrimitive = pii;
+                }
+                // If we must stop at the first intersection, we're done, quit!
+                if (intersectInfo.findFirstOnly) {
+                    intersectInfo._exit(firstLevel);
+                    return true;
+                }
+            }
+            // Recurse to children if needed
+            if (!levelIntersectRes || !intersectInfo.findFirstOnly) {
+                for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
+                    var curChild = _a[_i];
+                    // Don't test primitive not pick able or if it's hidden and we don't test hidden ones
+                    if (!curChild.isPickable || (!intersectInfo.intersectHidden && !curChild.isVisible)) {
+                        continue;
+                    }
+                    // Must compute the localPickLocation for the children level
+                    BABYLON.Vector2.TransformToRef(intersectInfo._globalPickPosition, curChild.invGlobalTransform, intersectInfo._localPickPosition);
+                    // If we got an intersection with the child and we only need to find the first one, quit!
+                    if (curChild.intersect(intersectInfo) && intersectInfo.findFirstOnly) {
+                        intersectInfo._exit(firstLevel);
+                        return true;
+                    }
+                }
+            }
+            intersectInfo._exit(firstLevel);
+            return intersectInfo.isIntersected;
+        };
         Prim2DBase.prototype.moveChild = function (child, previous) {
         Prim2DBase.prototype.moveChild = function (child, previous) {
             if (child.parent !== this) {
             if (child.parent !== this) {
                 return false;
                 return false;
@@ -233,15 +620,18 @@ var BABYLON;
             this._children.splice(prevIndex + 1, 0, this._children.splice(childIndex, 1)[0]);
             this._children.splice(prevIndex + 1, 0, this._children.splice(childIndex, 1)[0]);
         };
         };
         Prim2DBase.prototype.addChild = function (child) {
         Prim2DBase.prototype.addChild = function (child) {
-            child._siblingDepthOffset = (this._children.length + 1) * this.owner.hierarchySiblingZDelta;
-            child._depthLevel = this._depthLevel + 1;
-            child._hierarchyDepthOffset = child._depthLevel * this.owner.hierarchyLevelZFactor;
+            child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
+            child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             this._children.push(child);
             this._children.push(child);
         };
         };
         Prim2DBase.prototype.dispose = function () {
         Prim2DBase.prototype.dispose = function () {
             if (!_super.prototype.dispose.call(this)) {
             if (!_super.prototype.dispose.call(this)) {
                 return false;
                 return false;
             }
             }
+            if (this._actionManager) {
+                this._actionManager.dispose();
+                this._actionManager = null;
+            }
             // If there's a parent, remove this object from its parent list
             // If there's a parent, remove this object from its parent list
             if (this._parent) {
             if (this._parent) {
                 var i = this._parent._children.indexOf(this);
                 var i = this._parent._children.indexOf(this);
@@ -259,15 +649,15 @@ var BABYLON;
             return true;
             return true;
         };
         };
         Prim2DBase.prototype.getActualZOffset = function () {
         Prim2DBase.prototype.getActualZOffset = function () {
-            return this._zOrder || 1 - (this._siblingDepthOffset + this._hierarchyDepthOffset);
+            return this._zOrder || (1 - this._hierarchyDepthOffset);
         };
         };
         Prim2DBase.prototype.onPrimBecomesDirty = function () {
         Prim2DBase.prototype.onPrimBecomesDirty = function () {
             if (this._renderGroup) {
             if (this._renderGroup) {
                 this._renderGroup._addPrimToDirtyList(this);
                 this._renderGroup._addPrimToDirtyList(this);
             }
             }
         };
         };
-        Prim2DBase.prototype.needPrepare = function () {
-            return (this.isVisible || this._visibilityChanged) && (this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep));
+        Prim2DBase.prototype._needPrepare = function () {
+            return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         };
         };
         Prim2DBase.prototype._prepareRender = function (context) {
         Prim2DBase.prototype._prepareRender = function (context) {
             this._prepareRenderPre(context);
             this._prepareRenderPre(context);
@@ -306,11 +696,29 @@ var BABYLON;
             }
             }
         };
         };
         Prim2DBase.prototype.updateGlobalTransVisOf = function (list, recurse) {
         Prim2DBase.prototype.updateGlobalTransVisOf = function (list, recurse) {
-            for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
-                var cur = list_1[_i];
+            for (var _i = 0; _i < list.length; _i++) {
+                var cur = list[_i];
                 cur.updateGlobalTransVis(recurse);
                 cur.updateGlobalTransVis(recurse);
             }
             }
         };
         };
+        Prim2DBase.prototype._updateLocalTransform = function () {
+            var tflags = Prim2DBase.positionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId;
+            if (this.checkPropertiesDirty(tflags)) {
+                var rot = BABYLON.Quaternion.RotationAxis(new BABYLON.Vector3(0, 0, 1), this._rotation);
+                var local = BABYLON.Matrix.Compose(new BABYLON.Vector3(this._scale, this._scale, this._scale), rot, new BABYLON.Vector3(this._position.x, this._position.y, 0));
+                this._localTransform = local;
+                this.clearPropertiesDirty(tflags);
+                // this is important to access actualSize AFTER fetching a first version of the local transform and reset the dirty flag, because accessing actualSize on a Group2D which actualSize is built from its content will trigger a call to this very method on this very object. We won't mind about the origin offset not being computed, as long as we return a local transform based on the position/rotation/scale
+                //var actualSize = this.actualSize;
+                //if (!actualSize) {
+                //    throw new Error(`The primitive type: ${Tools.getClassName(this)} must implement the actualSize get property!`);
+                //}
+                //local.m[12] -= (actualSize.width * this.origin.x) * local.m[0] + (actualSize.height * this.origin.y) * local.m[4];
+                //local.m[13] -= (actualSize.width * this.origin.x) * local.m[1] + (actualSize.height * this.origin.y) * local.m[5];
+                return true;
+            }
+            return false;
+        };
         Prim2DBase.prototype.updateGlobalTransVis = function (recurse) {
         Prim2DBase.prototype.updateGlobalTransVis = function (recurse) {
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return;
                 return;
@@ -324,17 +732,15 @@ var BABYLON;
                 var curVisibleState = this.isVisible;
                 var curVisibleState = this.isVisible;
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
                 // Detect a change of visibility
                 // Detect a change of visibility
-                this._visibilityChanged = (curVisibleState !== undefined) && curVisibleState !== this.isVisible;
-                // Detect if either the parent or this node changed
-                var tflags = Prim2DBase.positionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId;
-                if (this.isVisible && (this._parent && this._parent._globalTransformStep !== this._parentTransformStep) || this.checkPropertiesDirty(tflags)) {
-                    var rot = BABYLON.Quaternion.RotationAxis(new BABYLON.Vector3(0, 0, 1), this._rotation);
-                    var local = BABYLON.Matrix.Compose(new BABYLON.Vector3(this._scale, this._scale, this._scale), rot, new BABYLON.Vector3(this._position.x, this._position.y, 0));
-                    this._globalTransform = this._parent ? local.multiply(this._parent._globalTransform) : local;
+                this._visibilityChanged = curVisibleState !== this.isVisible;
+                // Get/compute the localTransform
+                var localDirty = this._updateLocalTransform();
+                // Check if we have to update the globalTransform
+                if (!this._globalTransform || localDirty || (this._parent && this._parent._globalTransformStep !== this._parentTransformStep)) {
+                    this._globalTransform = this._parent ? this._localTransform.multiply(this._parent._globalTransform) : this._localTransform;
                     this._invGlobalTransform = BABYLON.Matrix.Invert(this._globalTransform);
                     this._invGlobalTransform = BABYLON.Matrix.Invert(this._globalTransform);
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
-                    this.clearPropertiesDirty(tflags);
                 }
                 }
                 this._globalTransformProcessStep = this.owner._globalTransformProcessStep;
                 this._globalTransformProcessStep = this.owner._globalTransformProcessStep;
             }
             }
@@ -372,6 +778,6 @@ var BABYLON;
             BABYLON.className("Prim2DBase")
             BABYLON.className("Prim2DBase")
         ], Prim2DBase);
         ], Prim2DBase);
         return Prim2DBase;
         return Prim2DBase;
-    }(BABYLON.SmartPropertyPrim));
+    })(BABYLON.SmartPropertyPrim);
     BABYLON.Prim2DBase = Prim2DBase;
     BABYLON.Prim2DBase = Prim2DBase;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 563 - 25
src/Canvas2d/babylon.prim2dBase.ts

@@ -4,7 +4,295 @@
         forceRefreshPrimitive: boolean;
         forceRefreshPrimitive: boolean;
     }
     }
 
 
+    /**
+     * This class store information for the pointerEventObservable Observable.
+     * The Observable is divided into many sub events (using the Mask feature of the Observable pattern): PointerOver, PointerEnter, PointerDown, PointerMouseWheel, PointerMove, PointerUp, PointerDown, PointerLeave, PointerGotCapture and PointerLostCapture.
+     */
+    export class PrimitivePointerInfo {
+        private static _pointerOver        = 0x0001;
+        private static _pointerEnter       = 0x0002;
+        private static _pointerDown        = 0x0004;
+        private static _pointerMouseWheel  = 0x0008;
+        private static _pointerMove        = 0x0010;
+        private static _pointerUp          = 0x0020;
+        private static _pointerOut         = 0x0040;
+        private static _pointerLeave       = 0x0080;
+        private static _pointerGotCapture  = 0x0100;
+        private static _pointerLostCapture = 0x0200;
+
+        private static _mouseWheelPrecision = 3.0;
+
+        // The behavior is based on the HTML specifications of the Pointer Events (https://www.w3.org/TR/pointerevents/#list-of-pointer-events). This is not 100% compliant and not meant to be, but still, it's based on these specs for most use cases to be programmed the same way (as closest as possible) as it would have been in HTML.
+
+        /**
+         * This event type is raised when a pointing device is moved into the hit test boundaries of a primitive.
+         * Bubbles: yes
+         */
+        public static get PointerOver(): number {
+            return PrimitivePointerInfo._pointerOver;
+        }
+
+        /**
+         * This event type is raised when a pointing device is moved into the hit test boundaries of a primitive or one of its descendants.
+         * Bubbles: no
+         */
+        public static get PointerEnter(): number {
+            return PrimitivePointerInfo._pointerEnter;
+        }
+
+        /**
+         * This event type is raised when a pointer enters the active button state (non-zero value in the buttons property). For mouse it's when the device transitions from no buttons depressed to at least one button depressed. For touch/pen this is when a physical contact is made.
+         * Bubbles: yes
+         */
+        public static get PointerDown(): number {
+            return PrimitivePointerInfo._pointerDown;
+        }
+
+        /**
+         * This event type is raised when the pointer is a mouse and it's wheel is rolling
+         * Bubbles: yes
+         */
+        public static get PointerMouseWheel(): number {
+            return PrimitivePointerInfo._pointerMouseWheel;
+        }
+
+        /**
+         * This event type is raised when a pointer change coordinates or when a pointer changes button state, pressure, tilt, or contact geometry and the circumstances produce no other pointers events.
+         * Bubbles: yes
+         */
+        public static get PointerMove(): number {
+            return PrimitivePointerInfo._pointerMove;
+        }
+
+        /**
+         * This event type is raised when the pointer leaves the active buttons states (zero value in the buttons property). For mouse, this is when the device transitions from at least one button depressed to no buttons depressed. For touch/pen, this is when physical contact is removed.
+         * Bubbles: yes
+         */
+        public static get PointerUp(): number {
+            return PrimitivePointerInfo._pointerUp;
+        }
+
+        /**
+         * This event type is raised when a pointing device is moved out of the hit test the boundaries of a primitive.
+         * Bubbles: yes
+         */
+        public static get PointerOut(): number {
+            return PrimitivePointerInfo._pointerOut;
+        }
+
+        /**
+         * This event type is raised when a pointing device is moved out of the hit test boundaries of a primitive and all its descendants.
+         * Bubbles: no
+         */
+        public static get PointerLeave(): number {
+            return PrimitivePointerInfo._pointerLeave;
+        }
+
+        /**
+         * This event type is raised when a primitive receives the pointer capture. This event is fired at the element that is receiving pointer capture. Subsequent events for that pointer will be fired at this element.
+         * Bubbles: yes
+         */
+        public static get PointerGotCapture(): number {
+            return PrimitivePointerInfo._pointerGotCapture;
+        }
+
+        /**
+         * This event type is raised after pointer capture is released for a pointer.
+         * Bubbles: yes
+         */
+        public static get PointerLostCapture(): number {
+            return PrimitivePointerInfo._pointerLostCapture;
+        }
+
+        public static get MouseWheelPrecision(): number {
+            return PrimitivePointerInfo._mouseWheelPrecision;
+        }
+
+        /**
+         * Event Type, one of the static PointerXXXX property defined above (PrimitivePointerInfo.PointerOver to PrimitivePointerInfo.PointerLostCapture)
+         */
+        eventType: number;
+
+        /**
+         * Position of the pointer relative to the bottom/left of the Canvas
+         */
+        canvasPointerPos: Vector2;
+
+        /**
+         * Position of the pointer relative to the bottom/left of the primitive that registered the Observer
+         */
+        primitivePointerPos: Vector2;
+
+        /**
+         * The primitive where the event was initiated first (in case of bubbling)
+         */
+        relatedTarget: Prim2DBase;
+
+        /**
+         * Position of the pointer relative to the bottom/left of the relatedTarget
+         */
+        relatedTargetPointerPos: Vector2;
+
+        /**
+         * An observable can set this property to true to stop bubbling on the upper levels
+         */
+        cancelBubble: boolean;
+
+        /**
+         * True if the Control keyboard key is down
+         */
+        ctrlKey: boolean;
+
+        /**
+         * true if the Shift keyboard key is down
+         */
+        shiftKey: boolean;
+
+        /**
+         * true if the Alt keyboard key is down
+         */
+        altKey: boolean;
+
+        /**
+         * true if the Meta keyboard key is down
+         */
+        metaKey: boolean;
+
+        /**
+         * For button, buttons, refer to https://www.w3.org/TR/pointerevents/#button-states
+         */
+        button: number;
+        /**
+         * For button, buttons, refer to https://www.w3.org/TR/pointerevents/#button-states
+         */
+        buttons: number;
+
+        /**
+         * The amount of mouse wheel rolled
+         */
+        mouseWheelDelta: number;
+
+        /**
+         * Id of the Pointer involved in the event
+         */
+        pointerId: number;
+        width: number;
+        height: number;
+        presssure: number;
+        tilt: Vector2;
+
+        /**
+         * true if the involved pointer is captured for a particular primitive, false otherwise.
+         */
+        isCaptured: boolean;
+
+        constructor() {
+            this.primitivePointerPos = Vector2.Zero();
+            this.tilt = Vector2.Zero();
+            this.cancelBubble = false;
+        }
+
+        updateRelatedTarget(prim: Prim2DBase, primPointerPos: Vector2) {
+            this.relatedTarget = prim;
+            this.relatedTargetPointerPos = primPointerPos;
+        }
+
+        public static getEventTypeName(mask: number): string {
+            switch (mask) {
+                case PrimitivePointerInfo.PointerOver:        return "PointerOver";
+                case PrimitivePointerInfo.PointerEnter:       return "PointerEnter";
+                case PrimitivePointerInfo.PointerDown:        return "PointerDown";
+                case PrimitivePointerInfo.PointerMouseWheel:  return "PointerMouseWheel";
+                case PrimitivePointerInfo.PointerMove:        return "PointerMove";
+                case PrimitivePointerInfo.PointerUp:          return "PointerUp";
+                case PrimitivePointerInfo.PointerOut:         return "PointerOut";
+                case PrimitivePointerInfo.PointerLeave:       return "PointerLeave";
+                case PrimitivePointerInfo.PointerGotCapture:  return "PointerGotCapture";
+                case PrimitivePointerInfo.PointerLostCapture: return "PointerLostCapture";
+            }
+        }
+    }
+
+    /**
+     * Stores information about a Primitive that was intersected
+     */
+    export class PrimitiveIntersectedInfo {
+        constructor(public prim: Prim2DBase, public intersectionLocation: Vector2) {
+            
+        }
+    }
+
+    /**
+     * Main class used for the Primitive Intersection API
+     */
+    export class IntersectInfo2D {
+        constructor() {
+            this.findFirstOnly = false;
+            this.intersectHidden = false;
+            this.pickPosition = Vector2.Zero();
+        }
+
+        // Input settings, to setup before calling an intersection related method
+
+        /**
+         * Set the pick position, relative to the primitive where the intersection test is made
+         */
+        public pickPosition: Vector2;
+
+        /**
+         * If true the intersection will stop at the first hit, if false all primitives will be tested and the intersectedPrimitives array will be filled accordingly (false default)
+         */
+        public findFirstOnly: boolean;
+
+        /**
+         * If true the intersection test will also be made on hidden primitive (false default)
+         */
+        public intersectHidden: boolean;
+
+        // Intermediate data, don't use!
+        public _globalPickPosition: Vector2;
+        public _localPickPosition: Vector2;
+
+        // Output settings, up to date in return of a call to an intersection related method
+
+        /**
+         * The topmost intersected primitive
+         */
+        public topMostIntersectedPrimitive: PrimitiveIntersectedInfo;
+
+        /**
+         * The array containing all intersected primitive, in no particular order.
+         */
+        public intersectedPrimitives: Array<PrimitiveIntersectedInfo>;
+
+        /**
+         * true if at least one primitive intersected during the test
+         */
+        public get isIntersected(): boolean {
+            return this.intersectedPrimitives && this.intersectedPrimitives.length > 0;
+        }
+
+        public isPrimIntersected(prim: Prim2DBase): Vector2 {
+            for (let cur of this.intersectedPrimitives) {
+                if (cur.prim === prim) {
+                    return cur.intersectionLocation;
+                }
+            }
+            return null;
+        }
+
+        // Internals, don't use
+        public _exit(firstLevel: boolean) {
+            if (firstLevel) {
+                this._globalPickPosition = null;
+            }
+        }
+    }
+
     @className("Prim2DBase")
     @className("Prim2DBase")
+    /**
+     * Base class for a Primitive of the Canvas2D feature
+     */
     export class Prim2DBase extends SmartPropertyPrim {
     export class Prim2DBase extends SmartPropertyPrim {
         static PRIM2DBASE_PROPCOUNT: number = 10;
         static PRIM2DBASE_PROPCOUNT: number = 10;
 
 
@@ -14,6 +302,9 @@
             }
             }
 
 
             this.setupSmartPropertyPrim();
             this.setupSmartPropertyPrim();
+            this._pointerEventObservable = new Observable<PrimitivePointerInfo>();
+            this._isPickable = true;
+            this._siblingDepthOffset = this._hierarchyDepthOffset = 0;
             this._boundingInfoDirty = true;
             this._boundingInfoDirty = true;
             this._boundingInfo = new BoundingInfo2D();
             this._boundingInfo = new BoundingInfo2D();
             this._owner = owner;
             this._owner = owner;
@@ -45,6 +336,19 @@
             this.origin = new Vector2(0.5, 0.5);
             this.origin = new Vector2(0.5, 0.5);
         }
         }
 
 
+
+        public get actionManager(): ActionManager {
+            if (!this._actionManager) {
+                this._actionManager = new ActionManager(this.owner.scene);
+            }
+            return this._actionManager;
+        }
+
+        /**
+         * From 'this' primitive, traverse up (from parent to parent) until the given predicate is true
+         * @param predicate the predicate to test on each parent
+         * @return the first primitive where the predicate was successful
+         */
         public traverseUp(predicate: (p: Prim2DBase) => boolean): Prim2DBase {
         public traverseUp(predicate: (p: Prim2DBase) => boolean): Prim2DBase {
             let p: Prim2DBase = this;
             let p: Prim2DBase = this;
             while (p != null) {
             while (p != null) {
@@ -56,27 +360,73 @@
             return null;
             return null;
         }
         }
 
 
+        /**
+         * Retrieve the owner Canvas2D
+         */
         public get owner(): Canvas2D {
         public get owner(): Canvas2D {
             return this._owner;
             return this._owner;
         }
         }
 
 
+        /**
+         * Get the parent primitive (can be the Canvas, only the Canvas has no parent)
+         */
         public get parent(): Prim2DBase {
         public get parent(): Prim2DBase {
             return this._parent;
             return this._parent;
         }
         }
 
 
+        /**
+         * The array of direct children primitives
+         */
+        public get children(): Prim2DBase[] {
+            return this._children;
+        }
+
+        /**
+         * The identifier of this primitive, may not be unique, it's for information purpose only
+         */
         public get id(): string {
         public get id(): string {
             return this._id;
             return this._id;
         }
         }
 
 
+        /**
+         * Metadata of the position property
+         */
         public static positionProperty: Prim2DPropInfo;
         public static positionProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the rotation property
+         */
         public static rotationProperty: Prim2DPropInfo;
         public static rotationProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the scale property
+         */
         public static scaleProperty: Prim2DPropInfo;
         public static scaleProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the origin property
+         */
         public static originProperty: Prim2DPropInfo;
         public static originProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the levelVisible property
+         */
         public static levelVisibleProperty: Prim2DPropInfo;
         public static levelVisibleProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the isVisible property
+         */
         public static isVisibleProperty: Prim2DPropInfo;
         public static isVisibleProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the zOrder property
+         */
         public static zOrderProperty: Prim2DPropInfo;
         public static zOrderProperty: Prim2DPropInfo;
 
 
         @instanceLevelProperty(1, pi => Prim2DBase.positionProperty = pi, false, true)
         @instanceLevelProperty(1, pi => Prim2DBase.positionProperty = pi, false, true)
+        /**
+         * Position of the primitive, relative to its parent.
+         */
         public get position(): Vector2 {
         public get position(): Vector2 {
             return this._position;
             return this._position;
         }
         }
@@ -86,6 +436,10 @@
         }
         }
 
 
         @instanceLevelProperty(2, pi => Prim2DBase.rotationProperty = pi, false, true)
         @instanceLevelProperty(2, pi => Prim2DBase.rotationProperty = pi, false, true)
+        /**
+         * Rotation of the primitive, in radian, along the Z axis
+         * @returns {} 
+         */
         public get rotation(): number {
         public get rotation(): number {
             return this._rotation;
             return this._rotation;
         }
         }
@@ -95,6 +449,9 @@
         }
         }
 
 
         @instanceLevelProperty(3, pi => Prim2DBase.scaleProperty = pi, false, true)
         @instanceLevelProperty(3, pi => Prim2DBase.scaleProperty = pi, false, true)
+        /**
+         * Uniform scale applied on the primitive
+         */
         public set scale(value: number) {
         public set scale(value: number) {
             this._scale = value;
             this._scale = value;
         }
         }
@@ -103,6 +460,14 @@
             return this._scale;
             return this._scale;
         }
         }
 
 
+        /**
+         * this method must be implemented by the primitive type to return its size
+         * @returns The size of the primitive
+         */
+        public get actualSize(): Size {
+            return undefined;
+        }
+
         @instanceLevelProperty(4, pi => Prim2DBase.originProperty = pi, false, true)
         @instanceLevelProperty(4, pi => Prim2DBase.originProperty = pi, false, true)
         public set origin(value: Vector2) {
         public set origin(value: Vector2) {
             this._origin = value;
             this._origin = value;
@@ -112,9 +477,9 @@
          * The origin defines the normalized coordinate of the center of the primitive, from the top/left corner.
          * The origin defines the normalized coordinate of the center of the primitive, from the top/left corner.
          * The origin is used only to compute transformation of the primitive, it has no meaning in the primitive local frame of reference
          * The origin is used only to compute transformation of the primitive, it has no meaning in the primitive local frame of reference
          * For instance:
          * For instance:
-         * 0,0 means the center is top/left
-         * 0.5,0.5 means the center is at the center of the primitive
-         * 0,1 means the center is bottom/left
+         * 0,0 means the center is bottom/left. Which is the default for Canvas2D instances
+         * 0.5,0.5 means the center is at the center of the primitive, which is default of all types of Primitives
+         * 0,1 means the center is top/left
          * @returns The normalized center.
          * @returns The normalized center.
          */
          */
         public get origin(): Vector2 {
         public get origin(): Vector2 {
@@ -122,6 +487,10 @@
         }
         }
 
 
         @dynamicLevelProperty(5, pi => Prim2DBase.levelVisibleProperty = pi)
         @dynamicLevelProperty(5, pi => Prim2DBase.levelVisibleProperty = pi)
+        /**
+         * Let the user defines if the Primitive is hidden or not at its level. As Primitives inherit the hidden status from their parent, only the isVisible property give properly the real visible state.
+         * Default is true, setting to false will hide this primitive and its children.
+         */
         public get levelVisible(): boolean {
         public get levelVisible(): boolean {
             return this._levelVisible;
             return this._levelVisible;
         }
         }
@@ -131,6 +500,10 @@
         }
         }
 
 
         @instanceLevelProperty(6, pi => Prim2DBase.isVisibleProperty = pi)
         @instanceLevelProperty(6, pi => Prim2DBase.isVisibleProperty = pi)
+        /**
+         * Use ONLY THE GETTER to determine if the primitive is visible or not.
+         * The Setter is for internal purpose only!
+         */
         public get isVisible(): boolean {
         public get isVisible(): boolean {
             return this._isVisible;
             return this._isVisible;
         }
         }
@@ -140,6 +513,10 @@
         }
         }
 
 
         @instanceLevelProperty(7, pi => Prim2DBase.zOrderProperty = pi)
         @instanceLevelProperty(7, pi => Prim2DBase.zOrderProperty = pi)
+        /**
+         * You can override the default Z Order through this property, but most of the time the default behavior is acceptable
+         * @returns {} 
+         */
         public get zOrder(): number {
         public get zOrder(): number {
             return this._zOrder;
             return this._zOrder;
         }
         }
@@ -148,22 +525,60 @@
             this._zOrder = value;
             this._zOrder = value;
         }
         }
 
 
+        /**
+         * Define if the Primitive can be subject to intersection test or not (default is true)
+         */
+        public get isPickable(): boolean {
+            return this._isPickable;
+        }
+
+        public set isPickable(value: boolean) {
+            this._isPickable = value;
+        }
+
+        /**
+         * Return the depth level of the Primitive into the Canvas' Graph. A Canvas will be 0, its direct children 1, and so on.
+         * @returns {} 
+         */
         public get hierarchyDepth(): number {
         public get hierarchyDepth(): number {
             return this._hierarchyDepth;
             return this._hierarchyDepth;
         }
         }
 
 
+        /**
+         * Retrieve the Group that is responsible to render this primitive
+         * @returns {} 
+         */
         public get renderGroup(): Group2D {
         public get renderGroup(): Group2D {
             return this._renderGroup;
             return this._renderGroup;
         }
         }
 
 
+        /**
+         * Get the global transformation matrix of the primitive
+         */
         public get globalTransform(): Matrix {
         public get globalTransform(): Matrix {
             return this._globalTransform;
             return this._globalTransform;
         }
         }
 
 
+        /**
+         * Get invert of the global transformation matrix of the primitive
+         * @returns {} 
+         */
         public get invGlobalTransform(): Matrix {
         public get invGlobalTransform(): Matrix {
             return this._invGlobalTransform;
             return this._invGlobalTransform;
         }
         }
 
 
+        /**
+         * Get the local transformation of the primitive
+         */
+        public get localTransform(): Matrix {
+            this._updateLocalTransform();
+            return this._localTransform;
+        }
+
+        /**
+         * Get the boundingInfo associated to the primitive.
+         * The value is supposed to be always up to date
+         */
         public get boundingInfo(): BoundingInfo2D {
         public get boundingInfo(): BoundingInfo2D {
             if (this._boundingInfoDirty) {
             if (this._boundingInfoDirty) {
                 this._boundingInfo = this.levelBoundingInfo.clone();
                 this._boundingInfo = this.levelBoundingInfo.clone();
@@ -171,8 +586,7 @@
 
 
                 var tps = new BoundingInfo2D();
                 var tps = new BoundingInfo2D();
                 for (let curChild of this._children) {
                 for (let curChild of this._children) {
-                    let t = curChild.globalTransform.multiply(this.invGlobalTransform);
-                    curChild.boundingInfo.transformToRef(t, curChild.origin, tps);
+                    curChild.boundingInfo.transformToRef(curChild.localTransform, tps);
                     bi.unionToRef(tps, bi);
                     bi.unionToRef(tps, bi);
                 }
                 }
 
 
@@ -181,6 +595,105 @@
             return this._boundingInfo;
             return this._boundingInfo;
         }
         }
 
 
+        /**
+         * Interaction with the primitive can be create using this Observable. See the PrimitivePointerInfo class for more information
+         */
+        public get pointerEventObservable(): Observable<PrimitivePointerInfo> {
+            return this._pointerEventObservable;
+        }
+
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+
+            return false;
+        }
+
+        /**
+         * Capture all the Events of the given PointerId for this primitive.
+         * Don't forget to call releasePointerEventsCapture when done.
+         * @param pointerId the Id of the pointer to capture the events from.
+         */
+        public setPointerEventCapture(pointerId: number): boolean {
+            return this.owner._setPointerCapture(pointerId, this);
+        }
+
+        /**
+         * Release a captured pointer made with setPointerEventCapture.
+         * @param pointerId the Id of the pointer to release the capture from.
+         */
+        public releasePointerEventsCapture(pointerId: number): boolean {
+            return this.owner._releasePointerCapture(pointerId, this);
+        }
+
+        /**
+         * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
+         * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
+         */
+        public intersect(intersectInfo: IntersectInfo2D): boolean {
+            if (!intersectInfo) {
+                return false;
+            }
+
+            // If this is null it means this method is call for the first level, initialize stuffs
+            let firstLevel = !intersectInfo._globalPickPosition;
+            if (firstLevel) {
+                // Compute the pickPosition in global space and use it to find the local position for each level down, always relative from the world to get the maximum accuracy (and speed). The other way would have been to compute in local every level down relative to its parent's local, which wouldn't be as accurate (even if javascript number is 80bits accurate).
+                intersectInfo._globalPickPosition = Vector2.Zero();
+                Vector2.TransformToRef(intersectInfo.pickPosition, this.globalTransform, intersectInfo._globalPickPosition);
+                intersectInfo._localPickPosition = intersectInfo.pickPosition.clone();
+                intersectInfo.intersectedPrimitives = new Array<PrimitiveIntersectedInfo>();
+                intersectInfo.topMostIntersectedPrimitive = null;
+            }
+
+            if (!intersectInfo.intersectHidden && !this.isVisible) {
+                return false;
+            }
+
+            // Fast rejection test with boundingInfo
+            if (!this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
+                // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
+                intersectInfo._exit(firstLevel);
+                return false;
+            }
+
+            // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
+            let levelIntersectRes = this.levelIntersect(intersectInfo);
+            if (levelIntersectRes) {
+                let pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
+                intersectInfo.intersectedPrimitives.push(pii);
+                if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.getActualZOffset() > pii.prim.getActualZOffset())) {
+                    intersectInfo.topMostIntersectedPrimitive = pii;
+                }
+
+                // If we must stop at the first intersection, we're done, quit!
+                if (intersectInfo.findFirstOnly) {
+                    intersectInfo._exit(firstLevel);
+                    return true;
+                }
+            }
+
+            // Recurse to children if needed
+            if (!levelIntersectRes || !intersectInfo.findFirstOnly) {
+                for (let curChild of this._children) {
+                    // Don't test primitive not pick able or if it's hidden and we don't test hidden ones
+                    if (!curChild.isPickable || (!intersectInfo.intersectHidden && !curChild.isVisible)) {
+                        continue;
+                    }
+
+                    // Must compute the localPickLocation for the children level
+                    Vector2.TransformToRef(intersectInfo._globalPickPosition, curChild.invGlobalTransform, intersectInfo._localPickPosition);
+
+                    // If we got an intersection with the child and we only need to find the first one, quit!
+                    if (curChild.intersect(intersectInfo) && intersectInfo.findFirstOnly) {
+                        intersectInfo._exit(firstLevel);
+                        return true;
+                    }
+                }
+            }
+
+            intersectInfo._exit(firstLevel);
+            return intersectInfo.isIntersected;
+        }
+
         public moveChild(child: Prim2DBase, previous: Prim2DBase): boolean {
         public moveChild(child: Prim2DBase, previous: Prim2DBase): boolean {
             if (child.parent !== this) {
             if (child.parent !== this) {
                 return false;
                 return false;
@@ -205,11 +718,9 @@
         }
         }
 
 
         private addChild(child: Prim2DBase) {
         private addChild(child: Prim2DBase) {
-            child._siblingDepthOffset = (this._children.length + 1) * this.owner.hierarchySiblingZDelta;
-            child._depthLevel = this._depthLevel + 1;
-            child._hierarchyDepthOffset = child._depthLevel * this.owner.hierarchyLevelZFactor;
+            child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
+            child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             this._children.push(child);
             this._children.push(child);
-
         }
         }
 
 
         public dispose(): boolean {
         public dispose(): boolean {
@@ -217,6 +728,11 @@
                 return false;
                 return false;
             }
             }
 
 
+            if (this._actionManager) {
+                this._actionManager.dispose();
+                this._actionManager = null;
+            }
+
             // If there's a parent, remove this object from its parent list
             // If there's a parent, remove this object from its parent list
             if (this._parent) {
             if (this._parent) {
                 let i = this._parent._children.indexOf(this);
                 let i = this._parent._children.indexOf(this);
@@ -236,8 +752,8 @@
             return true;
             return true;
         }
         }
 
 
-        protected getActualZOffset(): number {
-            return this._zOrder || 1 - (this._siblingDepthOffset + this._hierarchyDepthOffset);
+        public getActualZOffset(): number {
+            return this._zOrder || (1 - this._hierarchyDepthOffset);
         }
         }
 
 
         protected onPrimBecomesDirty() {
         protected onPrimBecomesDirty() {
@@ -246,8 +762,8 @@
             }
             }
         }
         }
 
 
-        public needPrepare(): boolean {
-            return (this.isVisible || this._visibilityChanged) && (this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep));
+        public _needPrepare(): boolean {
+            return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         }
         }
 
 
         public _prepareRender(context: Render2DContext) {
         public _prepareRender(context: Render2DContext) {
@@ -299,6 +815,28 @@
             }
             }
         }
         }
 
 
+        private _updateLocalTransform(): boolean {
+            let tflags = Prim2DBase.positionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId;
+            if (this.checkPropertiesDirty(tflags)) {
+                var rot = Quaternion.RotationAxis(new Vector3(0, 0, 1), this._rotation);
+                var local = Matrix.Compose(new Vector3(this._scale, this._scale, this._scale), rot, new Vector3(this._position.x, this._position.y, 0));
+
+                this._localTransform = local;
+                this.clearPropertiesDirty(tflags);
+
+                // this is important to access actualSize AFTER fetching a first version of the local transform and reset the dirty flag, because accessing actualSize on a Group2D which actualSize is built from its content will trigger a call to this very method on this very object. We won't mind about the origin offset not being computed, as long as we return a local transform based on the position/rotation/scale
+                //var actualSize = this.actualSize;
+                //if (!actualSize) {
+                //    throw new Error(`The primitive type: ${Tools.getClassName(this)} must implement the actualSize get property!`);
+                //}
+
+                //local.m[12] -= (actualSize.width * this.origin.x) * local.m[0] + (actualSize.height * this.origin.y) * local.m[4];
+                //local.m[13] -= (actualSize.width * this.origin.x) * local.m[1] + (actualSize.height * this.origin.y) * local.m[5];
+                return true;
+            }
+            return false;
+        }
+
         protected updateGlobalTransVis(recurse: boolean) {
         protected updateGlobalTransVis(recurse: boolean) {
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return;
                 return;
@@ -315,21 +853,18 @@
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
                 this.isVisible = (!this._parent || this._parent.isVisible) && this.levelVisible;
 
 
                 // Detect a change of visibility
                 // Detect a change of visibility
-                this._visibilityChanged = (curVisibleState !== undefined) && curVisibleState !== this.isVisible;
+                this._visibilityChanged = curVisibleState !== this.isVisible;
 
 
-                // Detect if either the parent or this node changed
-                let tflags = Prim2DBase.positionProperty.flagId | Prim2DBase.rotationProperty.flagId | Prim2DBase.scaleProperty.flagId;
-                if (this.isVisible && (this._parent && this._parent._globalTransformStep !== this._parentTransformStep) || this.checkPropertiesDirty(tflags)) {
-                    var rot = Quaternion.RotationAxis(new Vector3(0, 0, 1), this._rotation);
-                    var local = Matrix.Compose(new Vector3(this._scale, this._scale, this._scale), rot, new Vector3(this._position.x, this._position.y, 0));
+                // Get/compute the localTransform
+                let localDirty = this._updateLocalTransform();
 
 
-                    this._globalTransform = this._parent ? local.multiply(this._parent._globalTransform) : local;
+                // Check if we have to update the globalTransform
+                if (!this._globalTransform || localDirty || (this._parent && this._parent._globalTransformStep !== this._parentTransformStep)) {
+                    this._globalTransform = this._parent ? this._localTransform.multiply(this._parent._globalTransform) : this._localTransform;
                     this._invGlobalTransform = Matrix.Invert(this._globalTransform);
                     this._invGlobalTransform = Matrix.Invert(this._globalTransform);
 
 
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
-
-                    this.clearPropertiesDirty(tflags);
                 }
                 }
                 this._globalTransformProcessStep = this.owner._globalTransformProcessStep;
                 this._globalTransformProcessStep = this.owner._globalTransformProcessStep;
             }
             }
@@ -343,16 +878,18 @@
 
 
         private _owner: Canvas2D;
         private _owner: Canvas2D;
         private _parent: Prim2DBase;
         private _parent: Prim2DBase;
+        private _actionManager: ActionManager;
         protected _children: Array<Prim2DBase>;
         protected _children: Array<Prim2DBase>;
         private _renderGroup: Group2D;
         private _renderGroup: Group2D;
         private _hierarchyDepth: number;
         private _hierarchyDepth: number;
-        protected _depthLevel: number;
-        private _hierarchyDepthOffset: number;
-        private _siblingDepthOffset: number;
+        protected _hierarchyDepthOffset: number;
+        protected _siblingDepthOffset: number;
         private _zOrder: number;
         private _zOrder: number;
         private _levelVisible: boolean;
         private _levelVisible: boolean;
+        public _pointerEventObservable: Observable<PrimitivePointerInfo>;
         public _boundingInfoDirty: boolean;
         public _boundingInfoDirty: boolean;
         protected _visibilityChanged;
         protected _visibilityChanged;
+        private _isPickable;
         private _isVisible: boolean;
         private _isVisible: boolean;
         private _id: string;
         private _id: string;
         private _position: Vector2;
         private _position: Vector2;
@@ -370,6 +907,7 @@
 
 
         // Stores the previous 
         // Stores the previous 
         protected _globalTransformProcessStep: number;
         protected _globalTransformProcessStep: number;
+        protected _localTransform: Matrix;
         protected _globalTransform: Matrix;
         protected _globalTransform: Matrix;
         protected _invGlobalTransform: Matrix;
         protected _invGlobalTransform: Matrix;
     }
     }

+ 20 - 4
src/Canvas2d/babylon.rectangle2d.js

@@ -112,7 +112,7 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Rectangle2DRenderCache;
         return Rectangle2DRenderCache;
-    }(BABYLON.ModelRenderCache));
+    })(BABYLON.ModelRenderCache);
     BABYLON.Rectangle2DRenderCache = Rectangle2DRenderCache;
     BABYLON.Rectangle2DRenderCache = Rectangle2DRenderCache;
     var Rectangle2DInstanceData = (function (_super) {
     var Rectangle2DInstanceData = (function (_super) {
         __extends(Rectangle2DInstanceData, _super);
         __extends(Rectangle2DInstanceData, _super);
@@ -130,13 +130,20 @@ var BABYLON;
             BABYLON.instanceData()
             BABYLON.instanceData()
         ], Rectangle2DInstanceData.prototype, "properties", null);
         ], Rectangle2DInstanceData.prototype, "properties", null);
         return Rectangle2DInstanceData;
         return Rectangle2DInstanceData;
-    }(BABYLON.Shape2DInstanceData));
+    })(BABYLON.Shape2DInstanceData);
     BABYLON.Rectangle2DInstanceData = Rectangle2DInstanceData;
     BABYLON.Rectangle2DInstanceData = Rectangle2DInstanceData;
     var Rectangle2D = (function (_super) {
     var Rectangle2D = (function (_super) {
         __extends(Rectangle2D, _super);
         __extends(Rectangle2D, _super);
         function Rectangle2D() {
         function Rectangle2D() {
             _super.apply(this, arguments);
             _super.apply(this, arguments);
         }
         }
+        Object.defineProperty(Rectangle2D.prototype, "actualSize", {
+            get: function () {
+                return this.size;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Rectangle2D.prototype, "size", {
         Object.defineProperty(Rectangle2D.prototype, "size", {
             get: function () {
             get: function () {
                 return this._size;
                 return this._size;
@@ -168,8 +175,17 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Rectangle2D.prototype.levelIntersect = function (intersectInfo) {
+            // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
+            if (this.notRounded) {
+                return true;
+            }
+            // Well, for now we neglect the area where the pickPosition could be outside due to the roundRadius...
+            // TODO make REAL intersection test here!
+            return true;
+        };
         Rectangle2D.prototype.updateLevelBoundingInfo = function () {
         Rectangle2D.prototype.updateLevelBoundingInfo = function () {
-            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo);
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
         };
         };
         Rectangle2D.prototype.setupRectangle2D = function (owner, parent, id, position, size, roundRadius, fill, border, borderThickness) {
         Rectangle2D.prototype.setupRectangle2D = function (owner, parent, id, position, size, roundRadius, fill, border, borderThickness) {
             if (roundRadius === void 0) { roundRadius = 0; }
             if (roundRadius === void 0) { roundRadius = 0; }
@@ -296,6 +312,6 @@ var BABYLON;
             BABYLON.className("Rectangle2D")
             BABYLON.className("Rectangle2D")
         ], Rectangle2D);
         ], Rectangle2D);
         return Rectangle2D;
         return Rectangle2D;
-    }(BABYLON.Shape2D));
+    })(BABYLON.Shape2D);
     BABYLON.Rectangle2D = Rectangle2D;
     BABYLON.Rectangle2D = Rectangle2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 16 - 1
src/Canvas2d/babylon.rectangle2d.ts

@@ -148,6 +148,10 @@
         public static notRoundedProperty: Prim2DPropInfo;
         public static notRoundedProperty: Prim2DPropInfo;
         public static roundRadiusProperty: Prim2DPropInfo;
         public static roundRadiusProperty: Prim2DPropInfo;
 
 
+        public get actualSize(): Size {
+            return this.size;
+        }
+
         @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Rectangle2D.sizeProperty = pi, false, true)
         @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Rectangle2D.sizeProperty = pi, false, true)
         public get size(): Size {
         public get size(): Size {
             return this._size;
             return this._size;
@@ -176,8 +180,19 @@
             this.notRounded = value === 0;
             this.notRounded = value === 0;
         }
         }
 
 
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
+            if (this.notRounded) {
+                return true;
+            }
+
+            // Well, for now we neglect the area where the pickPosition could be outside due to the roundRadius...
+            // TODO make REAL intersection test here!
+            return true;
+        }
+
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
-            BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo);
+            BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
         }
         }
 
 
         protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, size: Size, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
         protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, size: Size, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {

+ 46 - 11
src/Canvas2d/babylon.renderablePrim2d.js

@@ -73,7 +73,7 @@ var BABYLON;
             return curOffset;
             return curOffset;
         };
         };
         return InstanceClassInfo;
         return InstanceClassInfo;
-    }());
+    })();
     BABYLON.InstanceClassInfo = InstanceClassInfo;
     BABYLON.InstanceClassInfo = InstanceClassInfo;
     var InstancePropInfo = (function () {
     var InstancePropInfo = (function () {
         //uniformLocation: WebGLUniformLocation;
         //uniformLocation: WebGLUniformLocation;
@@ -176,7 +176,7 @@ var BABYLON;
             }
             }
         };
         };
         return InstancePropInfo;
         return InstancePropInfo;
-    }());
+    })();
     BABYLON.InstancePropInfo = InstancePropInfo;
     BABYLON.InstancePropInfo = InstancePropInfo;
     function instanceData(category, shaderAttributeName) {
     function instanceData(category, shaderAttributeName) {
         return function (target, propName, descriptor) {
         return function (target, propName, descriptor) {
@@ -253,6 +253,9 @@ var BABYLON;
             return this.typeInfo;
             return this.typeInfo;
         };
         };
         InstanceDataBase.prototype.allocElements = function () {
         InstanceDataBase.prototype.allocElements = function () {
+            if (!this.dataBuffer) {
+                return;
+            }
             var res = new Array(this.dataElementCount);
             var res = new Array(this.dataElementCount);
             for (var i = 0; i < this.dataElementCount; i++) {
             for (var i = 0; i < this.dataElementCount; i++) {
                 res[i] = this.dataBuffer.allocElement();
                 res[i] = this.dataBuffer.allocElement();
@@ -260,12 +263,30 @@ var BABYLON;
             this.dataElements = res;
             this.dataElements = res;
         };
         };
         InstanceDataBase.prototype.freeElements = function () {
         InstanceDataBase.prototype.freeElements = function () {
+            if (!this.dataElements) {
+                return;
+            }
             for (var _i = 0, _a = this.dataElements; _i < _a.length; _i++) {
             for (var _i = 0, _a = this.dataElements; _i < _a.length; _i++) {
                 var ei = _a[_i];
                 var ei = _a[_i];
                 this.dataBuffer.freeElement(ei);
                 this.dataBuffer.freeElement(ei);
             }
             }
             this.dataElements = null;
             this.dataElements = null;
         };
         };
+        Object.defineProperty(InstanceDataBase.prototype, "dataElementCount", {
+            get: function () {
+                return this._dataElementCount;
+            },
+            set: function (value) {
+                if (value === this._dataElementCount) {
+                    return;
+                }
+                this.freeElements();
+                this._dataElementCount = value;
+                this.allocElements();
+            },
+            enumerable: true,
+            configurable: true
+        });
         __decorate([
         __decorate([
             instanceData()
             instanceData()
         ], InstanceDataBase.prototype, "zBias", null);
         ], InstanceDataBase.prototype, "zBias", null);
@@ -279,7 +300,7 @@ var BABYLON;
             instanceData()
             instanceData()
         ], InstanceDataBase.prototype, "origin", null);
         ], InstanceDataBase.prototype, "origin", null);
         return InstanceDataBase;
         return InstanceDataBase;
-    }());
+    })();
     BABYLON.InstanceDataBase = InstanceDataBase;
     BABYLON.InstanceDataBase = InstanceDataBase;
     var RenderablePrim2D = (function (_super) {
     var RenderablePrim2D = (function (_super) {
         __extends(RenderablePrim2D, _super);
         __extends(RenderablePrim2D, _super);
@@ -361,8 +382,8 @@ var BABYLON;
                     var usedCatList = new Array();
                     var usedCatList = new Array();
                     var partIdList = new Array();
                     var partIdList = new Array();
                     var joinedUsedCatList = new Array();
                     var joinedUsedCatList = new Array();
-                    for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {
-                        var dataPart = parts_1[_i];
+                    for (var _i = 0; _i < parts.length; _i++) {
+                        var dataPart = parts[_i];
                         var cat = this.getUsedShaderCategories(dataPart);
                         var cat = this.getUsedShaderCategories(dataPart);
                         var cti = dataPart.getClassTreeInfo();
                         var cti = dataPart.getClassTreeInfo();
                         // Make sure the instance is visible other the properties won't be set and their size/offset wont be computed
                         // Make sure the instance is visible other the properties won't be set and their size/offset wont be computed
@@ -373,7 +394,9 @@ var BABYLON;
                         var joinCat = cat.join(";");
                         var joinCat = cat.join(";");
                         joinedUsedCatList.push(joinCat);
                         joinedUsedCatList.push(joinCat);
                         InstanceClassInfo._CurCategories = joinCat;
                         InstanceClassInfo._CurCategories = joinCat;
+                        var obj = this.beforeRefreshForLayoutConstruction(dataPart);
                         this.refreshInstanceDataPart(dataPart);
                         this.refreshInstanceDataPart(dataPart);
+                        this.afterRefreshForLayoutConstruction(dataPart, obj);
                         this.isVisible = curVisible;
                         this.isVisible = curVisible;
                         var size = 0;
                         var size = 0;
                         cti.fullContent.forEach(function (k, v) {
                         cti.fullContent.forEach(function (k, v) {
@@ -488,28 +511,40 @@ var BABYLON;
         RenderablePrim2D.prototype.getUsedShaderCategories = function (dataPart) {
         RenderablePrim2D.prototype.getUsedShaderCategories = function (dataPart) {
             return [];
             return [];
         };
         };
+        RenderablePrim2D.prototype.beforeRefreshForLayoutConstruction = function (part) {
+        };
+        RenderablePrim2D.prototype.afterRefreshForLayoutConstruction = function (part, obj) {
+        };
         RenderablePrim2D.prototype.refreshInstanceDataPart = function (part) {
         RenderablePrim2D.prototype.refreshInstanceDataPart = function (part) {
             if (!this.isVisible) {
             if (!this.isVisible) {
                 return false;
                 return false;
             }
             }
             part.isVisible = this.isVisible;
             part.isVisible = this.isVisible;
-            // Which means, if there's only one data element, we're update it from this method, otherwise it is the responsability of the derived class to call updateInstanceDataPart as many times as needed, properly (look at Text2D's implementation for more information)
+            // Which means, if there's only one data element, we're update it from this method, otherwise it is the responsibility of the derived class to call updateInstanceDataPart as many times as needed, properly (look at Text2D's implementation for more information)
             if (part.dataElementCount === 1) {
             if (part.dataElementCount === 1) {
+                part.curElement = 0;
                 this.updateInstanceDataPart(part);
                 this.updateInstanceDataPart(part);
             }
             }
             return true;
             return true;
         };
         };
-        RenderablePrim2D.prototype.updateInstanceDataPart = function (part, positionOffset) {
+        /**
+         * Update the instanceDataBase level properties of a part
+         * @param part the part to update
+         * @param positionOffset to use in multi part per primitive (e.g. the Text2D has N parts for N letter to display), this give the offset to apply (e.g. the position of the letter from the bottom/left corner of the text). You MUST also set customSize.
+         * @param customSize to use in multi part per primitive, this is the size of the overall primitive to display (the bounding rect's size of the Text, for instance). This is mandatory to compute correct transformation based on the Primitive's origin property.
+         */
+        RenderablePrim2D.prototype.updateInstanceDataPart = function (part, positionOffset, customSize) {
             if (positionOffset === void 0) { positionOffset = null; }
             if (positionOffset === void 0) { positionOffset = null; }
+            if (customSize === void 0) { customSize = null; }
             var t = this._globalTransform.multiply(this.renderGroup.invGlobalTransform);
             var t = this._globalTransform.multiply(this.renderGroup.invGlobalTransform);
             var size = this.renderGroup.viewportSize;
             var size = this.renderGroup.viewportSize;
             var zBias = this.getActualZOffset();
             var zBias = this.getActualZOffset();
             var offX = 0;
             var offX = 0;
             var offY = 0;
             var offY = 0;
             // If there's an offset, apply the global transformation matrix on it to get a global offset
             // If there's an offset, apply the global transformation matrix on it to get a global offset
-            if (positionOffset) {
-                offX = positionOffset.x * t.m[0] + positionOffset.y * t.m[4];
-                offY = positionOffset.x * t.m[1] + positionOffset.y * t.m[5];
+            if (positionOffset && customSize) {
+                offX = (positionOffset.x - (customSize.width * this.origin.x)) * t.m[0] + (positionOffset.y - (customSize.height * this.origin.y)) * t.m[4];
+                offY = (positionOffset.x - (customSize.width * this.origin.x)) * t.m[1] + (positionOffset.y - (customSize.height * this.origin.y)) * t.m[5];
             }
             }
             // Have to convert the coordinates to clip space which is ranged between [-1;1] on X and Y axis, with 0,0 being the left/bottom corner
             // Have to convert the coordinates to clip space which is ranged between [-1;1] on X and Y axis, with 0,0 being the left/bottom corner
             // Current coordinates are expressed in renderGroup coordinates ([0, renderGroup.actualSize.width|height]) with 0,0 being at the left/top corner
             // Current coordinates are expressed in renderGroup coordinates ([0, renderGroup.actualSize.width|height]) with 0,0 being at the left/top corner
@@ -536,6 +571,6 @@ var BABYLON;
             BABYLON.className("RenderablePrim2D")
             BABYLON.className("RenderablePrim2D")
         ], RenderablePrim2D);
         ], RenderablePrim2D);
         return RenderablePrim2D;
         return RenderablePrim2D;
-    }(BABYLON.Prim2DBase));
+    })(BABYLON.Prim2DBase);
     BABYLON.RenderablePrim2D = RenderablePrim2D;
     BABYLON.RenderablePrim2D = RenderablePrim2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 44 - 6
src/Canvas2d/babylon.renderablePrim2d.ts

@@ -264,6 +264,9 @@
         }
         }
 
 
         allocElements() {
         allocElements() {
+            if (!this.dataBuffer) {
+                return;
+            }
             let res = new Array<DynamicFloatArrayElementInfo>(this.dataElementCount);
             let res = new Array<DynamicFloatArrayElementInfo>(this.dataElementCount);
             for (let i = 0; i < this.dataElementCount; i++) {
             for (let i = 0; i < this.dataElementCount; i++) {
                 res[i] = this.dataBuffer.allocElement();
                 res[i] = this.dataBuffer.allocElement();
@@ -272,18 +275,36 @@
         }
         }
 
 
         freeElements() {
         freeElements() {
+            if (!this.dataElements) {
+                return;
+            }
             for (let ei of this.dataElements) {
             for (let ei of this.dataElements) {
                 this.dataBuffer.freeElement(ei);
                 this.dataBuffer.freeElement(ei);
             }
             }
             this.dataElements = null;
             this.dataElements = null;
         }
         }
 
 
+        get dataElementCount(): number {
+            return this._dataElementCount;
+        }
+
+        set dataElementCount(value: number) {
+            if (value === this._dataElementCount) {
+                return;
+            }
+
+            this.freeElements();
+            this._dataElementCount = value;
+            this.allocElements();
+        }
+
         curElement: number;
         curElement: number;
-        dataElementCount: number;
         dataElements: DynamicFloatArrayElementInfo[];
         dataElements: DynamicFloatArrayElementInfo[];
         dataBuffer: DynamicFloatArray;
         dataBuffer: DynamicFloatArray;
         typeInfo: ClassTreeInfo<InstanceClassInfo, InstancePropInfo>;
         typeInfo: ClassTreeInfo<InstanceClassInfo, InstancePropInfo>;
 
 
+        private _dataElementCount: number;
+
     }
     }
 
 
     @className("RenderablePrim2D")
     @className("RenderablePrim2D")
@@ -390,7 +411,9 @@
                         let joinCat = cat.join(";");
                         let joinCat = cat.join(";");
                         joinedUsedCatList.push(joinCat);
                         joinedUsedCatList.push(joinCat);
                         InstanceClassInfo._CurCategories = joinCat;
                         InstanceClassInfo._CurCategories = joinCat;
+                        let obj = this.beforeRefreshForLayoutConstruction(dataPart);
                         this.refreshInstanceDataPart(dataPart);
                         this.refreshInstanceDataPart(dataPart);
+                        this.afterRefreshForLayoutConstruction(dataPart, obj);
                         this.isVisible = curVisible;
                         this.isVisible = curVisible;
 
 
                         var size = 0;
                         var size = 0;
@@ -522,20 +545,35 @@
             return [];
             return [];
         }
         }
 
 
+        protected beforeRefreshForLayoutConstruction(part: InstanceDataBase): any {
+
+        }
+
+        protected afterRefreshForLayoutConstruction(part: InstanceDataBase, obj: any) {
+
+        }
+
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
             if (!this.isVisible) {
             if (!this.isVisible) {
                 return false;
                 return false;
             }
             }
             part.isVisible = this.isVisible;
             part.isVisible = this.isVisible;
 
 
-            // Which means, if there's only one data element, we're update it from this method, otherwise it is the responsability of the derived class to call updateInstanceDataPart as many times as needed, properly (look at Text2D's implementation for more information)
+            // Which means, if there's only one data element, we're update it from this method, otherwise it is the responsibility of the derived class to call updateInstanceDataPart as many times as needed, properly (look at Text2D's implementation for more information)
             if (part.dataElementCount === 1) {
             if (part.dataElementCount === 1) {
+                part.curElement = 0;
                 this.updateInstanceDataPart(part);
                 this.updateInstanceDataPart(part);
             }
             }
             return true;
             return true;
         }
         }
 
 
-        protected updateInstanceDataPart(part: InstanceDataBase, positionOffset: Vector2 = null) {
+        /**
+         * Update the instanceDataBase level properties of a part
+         * @param part the part to update
+         * @param positionOffset to use in multi part per primitive (e.g. the Text2D has N parts for N letter to display), this give the offset to apply (e.g. the position of the letter from the bottom/left corner of the text). You MUST also set customSize.
+         * @param customSize to use in multi part per primitive, this is the size of the overall primitive to display (the bounding rect's size of the Text, for instance). This is mandatory to compute correct transformation based on the Primitive's origin property.
+         */
+        protected updateInstanceDataPart(part: InstanceDataBase, positionOffset: Vector2 = null, customSize: Size = null) {
             let t = this._globalTransform.multiply(this.renderGroup.invGlobalTransform);
             let t = this._globalTransform.multiply(this.renderGroup.invGlobalTransform);
             let size = (<Size>this.renderGroup.viewportSize);
             let size = (<Size>this.renderGroup.viewportSize);
             let zBias = this.getActualZOffset();
             let zBias = this.getActualZOffset();
@@ -543,9 +581,9 @@
             let offX = 0;
             let offX = 0;
             let offY = 0;
             let offY = 0;
             // If there's an offset, apply the global transformation matrix on it to get a global offset
             // If there's an offset, apply the global transformation matrix on it to get a global offset
-            if (positionOffset) {
-                offX = positionOffset.x * t.m[0] + positionOffset.y * t.m[4];
-                offY = positionOffset.x * t.m[1] + positionOffset.y * t.m[5];
+            if (positionOffset && customSize) {
+                offX = (positionOffset.x-(customSize.width*this.origin.x)) * t.m[0] + (positionOffset.y-(customSize.height*this.origin.y)) * t.m[4];
+                offY = (positionOffset.x-(customSize.width*this.origin.x)) * t.m[1] + (positionOffset.y-(customSize.height*this.origin.y)) * t.m[5];
             }
             }
 
 
             // Have to convert the coordinates to clip space which is ranged between [-1;1] on X and Y axis, with 0,0 being the left/bottom corner
             // Have to convert the coordinates to clip space which is ranged between [-1;1] on X and Y axis, with 0,0 being the left/bottom corner

+ 2 - 2
src/Canvas2d/babylon.shape2d.js

@@ -144,7 +144,7 @@ var BABYLON;
             BABYLON.className("Shape2D")
             BABYLON.className("Shape2D")
         ], Shape2D);
         ], Shape2D);
         return Shape2D;
         return Shape2D;
-    }(BABYLON.RenderablePrim2D));
+    })(BABYLON.RenderablePrim2D);
     BABYLON.Shape2D = Shape2D;
     BABYLON.Shape2D = Shape2D;
     var Shape2DInstanceData = (function (_super) {
     var Shape2DInstanceData = (function (_super) {
         __extends(Shape2DInstanceData, _super);
         __extends(Shape2DInstanceData, _super);
@@ -244,6 +244,6 @@ var BABYLON;
             BABYLON.instanceData(Shape2D.SHAPE2D_CATEGORY_BORDERGRADIENT)
             BABYLON.instanceData(Shape2D.SHAPE2D_CATEGORY_BORDERGRADIENT)
         ], Shape2DInstanceData.prototype, "borderGradientTY", null);
         ], Shape2DInstanceData.prototype, "borderGradientTY", null);
         return Shape2DInstanceData;
         return Shape2DInstanceData;
-    }(BABYLON.InstanceDataBase));
+    })(BABYLON.InstanceDataBase);
     BABYLON.Shape2DInstanceData = Shape2DInstanceData;
     BABYLON.Shape2DInstanceData = Shape2DInstanceData;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 90 - 27
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -10,7 +10,7 @@ var BABYLON;
         function Prim2DClassInfo() {
         function Prim2DClassInfo() {
         }
         }
         return Prim2DClassInfo;
         return Prim2DClassInfo;
-    }());
+    })();
     BABYLON.Prim2DClassInfo = Prim2DClassInfo;
     BABYLON.Prim2DClassInfo = Prim2DClassInfo;
     var Prim2DPropInfo = (function () {
     var Prim2DPropInfo = (function () {
         function Prim2DPropInfo() {
         function Prim2DPropInfo() {
@@ -19,13 +19,13 @@ var BABYLON;
         Prim2DPropInfo.PROPKIND_INSTANCE = 2;
         Prim2DPropInfo.PROPKIND_INSTANCE = 2;
         Prim2DPropInfo.PROPKIND_DYNAMIC = 3;
         Prim2DPropInfo.PROPKIND_DYNAMIC = 3;
         return Prim2DPropInfo;
         return Prim2DPropInfo;
-    }());
+    })();
     BABYLON.Prim2DPropInfo = Prim2DPropInfo;
     BABYLON.Prim2DPropInfo = Prim2DPropInfo;
     var PropertyChangedInfo = (function () {
     var PropertyChangedInfo = (function () {
         function PropertyChangedInfo() {
         function PropertyChangedInfo() {
         }
         }
         return PropertyChangedInfo;
         return PropertyChangedInfo;
-    }());
+    })();
     BABYLON.PropertyChangedInfo = PropertyChangedInfo;
     BABYLON.PropertyChangedInfo = PropertyChangedInfo;
     var ClassTreeInfo = (function () {
     var ClassTreeInfo = (function () {
         function ClassTreeInfo(baseClass, type, classContentFactory) {
         function ClassTreeInfo(baseClass, type, classContentFactory) {
@@ -62,13 +62,13 @@ var BABYLON;
         Object.defineProperty(ClassTreeInfo.prototype, "fullContent", {
         Object.defineProperty(ClassTreeInfo.prototype, "fullContent", {
             get: function () {
             get: function () {
                 if (!this._fullContent) {
                 if (!this._fullContent) {
-                    var dic_1 = new BABYLON.StringDictionary();
+                    var dic = new BABYLON.StringDictionary();
                     var curLevel = this;
                     var curLevel = this;
                     while (curLevel) {
                     while (curLevel) {
-                        curLevel.levelContent.forEach(function (k, v) { return dic_1.add(k, v); });
+                        curLevel.levelContent.forEach(function (k, v) { return dic.add(k, v); });
                         curLevel = curLevel._baseClass;
                         curLevel = curLevel._baseClass;
                     }
                     }
-                    this._fullContent = dic_1;
+                    this._fullContent = dic;
                 }
                 }
                 return this._fullContent;
                 return this._fullContent;
             },
             },
@@ -128,7 +128,7 @@ var BABYLON;
             return dic;
             return dic;
         };
         };
         return ClassTreeInfo;
         return ClassTreeInfo;
-    }());
+    })();
     BABYLON.ClassTreeInfo = ClassTreeInfo;
     BABYLON.ClassTreeInfo = ClassTreeInfo;
     var SmartPropertyPrim = (function () {
     var SmartPropertyPrim = (function () {
         function SmartPropertyPrim() {
         function SmartPropertyPrim() {
@@ -140,22 +140,45 @@ var BABYLON;
             this._instanceDirtyFlags = 0;
             this._instanceDirtyFlags = 0;
             this._isDisposed = false;
             this._isDisposed = false;
             this._levelBoundingInfo = new BABYLON.BoundingInfo2D();
             this._levelBoundingInfo = new BABYLON.BoundingInfo2D();
+            this.animations = new Array();
         };
         };
         Object.defineProperty(SmartPropertyPrim.prototype, "isDisposed", {
         Object.defineProperty(SmartPropertyPrim.prototype, "isDisposed", {
+            /**
+             * Check if the object is disposed or not.
+             * @returns true if the object is dispose, false otherwise.
+             */
             get: function () {
             get: function () {
                 return this._isDisposed;
                 return this._isDisposed;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        /**
+         * Disposable pattern, this method must be overloaded by derived types in order to clean up hardware related resources.
+         * @returns false if the object is already dispose, true otherwise. Your implementation must call super.dispose() and check for a false return and return immediately if it's the case.
+         */
         SmartPropertyPrim.prototype.dispose = function () {
         SmartPropertyPrim.prototype.dispose = function () {
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return false;
                 return false;
             }
             }
+            // Don't set to null, it may upset somebody...
+            this.animations.splice(0);
             this._isDisposed = true;
             this._isDisposed = true;
             return true;
             return true;
         };
         };
+        /**
+         * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
+         * Look at Sprite2D for more information
+         */
+        SmartPropertyPrim.prototype.getAnimatables = function () {
+            return new Array();
+        };
         Object.defineProperty(SmartPropertyPrim.prototype, "modelKey", {
         Object.defineProperty(SmartPropertyPrim.prototype, "modelKey", {
+            /**
+             * Property giving the Model Key associated to the property.
+             * This value is constructed from the type of the primitive and all the name/value of its properties declared with the modelLevelProperty decorator
+             * @returns the model key string.
+             */
             get: function () {
             get: function () {
                 var _this = this;
                 var _this = this;
                 // No need to compute it?
                 // No need to compute it?
@@ -178,6 +201,10 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(SmartPropertyPrim.prototype, "isDirty", {
         Object.defineProperty(SmartPropertyPrim.prototype, "isDirty", {
+            /**
+             * States if the Primitive is dirty and should be rendered again next time.
+             * @returns true is dirty, false otherwise
+             */
             get: function () {
             get: function () {
                 return (this._instanceDirtyFlags !== 0) || this._modelDirty;
                 return (this._instanceDirtyFlags !== 0) || this._modelDirty;
             },
             },
@@ -185,6 +212,10 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Object.defineProperty(SmartPropertyPrim.prototype, "propDic", {
         Object.defineProperty(SmartPropertyPrim.prototype, "propDic", {
+            /**
+             * Access the dictionary of properties metadata. Only properties decorated with XXXXLevelProperty are concerned
+             * @returns the dictionary, the key is the property name as declared in Javascript, the value is the metadata object
+             */
             get: function () {
             get: function () {
                 if (!this._propInfo) {
                 if (!this._propInfo) {
                     var cti = ClassTreeInfo.get(Object.getPrototypeOf(this));
                     var cti = ClassTreeInfo.get(Object.getPrototypeOf(this));
@@ -213,7 +244,7 @@ var BABYLON;
             propInfo.name = propName;
             propInfo.name = propName;
             propInfo.dirtyBoundingInfo = dirtyBoundingInfo;
             propInfo.dirtyBoundingInfo = dirtyBoundingInfo;
             propInfo.typeLevelCompare = typeLevelCompare;
             propInfo.typeLevelCompare = typeLevelCompare;
-            node.levelContent.add(propId.toString(), propInfo);
+            node.levelContent.add(propName, propInfo);
             return propInfo;
             return propInfo;
         };
         };
         SmartPropertyPrim._checkUnchanged = function (curValue, newValue) {
         SmartPropertyPrim._checkUnchanged = function (curValue, newValue) {
@@ -236,7 +267,36 @@ var BABYLON;
             }
             }
             return false;
             return false;
         };
         };
+        SmartPropertyPrim.prototype.markAsDirty = function (propertyName) {
+            var i = propertyName.indexOf(".");
+            if (i !== -1) {
+                propertyName = propertyName.substr(0, i);
+            }
+            var propInfo = this.propDic.get(propertyName);
+            if (!propInfo) {
+                return;
+            }
+            var newValue = this[propertyName];
+            this._handlePropChanged(undefined, newValue, propertyName, propInfo, propInfo.typeLevelCompare);
+        };
         SmartPropertyPrim.prototype._handlePropChanged = function (curValue, newValue, propName, propInfo, typeLevelCompare) {
         SmartPropertyPrim.prototype._handlePropChanged = function (curValue, newValue, propName, propInfo, typeLevelCompare) {
+            // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
+            if (propInfo.dirtyBoundingInfo) {
+                this._levelBoundingInfoDirty = true;
+                // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
+                if (this instanceof BABYLON.Prim2DBase) {
+                    var curprim = this.parent;
+                    while (curprim) {
+                        curprim._boundingInfoDirty = true;
+                        if (curprim instanceof BABYLON.Group2D) {
+                            if (curprim.isRenderableGroup) {
+                                break;
+                            }
+                        }
+                        curprim = curprim.parent;
+                    }
+                }
+            }
             // Trigger property changed
             // Trigger property changed
             var info = SmartPropertyPrim.propChangedInfo;
             var info = SmartPropertyPrim.propChangedInfo;
             info.oldValue = curValue;
             info.oldValue = curValue;
@@ -263,7 +323,7 @@ var BABYLON;
                     }
                     }
                     this._modelDirty = true;
                     this._modelDirty = true;
                 }
                 }
-                else if (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) {
+                else if ((propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) || (propInfo.kind === Prim2DPropInfo.PROPKIND_DYNAMIC)) {
                     if (!this.isDirty) {
                     if (!this.isDirty) {
                         this.onPrimBecomesDirty();
                         this.onPrimBecomesDirty();
                     }
                     }
@@ -273,14 +333,28 @@ var BABYLON;
         };
         };
         SmartPropertyPrim.prototype.handleGroupChanged = function (prop) {
         SmartPropertyPrim.prototype.handleGroupChanged = function (prop) {
         };
         };
+        /**
+         * Check if a given set of properties are dirty or not.
+         * @param flags a ORed combination of Prim2DPropInfo.flagId values
+         * @return true if at least one property is dirty, false if none of them are.
+         */
         SmartPropertyPrim.prototype.checkPropertiesDirty = function (flags) {
         SmartPropertyPrim.prototype.checkPropertiesDirty = function (flags) {
             return (this._instanceDirtyFlags & flags) !== 0;
             return (this._instanceDirtyFlags & flags) !== 0;
         };
         };
+        /**
+         * Clear a given set of properties.
+         * @param flags a ORed combination of Prim2DPropInfo.flagId values
+         * @return the new set of property still marked as dirty
+         */
         SmartPropertyPrim.prototype.clearPropertiesDirty = function (flags) {
         SmartPropertyPrim.prototype.clearPropertiesDirty = function (flags) {
             this._instanceDirtyFlags &= ~flags;
             this._instanceDirtyFlags &= ~flags;
             return this._instanceDirtyFlags;
             return this._instanceDirtyFlags;
         };
         };
         Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
         Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
+            /**
+             * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
+             * @returns {}
+             */
             get: function () {
             get: function () {
                 if (this._levelBoundingInfoDirty) {
                 if (this._levelBoundingInfoDirty) {
                     this.updateLevelBoundingInfo();
                     this.updateLevelBoundingInfo();
@@ -291,8 +365,14 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        /**
+         * This method must be overridden by a given Primitive implementation to compute its boundingInfo
+         */
         SmartPropertyPrim.prototype.updateLevelBoundingInfo = function () {
         SmartPropertyPrim.prototype.updateLevelBoundingInfo = function () {
         };
         };
+        /**
+         * Property method called when the Primitive becomes dirty
+         */
         SmartPropertyPrim.prototype.onPrimBecomesDirty = function () {
         SmartPropertyPrim.prototype.onPrimBecomesDirty = function () {
         };
         };
         SmartPropertyPrim._hookProperty = function (propId, piStore, typeLevelCompare, dirtyBoundingInfo, kind) {
         SmartPropertyPrim._hookProperty = function (propId, piStore, typeLevelCompare, dirtyBoundingInfo, kind) {
@@ -316,23 +396,6 @@ var BABYLON;
                     var prim = this;
                     var prim = this;
                     // Change the value
                     // Change the value
                     setter.call(this, val);
                     setter.call(this, val);
-                    // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
-                    if (propInfo.dirtyBoundingInfo) {
-                        prim._levelBoundingInfoDirty = true;
-                        // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
-                        if (prim instanceof BABYLON.Prim2DBase) {
-                            var curprim = prim.parent;
-                            while (curprim) {
-                                curprim._boundingInfoDirty = true;
-                                if (curprim instanceof BABYLON.Group2D) {
-                                    if (curprim.isRenderableGroup) {
-                                        break;
-                                    }
-                                }
-                                curprim = curprim.parent;
-                            }
-                        }
-                    }
                     // Notify change, dirty flags update
                     // Notify change, dirty flags update
                     prim._handlePropChanged(curVal, val, propName, propInfo, typeLevelCompare);
                     prim._handlePropChanged(curVal, val, propName, propInfo, typeLevelCompare);
                 };
                 };
@@ -343,7 +406,7 @@ var BABYLON;
             BABYLON.className("SmartPropertyPrim")
             BABYLON.className("SmartPropertyPrim")
         ], SmartPropertyPrim);
         ], SmartPropertyPrim);
         return SmartPropertyPrim;
         return SmartPropertyPrim;
-    }());
+    })();
     BABYLON.SmartPropertyPrim = SmartPropertyPrim;
     BABYLON.SmartPropertyPrim = SmartPropertyPrim;
     function modelLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo) {
     function modelLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo) {
         if (typeLevelCompare === void 0) { typeLevelCompare = false; }
         if (typeLevelCompare === void 0) { typeLevelCompare = false; }

+ 102 - 25
src/Canvas2d/babylon.smartPropertyPrim.ts

@@ -91,7 +91,7 @@
                     }
                     }
                 }
                 }
                 let node = new ClassTreeInfo<TClass, TProp>(this, type, this._classContentFactory);
                 let node = new ClassTreeInfo<TClass, TProp>(this, type, this._classContentFactory);
-                let info = { type: type, node: node};
+                let info = { type: type, node: node };
                 this._subClasses.push(info);
                 this._subClasses.push(info);
                 return info.node;
                 return info.node;
             }
             }
@@ -126,7 +126,7 @@
         private _type: Object;
         private _type: Object;
         private _classContent: TClass;
         private _classContent: TClass;
         private _baseClass: ClassTreeInfo<TClass, TProp>;
         private _baseClass: ClassTreeInfo<TClass, TProp>;
-        private _subClasses: Array<{type: Object, node: ClassTreeInfo<TClass, TProp>}>;
+        private _subClasses: Array<{ type: Object, node: ClassTreeInfo<TClass, TProp> }>;
         private _levelContent: StringDictionary<TProp>;
         private _levelContent: StringDictionary<TProp>;
         private _fullContent: StringDictionary<TProp>;
         private _fullContent: StringDictionary<TProp>;
         private _classContentFactory: (base: TClass) => TClass;
         private _classContentFactory: (base: TClass) => TClass;
@@ -142,23 +142,57 @@
             this._instanceDirtyFlags = 0;
             this._instanceDirtyFlags = 0;
             this._isDisposed = false;
             this._isDisposed = false;
             this._levelBoundingInfo = new BoundingInfo2D();
             this._levelBoundingInfo = new BoundingInfo2D();
+            this.animations = new Array<Animation>();
         }
         }
 
 
+        /**
+         * An observable that is triggered when a property (using of the XXXXLevelProperty decorator) has its value changing.
+         * You can add an observer that will be triggered only for a given set of Properties using the Mask feature of the Observable and the corresponding Prim2DPropInfo.flagid value (e.g. Prim2DBase.positionProperty.flagid|Prim2DBase.rotationProperty.flagid to be notified only about position or rotation change)
+         */
         public propertyChanged: Observable<PropertyChangedInfo>;
         public propertyChanged: Observable<PropertyChangedInfo>;
 
 
+        /**
+         * Check if the object is disposed or not.
+         * @returns true if the object is dispose, false otherwise.
+         */
         public get isDisposed(): boolean {
         public get isDisposed(): boolean {
             return this._isDisposed;
             return this._isDisposed;
         }
         }
 
 
+        /**
+         * Disposable pattern, this method must be overloaded by derived types in order to clean up hardware related resources.
+         * @returns false if the object is already dispose, true otherwise. Your implementation must call super.dispose() and check for a false return and return immediately if it's the case.
+         */
         public dispose(): boolean {
         public dispose(): boolean {
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return false;
                 return false;
             }
             }
 
 
+            // Don't set to null, it may upset somebody...
+            this.animations.splice(0);
+
             this._isDisposed = true;
             this._isDisposed = true;
             return true;
             return true;
         }
         }
 
 
+        /**
+         * Animation array, more info: http://doc.babylonjs.com/tutorials/Animations
+         */
+        public animations: Animation[];
+
+        /**
+         * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
+         * Look at Sprite2D for more information
+         */
+        public getAnimatables(): IAnimatable[] {
+            return new Array<IAnimatable>();
+        }
+
+        /**
+         * Property giving the Model Key associated to the property.
+         * This value is constructed from the type of the primitive and all the name/value of its properties declared with the modelLevelProperty decorator
+         * @returns the model key string.
+         */
         public get modelKey(): string {
         public get modelKey(): string {
 
 
             // No need to compute it?
             // No need to compute it?
@@ -181,10 +215,18 @@
             return modelKey;
             return modelKey;
         }
         }
 
 
+        /**
+         * States if the Primitive is dirty and should be rendered again next time.
+         * @returns true is dirty, false otherwise
+         */
         public get isDirty(): boolean {
         public get isDirty(): boolean {
             return (this._instanceDirtyFlags !== 0) || this._modelDirty;
             return (this._instanceDirtyFlags !== 0) || this._modelDirty;
         }
         }
 
 
+        /**
+         * Access the dictionary of properties metadata. Only properties decorated with XXXXLevelProperty are concerned
+         * @returns the dictionary, the key is the property name as declared in Javascript, the value is the metadata object
+         */
         private get propDic(): StringDictionary<Prim2DPropInfo> {
         private get propDic(): StringDictionary<Prim2DPropInfo> {
             if (!this._propInfo) {
             if (!this._propInfo) {
                 let cti = ClassTreeInfo.get<Prim2DClassInfo, Prim2DPropInfo>(Object.getPrototypeOf(this));
                 let cti = ClassTreeInfo.get<Prim2DClassInfo, Prim2DPropInfo>(Object.getPrototypeOf(this));
@@ -214,7 +256,7 @@
             propInfo.name = propName;
             propInfo.name = propName;
             propInfo.dirtyBoundingInfo = dirtyBoundingInfo;
             propInfo.dirtyBoundingInfo = dirtyBoundingInfo;
             propInfo.typeLevelCompare = typeLevelCompare;
             propInfo.typeLevelCompare = typeLevelCompare;
-            node.levelContent.add(propId.toString(), propInfo);
+            node.levelContent.add(propName, propInfo);
 
 
             return propInfo;
             return propInfo;
         }
         }
@@ -243,7 +285,43 @@
 
 
         private static propChangedInfo = new PropertyChangedInfo();
         private static propChangedInfo = new PropertyChangedInfo();
 
 
+        public markAsDirty(propertyName: string) {
+            let i = propertyName.indexOf(".");
+            if (i !== -1) {
+                propertyName = propertyName.substr(0, i);
+            }
+
+            var propInfo = this.propDic.get(propertyName);
+            if (!propInfo) {
+                return;
+            }
+
+            var newValue = this[propertyName];
+            this._handlePropChanged(undefined, newValue, propertyName, propInfo, propInfo.typeLevelCompare);
+        }
+
         private _handlePropChanged<T>(curValue: T, newValue: T, propName: string, propInfo: Prim2DPropInfo, typeLevelCompare: boolean) {
         private _handlePropChanged<T>(curValue: T, newValue: T, propName: string, propInfo: Prim2DPropInfo, typeLevelCompare: boolean) {
+            // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
+            if (propInfo.dirtyBoundingInfo) {
+                this._levelBoundingInfoDirty = true;
+
+                // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
+                if (this instanceof Prim2DBase) {
+                    let curprim = (<any>this).parent;
+                    while (curprim) {
+                        curprim._boundingInfoDirty = true;
+
+                        if (curprim instanceof Group2D) {
+                            if (curprim.isRenderableGroup) {
+                                break;
+                            }
+                        }
+
+                        curprim = curprim.parent;
+                    }
+                }
+            }
+
             // Trigger property changed
             // Trigger property changed
             let info = SmartPropertyPrim.propChangedInfo;
             let info = SmartPropertyPrim.propChangedInfo;
             info.oldValue = curValue;
             info.oldValue = curValue;
@@ -273,7 +351,7 @@
                         this.onPrimBecomesDirty();
                         this.onPrimBecomesDirty();
                     }
                     }
                     this._modelDirty = true;
                     this._modelDirty = true;
-                } else if (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) {
+                } else if ((propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) || (propInfo.kind === Prim2DPropInfo.PROPKIND_DYNAMIC)) {
                     if (!this.isDirty) {
                     if (!this.isDirty) {
                         this.onPrimBecomesDirty();
                         this.onPrimBecomesDirty();
                     }
                     }
@@ -286,15 +364,29 @@
 
 
         }
         }
 
 
+        /**
+         * Check if a given set of properties are dirty or not.
+         * @param flags a ORed combination of Prim2DPropInfo.flagId values
+         * @return true if at least one property is dirty, false if none of them are.
+         */
         public checkPropertiesDirty(flags: number): boolean {
         public checkPropertiesDirty(flags: number): boolean {
             return (this._instanceDirtyFlags & flags) !== 0;
             return (this._instanceDirtyFlags & flags) !== 0;
         }
         }
 
 
+        /**
+         * Clear a given set of properties.
+         * @param flags a ORed combination of Prim2DPropInfo.flagId values
+         * @return the new set of property still marked as dirty
+         */
         protected clearPropertiesDirty(flags: number): number {
         protected clearPropertiesDirty(flags: number): number {
             this._instanceDirtyFlags &= ~flags;
             this._instanceDirtyFlags &= ~flags;
             return this._instanceDirtyFlags;
             return this._instanceDirtyFlags;
         }
         }
 
 
+        /**
+         * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
+         * @returns {} 
+         */
         public get levelBoundingInfo(): BoundingInfo2D {
         public get levelBoundingInfo(): BoundingInfo2D {
             if (this._levelBoundingInfoDirty) {
             if (this._levelBoundingInfoDirty) {
                 this.updateLevelBoundingInfo();
                 this.updateLevelBoundingInfo();
@@ -303,10 +395,16 @@
             return this._levelBoundingInfo;
             return this._levelBoundingInfo;
         }
         }
 
 
+        /**
+         * This method must be overridden by a given Primitive implementation to compute its boundingInfo
+         */
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
 
 
         }
         }
 
 
+        /**
+         * Property method called when the Primitive becomes dirty
+         */
         protected onPrimBecomesDirty() {
         protected onPrimBecomesDirty() {
 
 
         }
         }
@@ -339,27 +437,6 @@
                     // Change the value
                     // Change the value
                     setter.call(this, val);
                     setter.call(this, val);
 
 
-                    // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
-                    if (propInfo.dirtyBoundingInfo) {
-                        prim._levelBoundingInfoDirty = true;
-
-                        // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
-                        if (prim instanceof Prim2DBase) {
-                            let curprim = prim.parent;
-                            while (curprim) {
-                                curprim._boundingInfoDirty = true;
-
-                                if (curprim instanceof Group2D) {
-                                    if (curprim.isRenderableGroup) {
-                                        break;
-                                    }
-                                }
-
-                                curprim = curprim.parent;
-                            }
-                        }
-                    }
-
                     // Notify change, dirty flags update
                     // Notify change, dirty flags update
                     prim._handlePropChanged(curVal, val, <string>propName, propInfo, typeLevelCompare);
                     prim._handlePropChanged(curVal, val, <string>propName, propInfo, typeLevelCompare);
                 }
                 }

+ 22 - 4
src/Canvas2d/babylon.sprite2d.js

@@ -69,7 +69,7 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Sprite2DRenderCache;
         return Sprite2DRenderCache;
-    }(BABYLON.ModelRenderCache));
+    })(BABYLON.ModelRenderCache);
     BABYLON.Sprite2DRenderCache = Sprite2DRenderCache;
     BABYLON.Sprite2DRenderCache = Sprite2DRenderCache;
     var Sprite2DInstanceData = (function (_super) {
     var Sprite2DInstanceData = (function (_super) {
         __extends(Sprite2DInstanceData, _super);
         __extends(Sprite2DInstanceData, _super);
@@ -127,7 +127,7 @@ var BABYLON;
             BABYLON.instanceData()
             BABYLON.instanceData()
         ], Sprite2DInstanceData.prototype, "invertY", null);
         ], Sprite2DInstanceData.prototype, "invertY", null);
         return Sprite2DInstanceData;
         return Sprite2DInstanceData;
-    }(BABYLON.InstanceDataBase));
+    })(BABYLON.InstanceDataBase);
     BABYLON.Sprite2DInstanceData = Sprite2DInstanceData;
     BABYLON.Sprite2DInstanceData = Sprite2DInstanceData;
     var Sprite2D = (function (_super) {
     var Sprite2D = (function (_super) {
         __extends(Sprite2D, _super);
         __extends(Sprite2D, _super);
@@ -144,6 +144,13 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Object.defineProperty(Sprite2D.prototype, "actualSize", {
+            get: function () {
+                return this.spriteSize;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Sprite2D.prototype, "spriteSize", {
         Object.defineProperty(Sprite2D.prototype, "spriteSize", {
             get: function () {
             get: function () {
                 return this._size;
                 return this._size;
@@ -185,7 +192,18 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Sprite2D.prototype.updateLevelBoundingInfo = function () {
         Sprite2D.prototype.updateLevelBoundingInfo = function () {
-            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo);
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo, this.origin);
+        };
+        Sprite2D.prototype.getAnimatables = function () {
+            var res = new Array();
+            if (this.texture && this.texture.animations && this.texture.animations.length > 0) {
+                res.push(this.texture);
+            }
+            return res;
+        };
+        Sprite2D.prototype.levelIntersect = function (intersectInfo) {
+            // If we've made it so far it means the boundingInfo intersection test succeed, the Sprite2D is shaped the same, so we always return true
+            return true;
         };
         };
         Sprite2D.prototype.setupSprite2D = function (owner, parent, id, position, texture, spriteSize, spriteLocation, invertY) {
         Sprite2D.prototype.setupSprite2D = function (owner, parent, id, position, texture, spriteSize, spriteLocation, invertY) {
             this.setupRenderablePrim2D(owner, parent, id, position, true);
             this.setupRenderablePrim2D(owner, parent, id, position, true);
@@ -278,6 +296,6 @@ var BABYLON;
             BABYLON.className("Sprite2D")
             BABYLON.className("Sprite2D")
         ], Sprite2D);
         ], Sprite2D);
         return Sprite2D;
         return Sprite2D;
-    }(BABYLON.RenderablePrim2D));
+    })(BABYLON.RenderablePrim2D);
     BABYLON.Sprite2D = Sprite2D;
     BABYLON.Sprite2D = Sprite2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 19 - 1
src/Canvas2d/babylon.sprite2d.ts

@@ -121,6 +121,10 @@
             this._texture = value;
             this._texture = value;
         }
         }
 
 
+        public get actualSize(): Size {
+            return this.spriteSize;
+        }
+
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Sprite2D.spriteSizeProperty = pi, false, true)
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => Sprite2D.spriteSizeProperty = pi, false, true)
         public get spriteSize(): Size {
         public get spriteSize(): Size {
             return this._size;
             return this._size;
@@ -158,7 +162,21 @@
         }
         }
 
 
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
-            BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo);
+            BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo, this.origin);
+        }
+
+        public getAnimatables(): IAnimatable[] {
+            let res = new Array<IAnimatable>();
+
+            if (this.texture && this.texture.animations && this.texture.animations.length > 0) {
+                res.push(this.texture);
+            }
+            return res;
+        }
+
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            // If we've made it so far it means the boundingInfo intersection test succeed, the Sprite2D is shaped the same, so we always return true
+            return true;
         }
         }
 
 
         protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean) {
         protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean) {

+ 36 - 17
src/Canvas2d/babylon.text2d.js

@@ -31,7 +31,7 @@ var BABYLON;
             this.effect.setTexture("diffuseSampler", this.fontTexture);
             this.effect.setTexture("diffuseSampler", this.fontTexture);
             engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
             engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
             var cur = engine.getAlphaMode();
             var cur = engine.getAlphaMode();
-            engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+            engine.setAlphaMode(BABYLON.Engine.ALPHA_ADD);
             var count = instanceInfo._instancesPartsData[0].usedElementCount;
             var count = instanceInfo._instancesPartsData[0].usedElementCount;
             if (instanceInfo._owner.owner.supportInstancedArray) {
             if (instanceInfo._owner.owner.supportInstancedArray) {
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
@@ -70,7 +70,7 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Text2DRenderCache;
         return Text2DRenderCache;
-    }(BABYLON.ModelRenderCache));
+    })(BABYLON.ModelRenderCache);
     BABYLON.Text2DRenderCache = Text2DRenderCache;
     BABYLON.Text2DRenderCache = Text2DRenderCache;
     var Text2DInstanceData = (function (_super) {
     var Text2DInstanceData = (function (_super) {
         __extends(Text2DInstanceData, _super);
         __extends(Text2DInstanceData, _super);
@@ -118,7 +118,7 @@ var BABYLON;
             BABYLON.instanceData()
             BABYLON.instanceData()
         ], Text2DInstanceData.prototype, "color", null);
         ], Text2DInstanceData.prototype, "color", null);
         return Text2DInstanceData;
         return Text2DInstanceData;
-    }(BABYLON.InstanceDataBase));
+    })(BABYLON.InstanceDataBase);
     BABYLON.Text2DInstanceData = Text2DInstanceData;
     BABYLON.Text2DInstanceData = Text2DInstanceData;
     var Text2D = (function (_super) {
     var Text2D = (function (_super) {
         __extends(Text2D, _super);
         __extends(Text2D, _super);
@@ -154,7 +154,7 @@ var BABYLON;
             },
             },
             set: function (value) {
             set: function (value) {
                 this._text = value;
                 this._text = value;
-                this._actualAreaSize = null; // A change of text will reset the Actual Area Size which will be recomputed next time it's used
+                this._actualSize = null; // A change of text will reset the Actual Area Size which will be recomputed next time it's used
                 this._updateCharCount();
                 this._updateCharCount();
             },
             },
             enumerable: true,
             enumerable: true,
@@ -190,16 +190,16 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        Object.defineProperty(Text2D.prototype, "actualAreaSize", {
+        Object.defineProperty(Text2D.prototype, "actualSize", {
             get: function () {
             get: function () {
                 if (this.areaSize) {
                 if (this.areaSize) {
                     return this.areaSize;
                     return this.areaSize;
                 }
                 }
-                if (this._actualAreaSize) {
-                    return this._actualAreaSize;
+                if (this._actualSize) {
+                    return this._actualSize;
                 }
                 }
-                this._actualAreaSize = this.fontTexture.measureText(this._text, this._tabulationSize);
-                return this._actualAreaSize;
+                this._actualSize = this.fontTexture.measureText(this._text, this._tabulationSize);
+                return this._actualSize;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -226,7 +226,7 @@ var BABYLON;
             return true;
             return true;
         };
         };
         Text2D.prototype.updateLevelBoundingInfo = function () {
         Text2D.prototype.updateLevelBoundingInfo = function () {
-            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.actualAreaSize, this._levelBoundingInfo);
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo, this.origin);
         };
         };
         Text2D.prototype.setupText2D = function (owner, parent, id, position, fontName, text, areaSize, defaultFontColor, vAlign, hAlign, tabulationSize) {
         Text2D.prototype.setupText2D = function (owner, parent, id, position, fontName, text, areaSize, defaultFontColor, vAlign, hAlign, tabulationSize) {
             this.setupRenderablePrim2D(owner, parent, id, position, true);
             this.setupRenderablePrim2D(owner, parent, id, position, true);
@@ -238,7 +238,6 @@ var BABYLON;
             this.hAlign = hAlign;
             this.hAlign = hAlign;
             this._tabulationSize = tabulationSize;
             this._tabulationSize = tabulationSize;
             this._isTransparent = true;
             this._isTransparent = true;
-            this.origin = BABYLON.Vector2.Zero();
         };
         };
         Text2D.Create = function (parent, id, x, y, fontName, text, defaultFontColor, areaSize, vAlign, hAlign, tabulationSize) {
         Text2D.Create = function (parent, id, x, y, fontName, text, defaultFontColor, areaSize, vAlign, hAlign, tabulationSize) {
             if (vAlign === void 0) { vAlign = Text2D.TEXT2D_VALIGN_TOP; }
             if (vAlign === void 0) { vAlign = Text2D.TEXT2D_VALIGN_TOP; }
@@ -249,6 +248,10 @@ var BABYLON;
             text2d.setupText2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), fontName, text, areaSize, defaultFontColor || new BABYLON.Color4(0, 0, 0, 1), vAlign, hAlign, tabulationSize);
             text2d.setupText2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), fontName, text, areaSize, defaultFontColor || new BABYLON.Color4(0, 0, 0, 1), vAlign, hAlign, tabulationSize);
             return text2d;
             return text2d;
         };
         };
+        Text2D.prototype.levelIntersect = function (intersectInfo) {
+            // For now I can't do something better that boundingInfo is a hit, detecting an intersection on a particular letter would be possible, but do we really need it? Not for now...
+            return true;
+        };
         Text2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
         Text2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
             var renderCache = new Text2DRenderCache(this.owner.engine, modelKey, isTransparent);
             var renderCache = new Text2DRenderCache(this.owner.engine, modelKey, isTransparent);
             return renderCache;
             return renderCache;
@@ -272,14 +275,28 @@ var BABYLON;
             renderCache.ib = engine.createIndexBuffer(ib);
             renderCache.ib = engine.createIndexBuffer(ib);
             // Effects
             // Effects
             var ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
             var ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
-            renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null, function (e) {
-                //                renderCache.setupUniformsLocation(e, ei.uniforms, Text2D.TEXT2D_MAINPARTID);
-            });
+            renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
             return renderCache;
             return renderCache;
         };
         };
         Text2D.prototype.createInstanceDataParts = function () {
         Text2D.prototype.createInstanceDataParts = function () {
             return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
             return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
         };
         };
+        // Looks like a hack!? Yes! Because that's what it is!
+        // For the InstanceData layer to compute correctly we need to set all the properties involved, which won't be the case if there's no text
+        // This method is called before the layout construction for us to detect this case, set some text and return the initial one to restore it after (there can be some text without char to display, say "\t\n" for instance)
+        Text2D.prototype.beforeRefreshForLayoutConstruction = function (part) {
+            if (!this._charCount) {
+                var curText = this._text;
+                this.text = "A";
+                return curText;
+            }
+        };
+        // if obj contains something, we restore the _text property
+        Text2D.prototype.afterRefreshForLayoutConstruction = function (part, obj) {
+            if (obj !== undefined) {
+                this.text = obj;
+            }
+        };
         Text2D.prototype.refreshInstanceDataPart = function (part) {
         Text2D.prototype.refreshInstanceDataPart = function (part) {
             if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
             if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
                 return false;
                 return false;
@@ -288,15 +305,17 @@ var BABYLON;
                 var d = part;
                 var d = part;
                 var texture = this.fontTexture;
                 var texture = this.fontTexture;
                 var ts = texture.getSize();
                 var ts = texture.getSize();
+                var textSize = texture.measureText(this.text, this._tabulationSize);
                 var offset = BABYLON.Vector2.Zero();
                 var offset = BABYLON.Vector2.Zero();
                 var charxpos = 0;
                 var charxpos = 0;
+                d.dataElementCount = this._charCount;
                 d.curElement = 0;
                 d.curElement = 0;
                 for (var _i = 0, _a = this.text; _i < _a.length; _i++) {
                 for (var _i = 0, _a = this.text; _i < _a.length; _i++) {
                     var char = _a[_i];
                     var char = _a[_i];
                     // Line feed
                     // Line feed
                     if (char === "\n") {
                     if (char === "\n") {
                         offset.x = 0;
                         offset.x = 0;
-                        offset.y += texture.lineHeight;
+                        offset.y -= texture.lineHeight;
                     }
                     }
                     // Tabulation ?
                     // Tabulation ?
                     if (char === "\t") {
                     if (char === "\t") {
@@ -309,7 +328,7 @@ var BABYLON;
                     if (char < " ") {
                     if (char < " ") {
                         continue;
                         continue;
                     }
                     }
-                    this.updateInstanceDataPart(d, offset);
+                    this.updateInstanceDataPart(d, offset, textSize);
                     var ci = texture.getChar(char);
                     var ci = texture.getChar(char);
                     offset.x += ci.charWidth;
                     offset.x += ci.charWidth;
                     d.topLeftUV = ci.topLeftUV;
                     d.topLeftUV = ci.topLeftUV;
@@ -362,6 +381,6 @@ var BABYLON;
             BABYLON.className("Text2D")
             BABYLON.className("Text2D")
         ], Text2D);
         ], Text2D);
         return Text2D;
         return Text2D;
-    }(BABYLON.RenderablePrim2D));
+    })(BABYLON.RenderablePrim2D);
     BABYLON.Text2D = Text2D;
     BABYLON.Text2D = Text2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 37 - 16
src/Canvas2d/babylon.text2d.ts

@@ -25,7 +25,7 @@
             engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
             engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
 
 
             var cur = engine.getAlphaMode();
             var cur = engine.getAlphaMode();
-            engine.setAlphaMode(Engine.ALPHA_COMBINE);
+            engine.setAlphaMode(Engine.ALPHA_ADD);
             let count = instanceInfo._instancesPartsData[0].usedElementCount;
             let count = instanceInfo._instancesPartsData[0].usedElementCount;
             if (instanceInfo._owner.owner.supportInstancedArray) {
             if (instanceInfo._owner.owner.supportInstancedArray) {
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
@@ -145,7 +145,7 @@
 
 
         public set text(value: string) {
         public set text(value: string) {
             this._text = value;
             this._text = value;
-            this._actualAreaSize = null;    // A change of text will reset the Actual Area Size which will be recomputed next time it's used
+            this._actualSize = null;    // A change of text will reset the Actual Area Size which will be recomputed next time it's used
             this._updateCharCount();
             this._updateCharCount();
         }
         }
 
 
@@ -176,18 +176,18 @@
             this._hAlign = value;
             this._hAlign = value;
         }
         }
 
 
-        public get actualAreaSize(): Size {
+        public get actualSize(): Size {
             if (this.areaSize) {
             if (this.areaSize) {
                 return this.areaSize;
                 return this.areaSize;
             }
             }
 
 
-            if (this._actualAreaSize) {
-                return this._actualAreaSize;
+            if (this._actualSize) {
+                return this._actualSize;
             }
             }
 
 
-            this._actualAreaSize = this.fontTexture.measureText(this._text, this._tabulationSize);
+            this._actualSize = this.fontTexture.measureText(this._text, this._tabulationSize);
 
 
-            return this._actualAreaSize;
+            return this._actualSize;
         }
         }
 
 
         protected get fontTexture(): FontTexture {
         protected get fontTexture(): FontTexture {
@@ -213,7 +213,7 @@
         }
         }
 
 
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
-            BoundingInfo2D.CreateFromSizeToRef(this.actualAreaSize, this._levelBoundingInfo);
+            BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo, this.origin);
         }
         }
 
 
         protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, fontName: string, text: string, areaSize: Size, defaultFontColor: Color4, vAlign, hAlign, tabulationSize: number) {
         protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, fontName: string, text: string, areaSize: Size, defaultFontColor: Color4, vAlign, hAlign, tabulationSize: number) {
@@ -227,7 +227,6 @@
             this.hAlign = hAlign;
             this.hAlign = hAlign;
             this._tabulationSize = tabulationSize;
             this._tabulationSize = tabulationSize;
             this._isTransparent = true;
             this._isTransparent = true;
-            this.origin = Vector2.Zero();
         }
         }
 
 
         public static Create(parent: Prim2DBase, id: string, x: number, y: number, fontName: string, text: string, defaultFontColor?: Color4, areaSize?: Size, vAlign = Text2D.TEXT2D_VALIGN_TOP, hAlign = Text2D.TEXT2D_HALIGN_LEFT, tabulationSize: number = 4): Text2D {
         public static Create(parent: Prim2DBase, id: string, x: number, y: number, fontName: string, text: string, defaultFontColor?: Color4, areaSize?: Size, vAlign = Text2D.TEXT2D_VALIGN_TOP, hAlign = Text2D.TEXT2D_HALIGN_LEFT, tabulationSize: number = 4): Text2D {
@@ -238,6 +237,11 @@
             return text2d;
             return text2d;
         }
         }
 
 
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            // For now I can't do something better that boundingInfo is a hit, detecting an intersection on a particular letter would be possible, but do we really need it? Not for now...
+            return true;
+        }
+
         protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
         protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
             let renderCache = new Text2DRenderCache(this.owner.engine, modelKey, isTransparent);
             let renderCache = new Text2DRenderCache(this.owner.engine, modelKey, isTransparent);
             return renderCache;
             return renderCache;
@@ -267,9 +271,7 @@
 
 
             // Effects
             // Effects
             let ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
             let ei = this.getDataPartEffectInfo(Text2D.TEXT2D_MAINPARTID, ["index"]);
-            renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null, e => {
-//                renderCache.setupUniformsLocation(e, ei.uniforms, Text2D.TEXT2D_MAINPARTID);
-            });
+            renderCache.effect = engine.createEffect("text2d", ei.attributes, ei.uniforms, ["diffuseSampler"], ei.defines, null);
 
 
             return renderCache;
             return renderCache;
         }
         }
@@ -278,6 +280,24 @@
             return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
             return [new Text2DInstanceData(Text2D.TEXT2D_MAINPARTID, this._charCount)];
         }
         }
 
 
+        // Looks like a hack!? Yes! Because that's what it is!
+        // For the InstanceData layer to compute correctly we need to set all the properties involved, which won't be the case if there's no text
+        // This method is called before the layout construction for us to detect this case, set some text and return the initial one to restore it after (there can be some text without char to display, say "\t\n" for instance)
+        protected beforeRefreshForLayoutConstruction(part: InstanceDataBase): any {
+            if (!this._charCount) {
+                let curText = this._text;
+                this.text = "A";
+                return curText;
+            }
+        }
+
+        // if obj contains something, we restore the _text property
+        protected afterRefreshForLayoutConstruction(part: InstanceDataBase, obj: any) {
+            if (obj !== undefined) {
+                this.text = obj;
+            }
+        }
+
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
             if (!super.refreshInstanceDataPart(part)) {
             if (!super.refreshInstanceDataPart(part)) {
                 return false;
                 return false;
@@ -287,16 +307,17 @@
                 let d = <Text2DInstanceData>part;
                 let d = <Text2DInstanceData>part;
                 let texture = this.fontTexture;
                 let texture = this.fontTexture;
                 let ts = texture.getSize();
                 let ts = texture.getSize();
-
+                let textSize = texture.measureText(this.text, this._tabulationSize);
                 let offset = Vector2.Zero();
                 let offset = Vector2.Zero();
                 let charxpos = 0;
                 let charxpos = 0;
+                d.dataElementCount = this._charCount;
                 d.curElement = 0;
                 d.curElement = 0;
                 for (let char of this.text) {
                 for (let char of this.text) {
 
 
                     // Line feed
                     // Line feed
                     if (char === "\n") {
                     if (char === "\n") {
                         offset.x = 0;
                         offset.x = 0;
-                        offset.y += texture.lineHeight;
+                        offset.y -= texture.lineHeight;
                     }
                     }
 
 
                     // Tabulation ?
                     // Tabulation ?
@@ -313,7 +334,7 @@
                         continue;
                         continue;
                     }
                     }
 
 
-                    this.updateInstanceDataPart(d, offset);
+                    this.updateInstanceDataPart(d, offset, textSize);
 
 
                     let ci = texture.getChar(char);
                     let ci = texture.getChar(char);
                     offset.x += ci.charWidth;
                     offset.x += ci.charWidth;
@@ -348,7 +369,7 @@
         private _defaultFontColor: Color4;
         private _defaultFontColor: Color4;
         private _text: string;
         private _text: string;
         private _areaSize: Size;
         private _areaSize: Size;
-        private _actualAreaSize: Size;
+        private _actualSize: Size;
         private _vAlign: number;
         private _vAlign: number;
         private _hAlign: number;
         private _hAlign: number;
     }
     }

+ 1 - 1
src/Canvas2d/babylon.worldSpaceCanvas2d.js

@@ -22,6 +22,6 @@ var BABYLON;
             }
             }
         };
         };
         return WorldSpaceCanvas2d;
         return WorldSpaceCanvas2d;
-    }(BABYLON.Mesh));
+    })(BABYLON.Mesh);
     BABYLON.WorldSpaceCanvas2d = WorldSpaceCanvas2d;
     BABYLON.WorldSpaceCanvas2d = WorldSpaceCanvas2d;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Collisions/babylon.collider.js

@@ -267,6 +267,6 @@ var BABYLON;
             this._destinationPoint.subtractToRef(this.intersectionPoint, vel);
             this._destinationPoint.subtractToRef(this.intersectionPoint, vel);
         };
         };
         return Collider;
         return Collider;
-    }());
+    })();
     BABYLON.Collider = Collider;
     BABYLON.Collider = Collider;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 2 - 2
src/Collisions/babylon.collisionCoordinator.js

@@ -205,7 +205,7 @@ var BABYLON;
             };
             };
         };
         };
         return CollisionCoordinatorWorker;
         return CollisionCoordinatorWorker;
-    }());
+    })();
     BABYLON.CollisionCoordinatorWorker = CollisionCoordinatorWorker;
     BABYLON.CollisionCoordinatorWorker = CollisionCoordinatorWorker;
     var CollisionCoordinatorLegacy = (function () {
     var CollisionCoordinatorLegacy = (function () {
         function CollisionCoordinatorLegacy() {
         function CollisionCoordinatorLegacy() {
@@ -268,6 +268,6 @@ var BABYLON;
             this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
             this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
         };
         };
         return CollisionCoordinatorLegacy;
         return CollisionCoordinatorLegacy;
-    }());
+    })();
     BABYLON.CollisionCoordinatorLegacy = CollisionCoordinatorLegacy;
     BABYLON.CollisionCoordinatorLegacy = CollisionCoordinatorLegacy;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 3 - 3
src/Collisions/babylon.collisionWorker.js

@@ -32,7 +32,7 @@ var BABYLON;
             delete this._geometries[id];
             delete this._geometries[id];
         };
         };
         return CollisionCache;
         return CollisionCache;
-    }());
+    })();
     BABYLON.CollisionCache = CollisionCache;
     BABYLON.CollisionCache = CollisionCache;
     var CollideWorker = (function () {
     var CollideWorker = (function () {
         function CollideWorker(collider, _collisionCache, finalPosition) {
         function CollideWorker(collider, _collisionCache, finalPosition) {
@@ -144,7 +144,7 @@ var BABYLON;
             return this.collider._canDoCollision(BABYLON.Vector3.FromArray(subMesh.sphereCenter), subMesh.sphereRadius, BABYLON.Vector3.FromArray(subMesh.boxMinimum), BABYLON.Vector3.FromArray(subMesh.boxMaximum));
             return this.collider._canDoCollision(BABYLON.Vector3.FromArray(subMesh.sphereCenter), subMesh.sphereRadius, BABYLON.Vector3.FromArray(subMesh.boxMinimum), BABYLON.Vector3.FromArray(subMesh.boxMaximum));
         };
         };
         return CollideWorker;
         return CollideWorker;
-    }());
+    })();
     BABYLON.CollideWorker = CollideWorker;
     BABYLON.CollideWorker = CollideWorker;
     var CollisionDetectorTransferable = (function () {
     var CollisionDetectorTransferable = (function () {
         function CollisionDetectorTransferable() {
         function CollisionDetectorTransferable() {
@@ -206,7 +206,7 @@ var BABYLON;
             postMessage(reply, undefined);
             postMessage(reply, undefined);
         };
         };
         return CollisionDetectorTransferable;
         return CollisionDetectorTransferable;
-    }());
+    })();
     BABYLON.CollisionDetectorTransferable = CollisionDetectorTransferable;
     BABYLON.CollisionDetectorTransferable = CollisionDetectorTransferable;
     //check if we are in a web worker, as this code should NOT run on the main UI thread
     //check if we are in a web worker, as this code should NOT run on the main UI thread
     try {
     try {

+ 2 - 2
src/Collisions/babylon.pickingInfo.js

@@ -9,7 +9,7 @@ var BABYLON;
             this.subMeshId = 0;
             this.subMeshId = 0;
         }
         }
         return IntersectionInfo;
         return IntersectionInfo;
-    }());
+    })();
     BABYLON.IntersectionInfo = IntersectionInfo;
     BABYLON.IntersectionInfo = IntersectionInfo;
     var PickingInfo = (function () {
     var PickingInfo = (function () {
         function PickingInfo() {
         function PickingInfo() {
@@ -71,6 +71,6 @@ var BABYLON;
             return new BABYLON.Vector2(uv0.x + uv1.x + uv2.x, uv0.y + uv1.y + uv2.y);
             return new BABYLON.Vector2(uv0.x + uv1.x + uv2.x, uv0.y + uv1.y + uv2.y);
         };
         };
         return PickingInfo;
         return PickingInfo;
-    }());
+    })();
     BABYLON.PickingInfo = PickingInfo;
     BABYLON.PickingInfo = PickingInfo;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Culling/Octrees/babylon.octree.js

@@ -83,6 +83,6 @@ var BABYLON;
             }
             }
         };
         };
         return Octree;
         return Octree;
-    }());
+    })();
     BABYLON.Octree = Octree;
     BABYLON.Octree = Octree;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Culling/Octrees/babylon.octreeBlock.js

@@ -117,6 +117,6 @@ var BABYLON;
             BABYLON.Octree._CreateBlocks(this._minPoint, this._maxPoint, this.entries, this._capacity, this._depth, this._maxDepth, this, this._creationFunc);
             BABYLON.Octree._CreateBlocks(this._minPoint, this._maxPoint, this.entries, this._capacity, this._depth, this._maxDepth, this, this._creationFunc);
         };
         };
         return OctreeBlock;
         return OctreeBlock;
-    }());
+    })();
     BABYLON.OctreeBlock = OctreeBlock;
     BABYLON.OctreeBlock = OctreeBlock;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Culling/babylon.boundingBox.js

@@ -138,6 +138,6 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return BoundingBox;
         return BoundingBox;
-    }());
+    })();
     BABYLON.BoundingBox = BoundingBox;
     BABYLON.BoundingBox = BoundingBox;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Culling/babylon.boundingInfo.js

@@ -114,6 +114,6 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return BoundingInfo;
         return BoundingInfo;
-    }());
+    })();
     BABYLON.BoundingInfo = BoundingInfo;
     BABYLON.BoundingInfo = BoundingInfo;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Culling/babylon.boundingSphere.js

@@ -44,6 +44,6 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return BoundingSphere;
         return BoundingSphere;
-    }());
+    })();
     BABYLON.BoundingSphere = BoundingSphere;
     BABYLON.BoundingSphere = BoundingSphere;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Culling/babylon.ray.js

@@ -269,6 +269,6 @@ var BABYLON;
         Ray.smallnum = 0.00000001;
         Ray.smallnum = 0.00000001;
         Ray.rayl = 10e8;
         Ray.rayl = 10e8;
         return Ray;
         return Ray;
-    }());
+    })();
     BABYLON.Ray = Ray;
     BABYLON.Ray = Ray;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Debug/babylon.debugLayer.js

@@ -674,6 +674,6 @@ var BABYLON;
             }
             }
         };
         };
         return DebugLayer;
         return DebugLayer;
-    }());
+    })();
     BABYLON.DebugLayer = DebugLayer;
     BABYLON.DebugLayer = DebugLayer;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Debug/babylon.skeletonViewer.js

@@ -131,7 +131,7 @@ var BABYLON;
                 }
                 }
             };
             };
             return SkeletonViewer;
             return SkeletonViewer;
-        }());
+        })();
         Debug.SkeletonViewer = SkeletonViewer;
         Debug.SkeletonViewer = SkeletonViewer;
     })(Debug = BABYLON.Debug || (BABYLON.Debug = {}));
     })(Debug = BABYLON.Debug || (BABYLON.Debug = {}));
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Layer/babylon.layer.js

@@ -133,6 +133,6 @@ var BABYLON;
             this.onBeforeRenderObservable.clear();
             this.onBeforeRenderObservable.clear();
         };
         };
         return Layer;
         return Layer;
-    }());
+    })();
     BABYLON.Layer = Layer;
     BABYLON.Layer = Layer;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/LensFlare/babylon.lensFlare.js

@@ -18,6 +18,6 @@ var BABYLON;
             system.lensFlares.push(this);
             system.lensFlares.push(this);
         }
         }
         return LensFlare;
         return LensFlare;
-    }());
+    })();
     BABYLON.LensFlare = LensFlare;
     BABYLON.LensFlare = LensFlare;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/LensFlare/babylon.lensFlareSystem.js

@@ -208,6 +208,6 @@ var BABYLON;
             return serializationObject;
             return serializationObject;
         };
         };
         return LensFlareSystem;
         return LensFlareSystem;
-    }());
+    })();
     BABYLON.LensFlareSystem = LensFlareSystem;
     BABYLON.LensFlareSystem = LensFlareSystem;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Lights/Shadows/babylon.shadowGenerator.js

@@ -402,6 +402,6 @@ var BABYLON;
         ShadowGenerator._FILTER_POISSONSAMPLING = 2;
         ShadowGenerator._FILTER_POISSONSAMPLING = 2;
         ShadowGenerator._FILTER_BLURVARIANCESHADOWMAP = 3;
         ShadowGenerator._FILTER_BLURVARIANCESHADOWMAP = 3;
         return ShadowGenerator;
         return ShadowGenerator;
-    }());
+    })();
     BABYLON.ShadowGenerator = ShadowGenerator;
     BABYLON.ShadowGenerator = ShadowGenerator;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Lights/babylon.directionalLight.js

@@ -124,6 +124,6 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], DirectionalLight.prototype, "autoUpdateExtends", void 0);
         ], DirectionalLight.prototype, "autoUpdateExtends", void 0);
         return DirectionalLight;
         return DirectionalLight;
-    }(BABYLON.Light));
+    })(BABYLON.Light);
     BABYLON.DirectionalLight = DirectionalLight;
     BABYLON.DirectionalLight = DirectionalLight;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 0 - 0
src/Lights/babylon.hemisphericLight.js


部分文件因为文件数量过多而无法显示