فهرست منبع

Merge pull request #7 from BabylonJS/master

update from upstream
Loïc Baumann 9 سال پیش
والد
کامیت
70da4aea6b
100فایلهای تغییر یافته به همراه12955 افزوده شده و 7099 حذف شده
  1. 8 2
      Exporters/Blender/io_export_babylon.py
  2. 2 0
      Exporters/Unity 5/Unity3D2Babylon/ExportationOptions.cs
  3. 1 1
      Exporters/Unity 5/Unity3D2Babylon/ExporterWindow.cs
  4. 27 23
      Tools/Gulp/config.json
  5. 21 10
      Tools/Gulp/gulpfile.js
  6. 2 1
      Tools/Gulp/package.json
  7. 25 25
      dist/preview release/babylon.core.js
  8. 6315 5696
      dist/preview release/babylon.d.ts
  9. 38 37
      dist/preview release/babylon.js
  10. 3204 847
      dist/preview release/babylon.max.js
  11. 38 37
      dist/preview release/babylon.noworker.js
  12. 26 8
      dist/preview release/what's new.md
  13. 1 1
      src/Actions/babylon.action.js
  14. 2 2
      src/Actions/babylon.actionManager.js
  15. 4 4
      src/Actions/babylon.condition.js
  16. 12 12
      src/Actions/babylon.directActions.js
  17. 1 1
      src/Actions/babylon.interpolateValueAction.js
  18. 1 1
      src/Animations/babylon.animatable.js
  19. 4 4
      src/Animations/babylon.animation.js
  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. 5 1
      src/Bones/babylon.skeleton.js
  27. 20 16
      src/Bones/babylon.skeleton.ts
  28. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.gamepad.js
  29. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js
  30. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.mousewheel.js
  31. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js
  32. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js
  33. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.js
  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. 2 2
      src/Cameras/VR/babylon.vrDeviceOrientationCamera.js
  42. 11 8
      src/Cameras/VR/babylon.webVRCamera.js
  43. 12 9
      src/Cameras/VR/babylon.webVRCamera.ts
  44. 1 1
      src/Cameras/babylon.arcRotateCamera.js
  45. 1 1
      src/Cameras/babylon.arcRotateCameraInputsManager.js
  46. 4 1
      src/Cameras/babylon.camera.js
  47. 37 38
      src/Cameras/babylon.camera.ts
  48. 1 1
      src/Cameras/babylon.cameraInputsManager.js
  49. 1 1
      src/Cameras/babylon.deviceOrientationCamera.js
  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. 1 1
      src/Cameras/babylon.targetCamera.js
  56. 1 1
      src/Cameras/babylon.touchCamera.js
  57. 1 1
      src/Cameras/babylon.universalCamera.js
  58. 1 1
      src/Cameras/babylon.virtualJoysticksCamera.js
  59. 3 3
      src/Canvas2d/babylon.bounding2d.js
  60. 3 3
      src/Canvas2d/babylon.brushes2d.js
  61. 42 37
      src/Canvas2d/babylon.canvas2d.js
  62. 46 33
      src/Canvas2d/babylon.canvas2d.ts
  63. 306 0
      src/Canvas2d/babylon.ellipse2d.js
  64. 321 0
      src/Canvas2d/babylon.ellipse2d.ts
  65. 21 11
      src/Canvas2d/babylon.group2d.js
  66. 17 6
      src/Canvas2d/babylon.group2d.ts
  67. 991 0
      src/Canvas2d/babylon.lines2d.js
  68. 973 0
      src/Canvas2d/babylon.lines2d.ts
  69. 2 2
      src/Canvas2d/babylon.modelRenderCache.js
  70. 1 1
      src/Canvas2d/babylon.modelRenderCache.ts
  71. 9 9
      src/Canvas2d/babylon.prim2dBase.js
  72. 2 2
      src/Canvas2d/babylon.prim2dBase.ts
  73. 34 22
      src/Canvas2d/babylon.rectangle2d.js
  74. 30 19
      src/Canvas2d/babylon.rectangle2d.ts
  75. 48 12
      src/Canvas2d/babylon.renderablePrim2d.js
  76. 46 6
      src/Canvas2d/babylon.renderablePrim2d.ts
  77. 4 4
      src/Canvas2d/babylon.shape2d.js
  78. 2 4
      src/Canvas2d/babylon.shape2d.ts
  79. 21 8
      src/Canvas2d/babylon.smartPropertyPrim.js
  80. 16 1
      src/Canvas2d/babylon.smartPropertyPrim.ts
  81. 30 14
      src/Canvas2d/babylon.sprite2d.js
  82. 28 10
      src/Canvas2d/babylon.sprite2d.ts
  83. 25 12
      src/Canvas2d/babylon.text2d.js
  84. 22 6
      src/Canvas2d/babylon.text2d.ts
  85. 7 7
      src/Canvas2d/babylon.worldSpaceCanvas2d.js
  86. 1 1
      src/Canvas2d/babylon.worldSpaceCanvas2d.ts
  87. 1 1
      src/Collisions/babylon.collider.js
  88. 2 2
      src/Collisions/babylon.collisionCoordinator.js
  89. 3 3
      src/Collisions/babylon.collisionWorker.js
  90. 2 2
      src/Collisions/babylon.pickingInfo.js
  91. 1 1
      src/Culling/Octrees/babylon.octree.js
  92. 1 1
      src/Culling/Octrees/babylon.octreeBlock.js
  93. 1 1
      src/Culling/babylon.boundingBox.js
  94. 1 1
      src/Culling/babylon.boundingInfo.js
  95. 1 1
      src/Culling/babylon.boundingSphere.js
  96. 1 1
      src/Culling/babylon.ray.js
  97. 1 1
      src/Debug/babylon.debugLayer.js
  98. 1 1
      src/Debug/babylon.skeletonViewer.js
  99. 13 11
      src/Layer/babylon.layer.js
  100. 0 0
      src/Layer/babylon.layer.ts

+ 8 - 2
Exporters/Blender/io_export_babylon.py

@@ -1,7 +1,7 @@
 bl_info = {
     'name': 'Babylon.js',
     'author': 'David Catuhe, Jeff Palmer',
-    'version': (4, 5, 0),
+    'version': (4, 6, 0),
     'blender': (2, 75, 0),
     'location': 'File > Export > Babylon.js (.babylon)',
     'description': 'Export Babylon.js scenes (.babylon)',
@@ -304,6 +304,7 @@ class Main(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
 
             # separate loop doing all skeletons, so available in Mesh to make skipping IK bones possible
             for object in [object for object in scene.objects]:
+                scene.frame_set(currentFrame)
                 if object.type == 'ARMATURE':  #skeleton.pose.bones
                     if object.is_visible(scene):
                         self.skeletons.append(Skeleton(object, scene, skeletonId))
@@ -311,9 +312,9 @@ class Main(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
                     else:
                         Main.warn('The following armature not visible in scene thus ignored: ' + object.name)
 
-            bpy.context.scene.frame_set(0)
             # exclude lamps in this pass, so ShadowGenerator constructor can be passed meshesAnNodes
             for object in [object for object in scene.objects]:
+                scene.frame_set(currentFrame)
                 if object.type == 'CAMERA':
                     if object.is_visible(scene): # no isInSelectedLayer() required, is_visible() handles this for them
                         self.cameras.append(Camera(object))
@@ -354,6 +355,7 @@ class Main(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
 
             # Lamp / shadow Generator pass; meshesAnNodes complete & forceParents included
             for object in [object for object in scene.objects]:
+                scene.frame_set(currentFrame)
                 if object.type == 'LAMP':
                     if object.is_visible(scene): # no isInSelectedLayer() required, is_visible() handles this for them
                         bulb = Light(object)
@@ -1425,6 +1427,9 @@ class Skeleton:
         scene.objects.active = skeleton
         bpy.ops.object.mode_set(mode='EDIT')
 
+        # dimensions when in edit mode, are those at rest
+        self.dimensions = skeleton.dimensions
+
         # you need to access edit_bones from skeleton.data not skeleton.pose when in edit mode
         for editBone in skeleton.data.edit_bones:
             for myBoneObj in self.bones:
@@ -1447,6 +1452,7 @@ class Skeleton:
         file_handler.write('{')
         write_string(file_handler, 'name', self.name, True)
         write_int(file_handler, 'id', self.id)  # keep int for legacy of original exporter
+        write_vector(file_handler, 'dimensionsAtRest', self.dimensions)
 
         file_handler.write(',"bones":[')
         first = True

+ 2 - 0
Exporters/Unity 5/Unity3D2Babylon/ExportationOptions.cs

@@ -9,6 +9,7 @@ namespace Unity3D2Babylon
         public float ReflectionDefaultLevel { get; set; }
         public bool ExportCollisions { get; set; }
         public bool ExportPhysics { get; set; }
+        public bool ExportShadows { get; set; }
         public SerializableVector3 CameraEllipsoid { get; set; }
         public SerializableVector3 Gravity { get; set; }
 
@@ -18,6 +19,7 @@ namespace Unity3D2Babylon
             ReflectionDefaultLevel = 0.3f;
             ExportCollisions = false;
             ExportPhysics = false;
+            ExportShadows = false;
             CameraEllipsoid = new Vector3(0.5f, 1.0f, 0.5f);
             Gravity = new Vector3(0, -0.9f, 0);
         }

+ 1 - 1
Exporters/Unity 5/Unity3D2Babylon/ExporterWindow.cs

@@ -143,7 +143,7 @@ namespace Unity3D2Babylon
                 var outputFile = sceneBuilder.WriteToBabylonFile();
 
                 watch.Stop();
-                ReportProgress(1, $"Exportation done in {watch.Elapsed.TotalSeconds:0.00}s");
+                ReportProgress(1, string.Format("Exportation done in {0:0.00}s", watch.Elapsed.TotalSeconds));
                 EditorUtility.ClearProgressBar();
 
                 sceneBuilder.GenerateStatus(logs);

+ 27 - 23
Tools/Gulp/config.json

@@ -14,7 +14,7 @@
       "!../../src/**/*.d.ts",
       "../../external references/**/*.d.ts"
     ],
-   "files": [
+    "files": [
       "../../src/Math/babylon.math.js",
       "../../src/Tools/babylon.decorators.js",
       "../../src/Tools/babylon.observable.js",
@@ -43,18 +43,18 @@
       "../../src/Collisions/babylon.collider.js",
       "../../src/Collisions/babylon.collisionCoordinator.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.freeCamera.js",
       "../../src/Cameras/babylon.freeCameraInputsManager.js",
@@ -65,6 +65,7 @@
       "../../src/Rendering/babylon.renderingManager.js",
       "../../src/Rendering/babylon.renderingGroup.js",
       "../../src/babylon.scene.js",
+      "../../src/Mesh/babylon.buffer.js",
       "../../src/Mesh/babylon.vertexBuffer.js",
       "../../src/Mesh/babylon.instancedMesh.js",
       "../../src/Mesh/babylon.mesh.js",
@@ -166,10 +167,12 @@
       "../../src/Canvas2d/babylon.shape2d.js",
       "../../src/Canvas2d/babylon.group2d.js",
       "../../src/Canvas2d/babylon.rectangle2d.js",
+      "../../src/Canvas2d/babylon.ellipse2d.js",
       "../../src/Canvas2d/babylon.sprite2d.js",
       "../../src/Canvas2d/babylon.text2d.js",
+      "../../src/Canvas2d/babylon.lines2d.js",
       "../../src/Canvas2d/babylon.canvas2d.js",
-      "../../src/Canvas2d/babylon.worldspacecanvas2d.js",
+      "../../src/Canvas2d/babylon.worldSpaceCanvas2d.js",
       "../../src/Materials/babylon.shaderMaterial.js",
       "../../src/Tools/babylon.tools.dds.js",
       "../../src/Physics/Plugins/babylon.cannonJSPlugin.js",
@@ -181,11 +184,11 @@
       "../../src/PostProcess/babylon.vrDistortionCorrectionPostProcess.js",
       "../../src/Tools/babylon.virtualJoystick.js",
       "../../src/Cameras/babylon.virtualJoysticksCamera.js",      
-      "../../src/cameras/inputs/babylon.freecamera.input.virtualjoystick.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.virtualjoystick.js",
       "../../src/PostProcess/babylon.anaglyphPostProcess.js",
       "../../src/Rendering/babylon.outlineRenderer.js",
       "../../src/Tools/babylon.assetsManager.js",
-      "../../src/cameras/vr/babylon.vrCameraMetrics.js",
+      "../../src/Cameras/VR/babylon.vrCameraMetrics.js",
       "../../src/Cameras/VR/babylon.vrDeviceOrientationCamera.js",
       "../../src/Cameras/VR/babylon.webVRCamera.js",
       "../../src/Tools/babylon.sceneOptimizer.js",
@@ -220,14 +223,15 @@
       "../../src/Probes/babylon.reflectionProbe.js",
       "../../src/Particles/babylon.solidParticle.js",
       "../../src/Particles/babylon.solidParticleSystem.js",
-      "../../src/tools/hdr/babylon.tools.cubemaptosphericalpolynomial.js",
-      "../../src/tools/hdr/babylon.tools.panoramatocubemap.js",
-      "../../src/tools/hdr/babylon.tools.hdr.js",
-      "../../src/tools/hdr/babylon.tools.pmremGenerator.js",
-      "../../src/materials/textures/babylon.hdrcubetexture.js",
-      "../../src/debug/babylon.skeletonViewer.js",
+      "../../src/Tools/HDR/babylon.tools.cubemapToSphericalPolynomial.js",
+      "../../src/Tools/HDR/babylon.tools.panoramaToCubemap.js",
+      "../../src/Tools/HDR/babylon.tools.hdr.js",
+      "../../src/Tools/HDR/babylon.tools.pmremgenerator.js",
+      "../../src/Materials/Textures/babylon.hdrCubeTexture.js",
+      "../../src/Debug/babylon.skeletonViewer.js",
       "../../src/Materials/Textures/babylon.colorGradingTexture.js",
-      "../../src/materials/babylon.pbrmaterial.js",      
+      "../../src/Materials/babylon.colorCurves.js",
+      "../../src/Materials/babylon.pbrMaterial.js",      
       "../../src/Debug/babylon.debugLayer.js"
     ]
   }

+ 21 - 10
Tools/Gulp/gulpfile.js

@@ -12,6 +12,7 @@ var changed = require('gulp-changed');
 var runSequence = require('run-sequence');
 var replace = require("gulp-replace");
 var uncommentShader = require("./gulp-removeShaderComments");
+var expect = require('gulp-expect-file');
 
 var config = require("./config.json");
 
@@ -36,6 +37,7 @@ function includeShadersName(filename) {
 gulp.task("includeShaders", function (cb) {
     includeShadersStream = config.includeShadersDirectories.map(function (shadersDef) {
         return gulp.src(shadersDef.files).
+            pipe(expect.real({ errorOnFailure: true }, shadersDef.files)).
             pipe(uncommentShader()).
             pipe(srcToVariable({
             variableName: shadersDef.variable, asMap: true, namingCallback: includeShadersName
@@ -47,6 +49,7 @@ gulp.task("includeShaders", function (cb) {
 gulp.task("shaders", ["includeShaders"], function (cb) {
     shadersStream = config.shadersDirectories.map(function (shadersDef) {
         return gulp.src(shadersDef.files).
+            pipe(expect.real({ errorOnFailure: true }, shadersDef.files)).
             pipe(uncommentShader()).
             pipe(srcToVariable({
             variableName: shadersDef.variable, asMap: true, namingCallback: shadersName
@@ -57,9 +60,12 @@ gulp.task("shaders", ["includeShaders"], function (cb) {
 
 gulp.task("workers", function (cb) {
     workersStream = config.workers.map(function (workerDef) {
-        return gulp.src(workerDef.files).pipe(uglify()).pipe(srcToVariable({
-            variableName: workerDef.variable
-        }));
+        return gulp.src(workerDef.files).
+            pipe(expect.real({ errorOnFailure: true }, workerDef.files)).
+            pipe(uglify()).
+            pipe(srcToVariable({
+                variableName: workerDef.variable
+            }));
     });
     cb();
 });
@@ -68,8 +74,8 @@ gulp.task("workers", function (cb) {
 Compiles all typescript files and creating a declaration file.
 */
 gulp.task('typescript-compile', function () {
-    var tsResult = gulp.src(config.core.typescript)
-        .pipe(typescript({
+    var tsResult = gulp.src(config.core.typescript).
+        pipe(typescript({
             noExternalResolve: true,
             target: 'ES5',
             declarationFiles: true,
@@ -114,7 +120,8 @@ gulp.task('typescript-sourcemaps', function () {
 
 gulp.task("buildCore", ["shaders"], function () {
     return merge2(
-        gulp.src(config.core.files),
+        gulp.src(config.core.files).        
+            pipe(expect.real({ errorOnFailure: true }, config.core.files)),
         shadersStream,
         includeShadersStream
         )
@@ -129,8 +136,10 @@ gulp.task("buildCore", ["shaders"], function () {
 
 gulp.task("buildNoWorker", ["shaders"], function () {
     return merge2(
-        gulp.src(config.core.files),
-        gulp.src(config.extras.files),
+        gulp.src(config.core.files).        
+            pipe(expect.real({ errorOnFailure: true }, config.core.files)),
+        gulp.src(config.extras.files).        
+            pipe(expect.real({ errorOnFailure: true }, config.extras.files)),
         shadersStream,
         includeShadersStream
         )
@@ -145,8 +154,10 @@ gulp.task("buildNoWorker", ["shaders"], function () {
 
 gulp.task("build", ["workers", "shaders"], function () {
     return merge2(
-        gulp.src(config.core.files),
-        gulp.src(config.extras.files),
+        gulp.src(config.core.files).        
+            pipe(expect.real({ errorOnFailure: true }, config.core.files)),
+        gulp.src(config.extras.files).        
+            pipe(expect.real({ errorOnFailure: true }, config.extras.files)),   
         shadersStream,
         includeShadersStream,
         workersStream

+ 2 - 1
Tools/Gulp/package.json

@@ -21,6 +21,7 @@
     "gulp-changed": "~1.2.1",
     "run-sequence": "~1.1.0",
     "gulp-replace": "~0.5.3",
-    "gulp-content-to-variable": "^0.1.0"
+    "gulp-content-to-variable": "^0.1.0",
+    "gulp-expect-file": "^0.0.7"
   }
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 25 - 25
dist/preview release/babylon.core.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 6315 - 5696
dist/preview release/babylon.d.ts


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 38 - 37
dist/preview release/babylon.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3204 - 847
dist/preview release/babylon.max.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 38 - 37
dist/preview release/babylon.noworker.js


+ 26 - 8
dist/preview release/what's new.md

@@ -1,23 +1,27 @@
 - 2.4.0:
   - **Major updates**    
     - New refraction channel for Standard material (including fresnel support). Refraction texture can be provided by a reflection probe or a refraction texture. [See demo here](http://www.babylonjs.com/Demos/refraction/) ([deltakosh](https://github.com/deltakosh))
-    - Added support for HDR cubemaps ([sebavan](https://github.com/sebavan))
+    - Added support for HDR cubemaps. [demo here](http://www.babylonjs-playground.com/#19JGPR#4) ([sebavan](https://github.com/sebavan))
     - Support for shaders includes ([deltakosh](https://github.com/deltakosh))
-    - New mesh type : `LineSystem` ([jerome](https://github.com/jbousquie))
+    - New mesh type : `LineSystem`. [Demo here](http://www.babylonjs-playground.com/#2K1IS4#5) ([jerome](https://github.com/jbousquie))
     - SerializationHelper for complex classes using TypeScript decorators ([deltakosh](https://github.com/deltakosh))
     - 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))
     - 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))
-    - Introduced new observable system to handle events ([nockawa](https://github.com/nockawa), [deltakosh](https://github.com/deltakosh))
-    - Added a new VR camera : VRDeviceOrientationArcRotateCamera ([temechon](https://github.com/Temechon)
+    - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance. [Documentation here](http://doc.babylonjs.com/tutorials/Customizing_Camera_Inputs) ([gleborgne](https://github.com/gleborgne))
+    - Introduced new observable system to handle events. [Documentation here](http://doc.babylonjs.com/overviews/Observables) ([nockawa](https://github.com/nockawa), [deltakosh](https://github.com/deltakosh))
+    - Added a new VR camera : VRDeviceOrientationArcRotateCamera ([temechon](https://github.com/Temechon))
     - 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. [Demo here](http://www.babylonjs-playground.com/#IRVAX#10) ([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))	
+    - Physics engine was completely rewritten, including both plugins for Oimo.js and Cannon.js. [overview](http://doc.babylonjs.com/overviews/Using_The_Physics_Engine) ([RaananW](https://github.com/RaananW))
+	- Interleaved buffers are now directly supported. Create a `Buffer` object and then use `buffer.createVertexBuffer` to specify the vertex buffers ([benaadams](https://github.com/benaadams)) 
+	- Vertex buffers can be marked as instanced to allow custom instancing attributes ([benaadams](https://github.com/benaadams)) 
+	- Mesh can have `overridenInstanceCount` set to specify the number of meshes to draw when custom instancing is used ([benaadams](https://github.com/benaadams)) 
   - **Updates**
+    - Added `mesh.toLefthanded()` to convert a mesh from right handed system ([kesshi](https://github.com/Kesshi))
     - 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))
@@ -38,10 +42,17 @@
     - 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))
     - 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. ([quick dock](http://www.html5gamedevs.com/topic/22566-be-efficient-my-friend-use-stringdictionary/)) ([nockawa](https://github.com/nockawa))
+    - Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([quick doc](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))
+    - Oimo.js updated to the latest version ([RaananW](https://github.com/RaananW))
+    - Added PhysicsImpostor and PhysicsJoint classes  ([RaananW](https://github.com/RaananW))
+    - LensFlareSystem now has both ID and name  ([RaananW](https://github.com/RaananW))
+    - TargetCamera has now a rotationQuaternion variable to can be used to set the camera's rotation  ([RaananW](https://github.com/RaananW))
+    - SSAORenderingPipeline now uses bilateral blur post-processes instead of standard blur post-process, in order to remove more efficiently the "textile effect"
+    - `Engine.updateDynamicVertexBuffer` now has optional count as well as offset to allow partial updates ([benaadams](https://github.com/benaadams)) 
+    - vertex attributes are only disabled if they aren't going to be reeabled by the next draw, to reduce gpu state changes ([benaadams](https://github.com/benaadams)) 
   - **Exporters**
     - Unity3D exporter: Added support for lightmaps ([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))
@@ -57,7 +68,14 @@
     - Fixed bug with billboards and parenting ([deltakosh](https://github.com/deltakosh))
     - Fixed bug with ArcRotateCamera.setTarget ([deltakosh](https://github.com/deltakosh))
     - Fixed bug with OBJ Loader - All meshes were concatenated with the previous one ([Temechon](https://github.com/Temechon))
+    - Fixed the device orientation cameras (both VR and non-VR cameras)  ([RaananW](https://github.com/RaananW))
+    - Fixed the WebVR implementation  ([RaananW](https://github.com/RaananW))
   - **Breaking changes**
     - `VertexData.CreateLines()` removed as `MeshBuilder.CreateLines()` now calls `MeshBuilder.CreateLineSystem()`
     - `scene.onNewXXXAdded` and `scene.onXXXRemoved` callbacks were removed and replaced by `scene.onNewXXXAddedObservable` and `scene.onXXXRemovedObservable`
     - `Material.dispose` does not dispose textures by default. You have to call `material.dispose(false, true)` to get the previous behavior.
+    - `SSAORenderingPipeline.getBlurHPostProcess` and `SSAORenderingPipeline.getBlurVPostProcess`. The SSAO rendering pipeline doesn't use standard blur post-process anymore. A bilateral blur post-process is used instead.
+    - `Engine.bindBuffers` is now `Engine.bindBuffersDirectly` ([benaadams](https://github.com/benaadams))
+    - `Engine.bindMultiBuffers` is now `Engine.bindBuffers` and strongly typed `{ [key: string]: VertexBuffer; }` of buffers ([benaadams](https://github.com/benaadams))
+    - `Engine.createDynamicVertexBuffer` takes vertices rather than capacity, creating and initalizing in one gpu instruction ([benaadams](https://github.com/benaadams)) 
+    - Internally new `Engine.bindBuffer` is used rather than `gl.bindBuffer` which only binds when the bound buffer is changing ([benaadams](https://github.com/benaadams)) 

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

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

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

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

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

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

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

@@ -29,7 +29,7 @@ var BABYLON;
             }, parent);
         };
         return SwitchBooleanAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.SwitchBooleanAction = SwitchBooleanAction;
     var SetStateAction = (function (_super) {
         __extends(SetStateAction, _super);
@@ -51,7 +51,7 @@ var BABYLON;
             }, parent);
         };
         return SetStateAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.SetStateAction = SetStateAction;
     var SetValueAction = (function (_super) {
         __extends(SetValueAction, _super);
@@ -82,7 +82,7 @@ var BABYLON;
             }, parent);
         };
         return SetValueAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.SetValueAction = SetValueAction;
     var IncrementValueAction = (function (_super) {
         __extends(IncrementValueAction, _super);
@@ -116,7 +116,7 @@ var BABYLON;
             }, parent);
         };
         return IncrementValueAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.IncrementValueAction = IncrementValueAction;
     var PlayAnimationAction = (function (_super) {
         __extends(PlayAnimationAction, _super);
@@ -145,7 +145,7 @@ var BABYLON;
             }, parent);
         };
         return PlayAnimationAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.PlayAnimationAction = PlayAnimationAction;
     var StopAnimationAction = (function (_super) {
         __extends(StopAnimationAction, _super);
@@ -166,7 +166,7 @@ var BABYLON;
             }, parent);
         };
         return StopAnimationAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.StopAnimationAction = StopAnimationAction;
     var DoNothingAction = (function (_super) {
         __extends(DoNothingAction, _super);
@@ -183,7 +183,7 @@ var BABYLON;
             }, parent);
         };
         return DoNothingAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.DoNothingAction = DoNothingAction;
     var CombineAction = (function (_super) {
         __extends(CombineAction, _super);
@@ -214,7 +214,7 @@ var BABYLON;
             return serializationObject;
         };
         return CombineAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.CombineAction = CombineAction;
     var ExecuteCodeAction = (function (_super) {
         __extends(ExecuteCodeAction, _super);
@@ -226,7 +226,7 @@ var BABYLON;
             this.func(evt);
         };
         return ExecuteCodeAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.ExecuteCodeAction = ExecuteCodeAction;
     var SetParentAction = (function (_super) {
         __extends(SetParentAction, _super);
@@ -256,7 +256,7 @@ var BABYLON;
             }, parent);
         };
         return SetParentAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.SetParentAction = SetParentAction;
     var PlaySoundAction = (function (_super) {
         __extends(PlaySoundAction, _super);
@@ -277,7 +277,7 @@ var BABYLON;
             }, parent);
         };
         return PlaySoundAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.PlaySoundAction = PlaySoundAction;
     var StopSoundAction = (function (_super) {
         __extends(StopSoundAction, _super);
@@ -298,6 +298,6 @@ var BABYLON;
             }, parent);
         };
         return StopSoundAction;
-    })(BABYLON.Action);
+    }(BABYLON.Action));
     BABYLON.StopSoundAction = StopSoundAction;
 })(BABYLON || (BABYLON = {}));

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

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

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

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

+ 4 - 4
src/Animations/babylon.animation.js

@@ -10,7 +10,7 @@ var BABYLON;
             return new AnimationRange(this.name, this.from, this.to);
         };
         return AnimationRange;
-    })();
+    }());
     BABYLON.AnimationRange = AnimationRange;
     /**
      * Composed of a frame, and an action function
@@ -23,7 +23,7 @@ var BABYLON;
             this.isDone = false;
         }
         return AnimationEvent;
-    })();
+    }());
     BABYLON.AnimationEvent = AnimationEvent;
     var PathCursor = (function () {
         function PathCursor(path) {
@@ -80,7 +80,7 @@ var BABYLON;
             return this;
         };
         return PathCursor;
-    })();
+    }());
     BABYLON.PathCursor = PathCursor;
     var Animation = (function () {
         function Animation(name, targetProperty, framePerSecond, dataType, loopMode, enableBlending) {
@@ -736,6 +736,6 @@ var BABYLON;
         Animation._ANIMATIONLOOPMODE_CYCLE = 1;
         Animation._ANIMATIONLOOPMODE_CONSTANT = 2;
         return Animation;
-    })();
+    }());
     BABYLON.Animation = Animation;
 })(BABYLON || (BABYLON = {}));

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -272,6 +272,7 @@ var BABYLON;
             var serializationObject = {};
             serializationObject.name = this.name;
             serializationObject.id = this.id;
+            serializationObject.dimensionsAtRest = this.dimensionsAtRest;
             serializationObject.bones = [];
             serializationObject.needInitialSkinMatrix = this.needInitialSkinMatrix;
             for (var index = 0; index < this.bones.length; index++) {
@@ -302,6 +303,9 @@ var BABYLON;
         };
         Skeleton.Parse = function (parsedSkeleton, scene) {
             var skeleton = new Skeleton(parsedSkeleton.name, parsedSkeleton.id, scene);
+            if (parsedSkeleton.dimensionsAtRest) {
+                skeleton.dimensionsAtRest = BABYLON.Vector3.FromArray(parsedSkeleton.dimensionsAtRest);
+            }
             skeleton.needInitialSkinMatrix = parsedSkeleton.needInitialSkinMatrix;
             var index;
             for (index = 0; index < parsedSkeleton.bones.length; index++) {
@@ -329,6 +333,6 @@ var BABYLON;
             return skeleton;
         };
         return Skeleton;
-    })();
+    }());
     BABYLON.Skeleton = Skeleton;
 })(BABYLON || (BABYLON = {}));

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

@@ -1,7 +1,7 @@
 module BABYLON {
     export class Skeleton {
         public bones = new Array<Bone>();
-
+        public dimensionsAtRest: Vector3;
         public needInitialSkinMatrix = false;
 
         private _scene: Scene;
@@ -41,23 +41,23 @@
         /**
          * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
          */
-        public toString(fullDetails? : boolean) : string {
+        public toString(fullDetails?: boolean): string {
             var ret = `Name: ${this.name}, nBones: ${this.bones.length}`;
             ret += `, nAnimationRanges: ${this._ranges ? Object.keys(this._ranges).length : "none"}`;
             if (fullDetails) {
-                ret += ", Ranges: {"; 
+                ret += ", Ranges: {";
                 let first = true;
                 for (let name in this._ranges) {
                     if (first) {
                         ret += ", ";
-                        first = false; 
+                        first = false;
                     }
-                    ret += name; 
+                    ret += name;
                 }
                 ret += "}";
             }
             return ret;
-        } 
+        }
 
         /**
         * Get bone's index searching by name
@@ -72,7 +72,7 @@
             }
             return -1;
         }
-        
+
         public createAnimationRange(name: string, from: number, to: number): void {
             // check name not already in use
             if (!this._ranges[name]) {
@@ -97,15 +97,15 @@
         public getAnimationRange(name: string): AnimationRange {
             return this._ranges[name];
         }
-        
+
         /**
          *  Returns as an Array, all AnimationRanges defined on this skeleton
          */
         public getAnimationRanges(): AnimationRange[] {
-            var animationRanges :  AnimationRange[] = [];
-            var name : string;
+            var animationRanges: AnimationRange[] = [];
+            var name: string;
             var i: number = 0;
-            for (name in this._ranges){
+            for (name in this._ranges) {
                 animationRanges[i] = this._ranges[name];
                 i++;
             }
@@ -121,7 +121,7 @@
             }
             var ret = true;
             var frameOffset = this._getHighestAnimationFrame() + 1;
-            
+
             // make a dictionary of source skeleton's bones, so exact same order or doublely nested loop is not required
             var boneDict = {};
             var sourceBones = source.bones;
@@ -131,11 +131,11 @@
                 boneDict[sourceBones[i].name] = sourceBones[i];
             }
 
-            if (this.bones.length !== sourceBones.length){
+            if (this.bones.length !== sourceBones.length) {
                 Tools.Warn(`copyAnimationRange: this rig has ${this.bones.length} bones, while source as ${sourceBones.length}`);
                 ret = false;
             }
-            
+
             for (i = 0, nBones = this.bones.length; i < nBones; i++) {
                 var boneName = this.bones[i].name;
                 var sourceBone = boneDict[boneName];
@@ -325,6 +325,7 @@
 
             serializationObject.name = this.name;
             serializationObject.id = this.id;
+            serializationObject.dimensionsAtRest = this.dimensionsAtRest;
 
             serializationObject.bones = [];
 
@@ -364,6 +365,9 @@
 
         public static Parse(parsedSkeleton: any, scene: Scene): Skeleton {
             var skeleton = new Skeleton(parsedSkeleton.name, parsedSkeleton.id, scene);
+            if (parsedSkeleton.dimensionsAtRest) {
+                skeleton.dimensionsAtRest = Vector3.FromArray(parsedSkeleton.dimensionsAtRest);
+            }
 
             skeleton.needInitialSkinMatrix = parsedSkeleton.needInitialSkinMatrix;
 
@@ -386,7 +390,7 @@
                     bone.animations.push(Animation.Parse(parsedBone.animation));
                 }
             }
-            
+
             // placed after bones, so createAnimationRange can cascade down
             if (parsedSkeleton.ranges) {
                 for (index = 0; index < parsedSkeleton.ranges.length; index++) {
@@ -397,4 +401,4 @@
             return skeleton;
         }
     }
-}
+}

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

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

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

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

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

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

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

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

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

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

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

@@ -56,7 +56,7 @@ var BABYLON;
             return "deviceOrientation";
         };
         return FreeCameraDeviceOrientationInput;
-    })();
+    }());
     BABYLON.FreeCameraDeviceOrientationInput = FreeCameraDeviceOrientationInput;
     BABYLON.CameraInputTypes["FreeCameraDeviceOrientationInput"] = FreeCameraDeviceOrientationInput;
 })(BABYLON || (BABYLON = {}));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 2 - 2
src/Cameras/VR/babylon.vrDeviceOrientationCamera.js

@@ -20,7 +20,7 @@ var BABYLON;
             return "VRDeviceOrientationFreeCamera";
         };
         return VRDeviceOrientationFreeCamera;
-    })(BABYLON.FreeCamera);
+    }(BABYLON.FreeCamera));
     BABYLON.VRDeviceOrientationFreeCamera = VRDeviceOrientationFreeCamera;
     var VRDeviceOrientationArcRotateCamera = (function (_super) {
         __extends(VRDeviceOrientationArcRotateCamera, _super);
@@ -36,6 +36,6 @@ var BABYLON;
             return "VRDeviceOrientationArcRotateCamera";
         };
         return VRDeviceOrientationArcRotateCamera;
-    })(BABYLON.ArcRotateCamera);
+    }(BABYLON.ArcRotateCamera));
     BABYLON.VRDeviceOrientationArcRotateCamera = VRDeviceOrientationArcRotateCamera;
 })(BABYLON || (BABYLON = {}));

+ 11 - 8
src/Cameras/VR/babylon.webVRCamera.js

@@ -13,13 +13,12 @@ var BABYLON;
             this._hmdDevice = null;
             this._sensorDevice = null;
             this._cacheState = null;
-            this._cacheQuaternion = new BABYLON.Quaternion();
-            this._cacheRotation = BABYLON.Vector3.Zero();
             this._vrEnabled = false;
             var metrics = BABYLON.VRCameraMetrics.GetDefault();
             metrics.compensateDistortion = compensateDistortion;
             this.setCameraRigMode(BABYLON.Camera.RIG_MODE_VR, { vrCameraMetrics: metrics });
             this._getWebVRDevices = this._getWebVRDevices.bind(this);
+            this.rotationQuaternion = new BABYLON.Quaternion();
         }
         WebVRFreeCamera.prototype._getWebVRDevices = function (devices) {
             var size = devices.length;
@@ -46,11 +45,10 @@ var BABYLON;
         WebVRFreeCamera.prototype._checkInputs = function () {
             if (this._vrEnabled) {
                 this._cacheState = this._sensorDevice.getState();
-                this._cacheQuaternion.copyFromFloats(this._cacheState.orientation.x, this._cacheState.orientation.y, this._cacheState.orientation.z, this._cacheState.orientation.w);
-                this._cacheQuaternion.toEulerAnglesToRef(this._cacheRotation);
-                this.rotation.x = -this._cacheRotation.x;
-                this.rotation.y = -this._cacheRotation.y;
-                this.rotation.z = this._cacheRotation.z;
+                this.rotationQuaternion.copyFrom(this._cacheState.orientation);
+                //Flip in XY plane
+                this.rotationQuaternion.z *= -1;
+                this.rotationQuaternion.w *= -1;
             }
             _super.prototype._checkInputs.call(this);
         };
@@ -68,10 +66,15 @@ var BABYLON;
             _super.prototype.detachControl.call(this, element);
             this._vrEnabled = false;
         };
+        WebVRFreeCamera.prototype.requestVRFullscreen = function (requestPointerlock) {
+            if (!this._hmdDevice)
+                return;
+            this.getEngine().switchFullscreen(requestPointerlock, { vrDisplay: this._hmdDevice });
+        };
         WebVRFreeCamera.prototype.getTypeName = function () {
             return "WebVRFreeCamera";
         };
         return WebVRFreeCamera;
-    })(BABYLON.FreeCamera);
+    }(BABYLON.FreeCamera));
     BABYLON.WebVRFreeCamera = WebVRFreeCamera;
 })(BABYLON || (BABYLON = {}));

+ 12 - 9
src/Cameras/VR/babylon.webVRCamera.ts

@@ -5,9 +5,7 @@ module BABYLON {
     export class WebVRFreeCamera extends FreeCamera {
         public _hmdDevice = null;
         public _sensorDevice = null;
-        public _cacheState = null;
-        public _cacheQuaternion = new Quaternion();
-        public _cacheRotation = Vector3.Zero();
+        private _cacheState = null;
         public _vrEnabled = false;
 
         constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true) {
@@ -18,6 +16,8 @@ module BABYLON {
             this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: metrics });
 
             this._getWebVRDevices = this._getWebVRDevices.bind(this);
+
+            this.rotationQuaternion = new Quaternion();
         }
 
         private _getWebVRDevices(devices: Array<any>): void {
@@ -51,12 +51,10 @@ module BABYLON {
         public _checkInputs(): void {
             if (this._vrEnabled) {
                 this._cacheState = this._sensorDevice.getState();
-                this._cacheQuaternion.copyFromFloats(this._cacheState.orientation.x, this._cacheState.orientation.y, this._cacheState.orientation.z, this._cacheState.orientation.w);
-                this._cacheQuaternion.toEulerAnglesToRef(this._cacheRotation);
-
-                this.rotation.x = -this._cacheRotation.x;
-                this.rotation.y = -this._cacheRotation.y;
-                this.rotation.z = this._cacheRotation.z;
+                this.rotationQuaternion.copyFrom(this._cacheState.orientation);
+                //Flip in XY plane
+                this.rotationQuaternion.z *= -1;
+                this.rotationQuaternion.w *= -1;
             }
 
             super._checkInputs();
@@ -80,6 +78,11 @@ module BABYLON {
             this._vrEnabled = false;
         }
 
+        public requestVRFullscreen(requestPointerlock: boolean) {
+            if (!this._hmdDevice) return;
+            this.getEngine().switchFullscreen(requestPointerlock, { vrDisplay: this._hmdDevice })
+        }
+
         public getTypeName(): string {
             return "WebVRFreeCamera";
         }

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

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

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

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

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

@@ -247,6 +247,9 @@ var BABYLON;
                     cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
                     rigPostProcess.markTextureDirty();
                 }
+                else {
+                    cam._postProcesses = this._postProcesses.slice(0);
+                }
             }
         };
         Camera.prototype.attachPostProcess = function (postProcess, insertAt) {
@@ -612,6 +615,6 @@ var BABYLON;
             BABYLON.serialize()
         ], Camera.prototype, "isStereoscopicSideBySide", void 0);
         return Camera;
-    })(BABYLON.Node);
+    }(BABYLON.Node));
     BABYLON.Camera = Camera;
 })(BABYLON || (BABYLON = {}));

+ 37 - 38
src/Cameras/babylon.camera.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class Camera extends Node {
         public inputs: CameraInputsManager<Camera>;
-        
+
         // Statics
         private static _PERSPECTIVE_CAMERA = 0;
         private static _ORTHOGRAPHIC_CAMERA = 1;
@@ -57,7 +57,7 @@
         }
 
         public static ForceAttachControlToAlwaysPreventDefault = false;
-        
+
         // Members
         @serializeAsVector3()
         public position: Vector3;
@@ -100,7 +100,7 @@
 
         @serialize()
         public fovMode: number = Camera.FOVMODE_VERTICAL_FIXED;
-   
+
         // Camera rig members
         @serialize()
         public cameraRigMode = Camera.RIG_MODE_NONE;
@@ -113,7 +113,7 @@
 
         public _cameraRigParams: any;
         public _rigCameras = new Array<Camera>();
-        public _rigPostProcess : PostProcess;
+        public _rigPostProcess: PostProcess;
 
         // Cache
         private _computedViewMatrix = Matrix.Identity();
@@ -274,27 +274,30 @@
 
         public _checkInputs(): void {
         }
-        
-        private _cascadePostProcessesToRigCams() : void {
+
+        private _cascadePostProcessesToRigCams(): void {
             // invalidate framebuffer
-            if (this._postProcesses.length > 0){
+            if (this._postProcesses.length > 0) {
                 this._postProcesses[0].markTextureDirty();
             }
-            
+
             // glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
-            for(var i = 0, len = this._rigCameras.length; i < len; i++){
+            for (var i = 0, len = this._rigCameras.length; i < len; i++) {
                 var cam = this._rigCameras[i];
                 var rigPostProcess = cam._rigPostProcess;
-                
+
                 // for VR rig, there does not have to be a post process 
-                if (rigPostProcess){
+                if (rigPostProcess) {
                     var isPass = rigPostProcess instanceof PassPostProcess;
-                    if (isPass){
+                    if (isPass) {
                         // any rig which has a PassPostProcess for rig[0], cannot be isIntermediate when there are also user postProcesses
                         cam.isIntermediate = this._postProcesses.length === 0;
-                    }               
+                    }
                     cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
                     rigPostProcess.markTextureDirty();
+
+                } else {
+                    cam._postProcesses = this._postProcesses.slice(0);
                 }
             }
         }
@@ -304,17 +307,17 @@
                 Tools.Error("You're trying to reuse a post process not defined as reusable.");
                 return 0;
             }
-            
+
             if (insertAt == null || insertAt < 0) {
                 this._postProcesses.push(postProcess);
-                
-            }else{
+
+            } else {
                 this._postProcesses.splice(insertAt, 0, postProcess);
             }
             this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated            
             return this._postProcesses.indexOf(postProcess);
         }
-        
+
         public detachPostProcess(postProcess: PostProcess, atIndices: any = null): number[] {
             var result = [];
             var i: number;
@@ -322,14 +325,14 @@
 
             if (!atIndices) {
                 var idx = this._postProcesses.indexOf(postProcess);
-                if (idx !== -1){
+                if (idx !== -1) {
                     this._postProcesses.splice(idx, 1);
                 }
             } else {
                 atIndices = (atIndices instanceof Array) ? atIndices : [atIndices];
                 // iterate descending, so can just splice as we go
                 for (i = atIndices.length - 1; i >= 0; i--) {
-                    if ( this._postProcesses[atIndices[i]] !== postProcess) {
+                    if (this._postProcesses[atIndices[i]] !== postProcess) {
                         result.push(i);
                         continue;
                     }
@@ -339,7 +342,7 @@
             this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
             return result;
         }
-        
+
         public getWorldMatrix(): Matrix {
             if (!this._worldMatrix) {
                 this._worldMatrix = Matrix.Identity();
@@ -434,7 +437,7 @@
 
             super.dispose();
         }
-        
+
         // ---- Camera rigs section ----
         public setCameraRigMode(mode: number, rigParams: any): void {
             while (this._rigCameras.length > 0) {
@@ -448,7 +451,7 @@
             this._cameraRigParams.stereoHalfAngle = BABYLON.Tools.ToRadians(this._cameraRigParams.interaxialDistance / 0.0637);
 
             // create the rig cameras, unless none
-            if (this.cameraRigMode !== Camera.RIG_MODE_NONE){
+            if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
                 this._rigCameras.push(this.createRigCamera(this.name + "_L", 0));
                 this._rigCameras.push(this.createRigCamera(this.name + "_R", 1));
             }
@@ -463,14 +466,14 @@
                 case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
                 case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
                     var isStereoscopicHoriz = this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL || this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;
-                    
+
                     this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
                     this._rigCameras[1]._rigPostProcess = new StereoscopicInterlacePostProcess(this.name + "_stereoInterlace", this._rigCameras, isStereoscopicHoriz);
                     break;
 
                 case Camera.RIG_MODE_VR:
                     var metrics = rigParams.vrCameraMetrics || VRCameraMetrics.GetDefault();
-                    
+
                     this._rigCameras[0]._cameraRigParams.vrMetrics = metrics;
                     this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
                     this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
@@ -492,7 +495,7 @@
                     break;
             }
 
-            this._cascadePostProcessesToRigCams(); 
+            this._cascadePostProcessesToRigCams();
             this._update();
         }
 
@@ -503,8 +506,8 @@
         }
 
         public setCameraRigParameter(name: string, value: any) {
-            if (!this._cameraRigParams){
-               this._cameraRigParams = {}; 
+            if (!this._cameraRigParams) {
+                this._cameraRigParams = {};
             }
             this._cameraRigParams[name] = value;
             //provisionnally:
@@ -512,14 +515,14 @@
                 this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(value / 0.0637);
             }
         }
-        
+
         /**
          * needs to be overridden by children so sub has required properties to be copied
          */
         public createRigCamera(name: string, cameraIndex: number): Camera {
-           return null;
+            return null;
         }
-        
+
         /**
          * May need to be overridden by children
          */
@@ -529,7 +532,7 @@
                 this._rigCameras[i].maxZ = this.maxZ;
                 this._rigCameras[i].fov = this.fov;
             }
-            
+
             // only update viewport when ANAGLYPH
             if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
                 this._rigCameras[0].viewport = this._rigCameras[1].viewport = this.viewport;
@@ -621,14 +624,14 @@
             if (parsedCamera.parentId) {
                 camera._waitingParentId = parsedCamera.parentId;
             }
-            
+
             //If camera has an input manager, let it parse inputs settings
             if (camera.inputs) {
                 camera.inputs.parse(parsedCamera);
 
                 camera._setupInputs();
             }
-            
+
             // Target
             if (parsedCamera.target) {
                 if ((<any>camera).setTarget) {
@@ -659,8 +662,4 @@
             return camera;
         }
     }
-}
-
-
-
-
+}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -269,6 +269,6 @@ var BABYLON;
             BABYLON.serializeAsMeshReference("lockedTargetId")
         ], TargetCamera.prototype, "lockedTarget", void 0);
         return TargetCamera;
-    })(BABYLON.Camera);
+    }(BABYLON.Camera));
     BABYLON.TargetCamera = TargetCamera;
 })(BABYLON || (BABYLON = {}));

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

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

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

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

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

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

+ 3 - 3
src/Canvas2d/babylon.bounding2d.js

@@ -45,8 +45,8 @@ var BABYLON;
         };
         BoundingInfo2D.CreateFromPointsToRef = function (points, b, origin) {
             var xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
-            for (var _i = 0; _i < points.length; _i++) {
-                var p = points[_i];
+            for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
+                var p = points_1[_i];
                 xmin = Math.min(p.x, xmin);
                 xmax = Math.max(p.x, xmax);
                 ymin = Math.min(p.y, ymin);
@@ -147,6 +147,6 @@ var BABYLON;
             return false;
         };
         return BoundingInfo2D;
-    })();
+    }());
     BABYLON.BoundingInfo2D = BoundingInfo2D;
 })(BABYLON || (BABYLON = {}));

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

@@ -35,7 +35,7 @@ var BABYLON;
         LockableBase.prototype.onLock = function () {
         };
         return LockableBase;
-    })();
+    }());
     BABYLON.LockableBase = LockableBase;
     /**
      * 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).
@@ -82,7 +82,7 @@ var BABYLON;
             BABYLON.className("SolidColorBrush2D")
         ], SolidColorBrush2D);
         return SolidColorBrush2D;
-    })(LockableBase);
+    }(LockableBase));
     BABYLON.SolidColorBrush2D = SolidColorBrush2D;
     var GradientColorBrush2D = (function (_super) {
         __extends(GradientColorBrush2D, _super);
@@ -179,6 +179,6 @@ var BABYLON;
             BABYLON.className("GradientColorBrush2D")
         ], GradientColorBrush2D);
         return GradientColorBrush2D;
-    })(LockableBase);
+    }(LockableBase));
     BABYLON.GradientColorBrush2D = GradientColorBrush2D;
 })(BABYLON || (BABYLON = {}));

+ 42 - 37
src/Canvas2d/babylon.canvas2d.js

@@ -26,7 +26,7 @@ var BABYLON;
             return true;
         };
         return Canvas2DEngineBoundData;
-    })();
+    }());
     BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
     var Canvas2D = (function (_super) {
         __extends(Canvas2D, _super);
@@ -36,64 +36,62 @@ var BABYLON;
             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 bottom/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.
          * 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 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 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.
+         * Options:
+         *  - id: a text identifier, for information purpose only
+         *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport
+         *  - 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
+         *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property.
          */
-        Canvas2D.CreateScreenSpace = function (scene, name, pos, size, cachingStrategy, enableInteraction) {
-            if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
-            if (enableInteraction === void 0) { enableInteraction = true; }
+        Canvas2D.CreateScreenSpace = function (scene, options) {
             var c = new Canvas2D();
-            c.setupCanvas(scene, name, size, true, cachingStrategy, enableInteraction);
-            c.position = pos;
-            c.origin = BABYLON.Vector2.Zero();
+            c.setupCanvas(scene, options && options.id || null, options && options.size || null, true, options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, options && options.enableInteraction || true);
+            c.position = options && options.pos || BABYLON.Vector2.Zero();
+            c.origin = options && options.origin || BABYLON.Vector2.Zero();
             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.
          * 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.
          * @param scene the Scene that owns the Canvas
-         * @param name the name of the Canvas, for information purpose only
-         * @param position the position of the Canvas in World Space
-         * @param rotation the rotation of the Canvas in World Space
          * @param size the dimension of the Canvas in World Space
-         * @param renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality.
+         * Options:
+         *  - id: a text identifier, for information purpose only, default is null.
+         *  - position the position of the Canvas in World Space, default is [0,0,0]
+         *  - rotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         *  - renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality. Default value is 1.
          * BE AWARE that the Canvas true dimension will be size*renderScaleFactor, then all coordinates and size will have to be express regarding this size.
          * TIPS: if you want a renderScaleFactor independent reference of frame, create a child Group2D in the Canvas with position 0,0 and size set to null, then set its scale property to the same amount than the renderScaleFactor, put all your primitive inside using coordinates regarding the size property you pick for the Canvas and you'll be fine.
-         * @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
+         * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
+         * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
          */
-        Canvas2D.CreateWorldSpace = function (scene, name, position, rotation, size, renderScaleFactor, sideOrientation, cachingStrategy, enableInteraction) {
-            if (renderScaleFactor === void 0) { renderScaleFactor = 1; }
-            if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
-            if (enableInteraction === void 0) { enableInteraction = true; }
-            if (cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
+        Canvas2D.CreateWorldSpace = function (scene, size, options) {
+            var cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
+            if (cs !== Canvas2D.CACHESTRATEGY_CANVAS) {
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
             }
             //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
-            if (!sideOrientation) {
-                sideOrientation = BABYLON.Mesh.DEFAULTSIDE;
-            }
+            var id = options && options.id || null;
+            var rsf = options && options.renderScaleFactor || 1;
             var c = new Canvas2D();
-            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 vertexData = BABYLON.VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: sideOrientation });
-            var mtl = new BABYLON.StandardMaterial(name + "_Material", scene);
+            c.setupCanvas(scene, id, new BABYLON.Size(size.width * rsf, size.height * rsf), false, cs, options && options.enableInteraction || true);
+            var plane = new BABYLON.WorldSpaceCanvas2D(id, scene, c);
+            var vertexData = BABYLON.VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: options && options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE });
+            var mtl = new BABYLON.StandardMaterial(id + "_Material", scene);
             c.applyCachedTexture(vertexData, mtl);
             vertexData.applyToMesh(plane, false);
             mtl.specularColor = new BABYLON.Color3(0, 0, 0);
             mtl.disableLighting = true;
             mtl.useAlphaFromDiffuseTexture = true;
-            plane.position = position;
-            plane.rotationQuaternion = rotation;
+            plane.position = options && options.position || BABYLON.Vector3.Zero();
+            plane.rotationQuaternion = options && options.rotation || BABYLON.Quaternion.Identity();
             plane.material = mtl;
             c._worldSpaceNode = plane;
             return c;
@@ -110,7 +108,7 @@ var BABYLON;
             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(), null, 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;
@@ -122,7 +120,7 @@ var BABYLON;
                 _this.dispose();
             });
             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, { id: "###CANVAS BACKGROUND###", width: size.width, height: size.height });
                 this._background.isPickable = false;
                 this._background.origin = BABYLON.Vector2.Zero();
                 this._background.levelVisible = false;
@@ -222,6 +220,10 @@ var BABYLON;
             return this._capturedPointers.get(pointerId.toString());
         };
         Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, eventState) {
+            // Dispose check
+            if (this.isDisposed) {
+                return;
+            }
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
             this._updatePointerInfo(eventData);
             var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
@@ -668,6 +670,9 @@ var BABYLON;
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
             }
         };
+        Canvas2D.prototype.onPrimBecomesDirty = function () {
+            this._addPrimToDirtyList(this);
+        };
         Canvas2D.prototype._updateCanvasState = function () {
             // Check if the update has already been made for this render Frame
             if (this.scene.getRenderId() === this._updateRenderId) {
@@ -752,12 +757,12 @@ var BABYLON;
                 // Special case if the canvas is entirely cached: create a group that will have a single sprite it will be rendered specifically at the very end of the rendering process
                 if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
                     this._cachedCanvasGroup = BABYLON.Group2D._createCachedCanvasGroup(this);
-                    var sprite = BABYLON.Sprite2D.Create(this._cachedCanvasGroup, "__cachedCanvasSprite__", 0, 0, map, node.contentSize, node.pos);
+                    var sprite = BABYLON.Sprite2D.Create(this._cachedCanvasGroup, map, { id: "__cachedCanvasSprite__", spriteSize: node.contentSize, spriteLocation: node.pos });
                     sprite.zOrder = 1;
                     sprite.origin = BABYLON.Vector2.Zero();
                 }
                 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, map, { id: "__cachedSpriteOfGroup__" + group.id, x: group.position.x, y: group.position.y, spriteSize: node.contentSize, spriteLocation: node.pos });
                     sprite.origin = group.origin.clone();
                     res.sprite = sprite;
                 }
@@ -821,6 +826,6 @@ var BABYLON;
             BABYLON.className("Canvas2D")
         ], Canvas2D);
         return Canvas2D;
-    })(BABYLON.Group2D);
+    }(BABYLON.Group2D));
     BABYLON.Canvas2D = Canvas2D;
 })(BABYLON || (BABYLON = {}));

+ 46 - 33
src/Canvas2d/babylon.canvas2d.ts

@@ -48,21 +48,23 @@
         public static CACHESTRATEGY_DONTCACHE = 4;
 
         /**
-         * 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 bottom/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.
          * 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 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 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.
+         * Options:
+         *  - id: a text identifier, for information purpose only
+         *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport
+         *  - 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
+         *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property.
          */
-        static CreateScreenSpace(scene: Scene, name: string, pos: Vector2, size: Size, cachingStrategy: number = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, enableInteraction: boolean = true): Canvas2D {
+        static CreateScreenSpace(scene: Scene, options: { id?: string, pos?: Vector2, origin?: Vector2, size?: Size, cachingStrategy?: number, enableInteraction?: boolean }): Canvas2D {
             let c = new Canvas2D();
-            c.setupCanvas(scene, name, size, true, cachingStrategy, enableInteraction);
-            c.position = pos;
-            c.origin = Vector2.Zero();
+            c.setupCanvas(scene, options && options.id || null, options && options.size || null, true, options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, options && options.enableInteraction || true);
+            c.position = options && options.pos || Vector2.Zero();
+            c.origin = options && options.origin || Vector2.Zero();
 
             return c;
         }
@@ -71,18 +73,22 @@
          * 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.
          * @param scene the Scene that owns the Canvas
-         * @param name the name of the Canvas, for information purpose only
-         * @param position the position of the Canvas in World Space
-         * @param rotation the rotation of the Canvas in World Space
          * @param size the dimension of the Canvas in World Space
-         * @param renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality.
+         * Options:
+         *  - id: a text identifier, for information purpose only, default is null.
+         *  - position the position of the Canvas in World Space, default is [0,0,0]
+         *  - rotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         *  - renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality. Default value is 1.
          * BE AWARE that the Canvas true dimension will be size*renderScaleFactor, then all coordinates and size will have to be express regarding this size.
          * TIPS: if you want a renderScaleFactor independent reference of frame, create a child Group2D in the Canvas with position 0,0 and size set to null, then set its scale property to the same amount than the renderScaleFactor, put all your primitive inside using coordinates regarding the size property you pick for the Canvas and you'll be fine.
-         * @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
+         * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
+         * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
          */
-        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) {
+        static CreateWorldSpace(scene: Scene, size: Size, options: { id?: string, position?: Vector3, rotation?: Quaternion, renderScaleFactor?: number, sideOrientation?: number, cachingStrategy?: number, enableInteraction?: boolean}): Canvas2D {
+
+            let cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
+
+            if (cs !== Canvas2D.CACHESTRATEGY_CANVAS) {
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
             }
 
@@ -90,16 +96,14 @@
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
 
-            if (!sideOrientation) {
-                sideOrientation = Mesh.DEFAULTSIDE;
-            }
-
+            let id = options && options.id || null;
+            let rsf = options && options.renderScaleFactor || 1;
             let c = new Canvas2D();
-            c.setupCanvas(scene, name, new Size(size.width*renderScaleFactor, size.height*renderScaleFactor), false, cachingStrategy, enableInteraction);
+            c.setupCanvas(scene, id, new Size(size.width * rsf, size.height * rsf), false, cs, options && options.enableInteraction || true);
 
-            let plane = new WorldSpaceCanvas2d(name, scene, c);
-            let vertexData = VertexData.CreatePlane({ width: size.width/2, height: size.height/2, sideOrientation: sideOrientation });
-            let mtl = new StandardMaterial(name + "_Material", scene);
+            let plane = new WorldSpaceCanvas2D(id, scene, c);
+            let vertexData = VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: options && options.sideOrientation || Mesh.DEFAULTSIDE });
+            let mtl = new StandardMaterial(id + "_Material", scene);
 
             c.applyCachedTexture(vertexData, mtl);
             vertexData.applyToMesh(plane, false);
@@ -107,8 +111,8 @@
             mtl.specularColor = new Color3(0, 0, 0);
             mtl.disableLighting =true;
             mtl.useAlphaFromDiffuseTexture = true;
-            plane.position = position;
-            plane.rotationQuaternion = rotation;
+            plane.position = options && options.position || Vector3.Zero();
+            plane.rotationQuaternion = options && options.rotation || Quaternion.Identity();
             plane.material = mtl;
             c._worldSpaceNode = plane;
 
@@ -127,7 +131,7 @@
             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(), null, size, this._cachingStrategy===Canvas2D.CACHESTRATEGY_ALLGROUPS ? Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
 
             this._hierarchyLevelMaxSiblingCount = 100;
             this._hierarchyDepthOffset = 0;
@@ -142,7 +146,7 @@
             });
 
             if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
-                this._background = Rectangle2D.Create(this, "###CANVAS BACKGROUND###", 0, 0, size.width, size.height);
+                this._background = Rectangle2D.Create(this, { id: "###CANVAS BACKGROUND###", width: size.width, height: size.height});
                 this._background.isPickable = false;
                 this._background.origin = Vector2.Zero();
                 this._background.levelVisible = false;
@@ -257,6 +261,11 @@
            
         private static _interInfo = new IntersectInfo2D();
         private _handlePointerEventForInteraction(eventData: PointerInfoPre, eventState: EventState) {
+            // Dispose check
+            if (this.isDisposed) {
+                return;
+            }
+
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
             this._updatePointerInfo(eventData);
 
@@ -640,7 +649,7 @@
         /**
          * Only valid for World Space Canvas, returns the scene node that display the canvas
          */
-        public get worldSpaceCanvasNode(): WorldSpaceCanvas2d {
+        public get worldSpaceCanvasNode(): WorldSpaceCanvas2D {
             return this._worldSpaceNode;
         }
 
@@ -757,7 +766,7 @@
         private _actualOverPrimitive: PrimitiveIntersectedInfo;
         private _capturedPointers: StringDictionary<Prim2DBase>;
         private _scenePrePointerObserver: Observer<PointerInfoPre>;
-        private _worldSpaceNode: WorldSpaceCanvas2d;
+        private _worldSpaceNode: WorldSpaceCanvas2D;
         private _mapCounter = 0;
         private _background: Rectangle2D;
         private _scene: Scene;
@@ -774,6 +783,10 @@
 
         public _renderingSize: Size;
 
+        protected onPrimBecomesDirty() {
+            this._addPrimToDirtyList(this);
+        }
+
         private _updateCanvasState() {
             // Check if the update has already been made for this render Frame
             if (this.scene.getRenderId() === this._updateRenderId) {
@@ -877,14 +890,14 @@
                 // Special case if the canvas is entirely cached: create a group that will have a single sprite it will be rendered specifically at the very end of the rendering process
                 if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
                     this._cachedCanvasGroup = Group2D._createCachedCanvasGroup(this);
-                    let sprite = Sprite2D.Create(this._cachedCanvasGroup, "__cachedCanvasSprite__", 0, 0, map, node.contentSize, node.pos);
+                    let sprite = Sprite2D.Create(this._cachedCanvasGroup, map, {id: "__cachedCanvasSprite__", spriteSize:node.contentSize, spriteLocation:node.pos});
                     sprite.zOrder = 1;
                     sprite.origin = Vector2.Zero();
                 }
 
                 // 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 {
-                    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, map, {id:`__cachedSpriteOfGroup__${group.id}`, x: group.position.x, y: group.position.y, spriteSize:node.contentSize, spriteLocation:node.pos});
                     sprite.origin = group.origin.clone();
                     res.sprite = sprite;
                 }

+ 306 - 0
src/Canvas2d/babylon.ellipse2d.js

@@ -0,0 +1,306 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var Ellipse2DRenderCache = (function (_super) {
+        __extends(Ellipse2DRenderCache, _super);
+        function Ellipse2DRenderCache(engine, modelKey, isTransparent) {
+            _super.call(this, engine, modelKey, isTransparent);
+        }
+        Ellipse2DRenderCache.prototype.render = function (instanceInfo, context) {
+            // Do nothing if the shader is still loading/preparing 
+            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
+                return false;
+            }
+            var engine = instanceInfo._owner.owner.engine;
+            var depthFunction = 0;
+            if (this.effectFill && this.effectBorder) {
+                depthFunction = engine.getDepthFunction();
+                engine.setDepthFunctionToLessOrEqual();
+            }
+            var cur;
+            if (this.isTransparent) {
+                cur = engine.getAlphaMode();
+                engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+            }
+            if (this.effectFill) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
+                engine.enableEffect(this.effectFill);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, this.effectFill);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingFillAttributes) {
+                        // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+                        this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.fillIndicesCount);
+                    }
+                }
+            }
+            if (this.effectBorder) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
+                engine.enableEffect(this.effectBorder);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingBorderAttributes) {
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.borderIndicesCount);
+                    }
+                }
+            }
+            if (this.isTransparent) {
+                engine.setAlphaMode(cur);
+            }
+            if (this.effectFill && this.effectBorder) {
+                engine.setDepthFunction(depthFunction);
+            }
+            return true;
+        };
+        Ellipse2DRenderCache.prototype.dispose = function () {
+            if (!_super.prototype.dispose.call(this)) {
+                return false;
+            }
+            if (this.fillVB) {
+                this._engine._releaseBuffer(this.fillVB);
+                this.fillVB = null;
+            }
+            if (this.fillIB) {
+                this._engine._releaseBuffer(this.fillIB);
+                this.fillIB = null;
+            }
+            if (this.effectFill) {
+                this._engine._releaseEffect(this.effectFill);
+                this.effectFill = null;
+            }
+            if (this.borderVB) {
+                this._engine._releaseBuffer(this.borderVB);
+                this.borderVB = null;
+            }
+            if (this.borderIB) {
+                this._engine._releaseBuffer(this.borderIB);
+                this.borderIB = null;
+            }
+            if (this.effectBorder) {
+                this._engine._releaseEffect(this.effectBorder);
+                this.effectBorder = null;
+            }
+            return true;
+        };
+        return Ellipse2DRenderCache;
+    }(BABYLON.ModelRenderCache));
+    BABYLON.Ellipse2DRenderCache = Ellipse2DRenderCache;
+    var Ellipse2DInstanceData = (function (_super) {
+        __extends(Ellipse2DInstanceData, _super);
+        function Ellipse2DInstanceData(partId) {
+            _super.call(this, partId, 1);
+        }
+        Object.defineProperty(Ellipse2DInstanceData.prototype, "properties", {
+            get: function () {
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        __decorate([
+            BABYLON.instanceData()
+        ], Ellipse2DInstanceData.prototype, "properties", null);
+        return Ellipse2DInstanceData;
+    }(BABYLON.Shape2DInstanceData));
+    BABYLON.Ellipse2DInstanceData = Ellipse2DInstanceData;
+    var Ellipse2D = (function (_super) {
+        __extends(Ellipse2D, _super);
+        function Ellipse2D() {
+            _super.apply(this, arguments);
+        }
+        Object.defineProperty(Ellipse2D.prototype, "actualSize", {
+            get: function () {
+                return this.size;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Ellipse2D.prototype, "size", {
+            get: function () {
+                return this._size;
+            },
+            set: function (value) {
+                this._size = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Ellipse2D.prototype, "subdivisions", {
+            get: function () {
+                return this._subdivisions;
+            },
+            set: function (value) {
+                this._subdivisions = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Ellipse2D.prototype.levelIntersect = function (intersectInfo) {
+            var x = intersectInfo._localPickPosition.x;
+            var y = intersectInfo._localPickPosition.y;
+            var w = this.size.width / 2;
+            var h = this.size.height / 2;
+            return ((x * x) / (w * w) + (y * y) / (h * h)) <= 1;
+        };
+        Ellipse2D.prototype.updateLevelBoundingInfo = function () {
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+        };
+        Ellipse2D.prototype.setupEllipse2D = function (owner, parent, id, position, origin, size, subdivisions, fill, border, borderThickness) {
+            if (subdivisions === void 0) { subdivisions = 64; }
+            if (borderThickness === void 0) { borderThickness = 1; }
+            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+            this.size = size;
+            this.subdivisions = subdivisions;
+        };
+        /**
+         * Create an Ellipse 2D Shape primitive
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id: a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - width: the width of the ellipse, default is 10
+         *  - height: the height of the ellipse, default is 10
+         *  - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
+         *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
+         *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
+         *  - borderThickness: the thickness of the drawn border, default is 1.
+         */
+        Ellipse2D.Create = function (parent, options) {
+            BABYLON.Prim2DBase.CheckParent(parent);
+            var fill;
+            if (options && options.fill !== undefined) {
+                fill = options.fill;
+            }
+            else {
+                fill = BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+            }
+            var ellipse = new Ellipse2D();
+            ellipse.setupEllipse2D(parent.owner, parent, options && options.id || null, new BABYLON.Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, new BABYLON.Size(options && options.width || 10, options && options.height || 10), options && options.subdivisions || 64, fill, options && options.border || null, options && options.borderThickness || 1);
+            return ellipse;
+        };
+        Ellipse2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
+            var renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey, isTransparent);
+            return renderCache;
+        };
+        Ellipse2D.prototype.setupModelRenderCache = function (modelRenderCache) {
+            var renderCache = modelRenderCache;
+            var engine = this.owner.engine;
+            // Need to create WebGL resources for fill part?
+            if (this.fill) {
+                var vbSize = this.subdivisions + 1;
+                var vb = new Float32Array(vbSize);
+                for (var i = 0; i < vbSize; i++) {
+                    vb[i] = i;
+                }
+                renderCache.fillVB = engine.createVertexBuffer(vb);
+                var triCount = vbSize - 1;
+                var ib = new Float32Array(triCount * 3);
+                for (var i = 0; i < triCount; i++) {
+                    ib[i * 3 + 0] = 0;
+                    ib[i * 3 + 2] = i + 1;
+                    ib[i * 3 + 1] = i + 2;
+                }
+                ib[triCount * 3 - 2] = 1;
+                renderCache.fillIB = engine.createIndexBuffer(ib);
+                renderCache.fillIndicesCount = triCount * 3;
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"]);
+                renderCache.effectFill = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            // Need to create WebGL resource for border part?
+            if (this.border) {
+                var vbSize = this.subdivisions * 2;
+                var vb = new Float32Array(vbSize);
+                for (var i = 0; i < vbSize; i++) {
+                    vb[i] = i;
+                }
+                renderCache.borderVB = engine.createVertexBuffer(vb);
+                var triCount = vbSize;
+                var rs = triCount / 2;
+                var ib = new Float32Array(triCount * 3);
+                for (var i = 0; i < rs; i++) {
+                    var r0 = i;
+                    var r1 = (i + 1) % rs;
+                    ib[i * 6 + 0] = rs + r1;
+                    ib[i * 6 + 1] = rs + r0;
+                    ib[i * 6 + 2] = r0;
+                    ib[i * 6 + 3] = r1;
+                    ib[i * 6 + 4] = rs + r1;
+                    ib[i * 6 + 5] = r0;
+                }
+                renderCache.borderIB = engine.createIndexBuffer(ib);
+                renderCache.borderIndicesCount = (triCount * 3);
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"]);
+                renderCache.effectBorder = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            return renderCache;
+        };
+        Ellipse2D.prototype.createInstanceDataParts = function () {
+            var res = new Array();
+            if (this.border) {
+                res.push(new Ellipse2DInstanceData(BABYLON.Shape2D.SHAPE2D_BORDERPARTID));
+            }
+            if (this.fill) {
+                res.push(new Ellipse2DInstanceData(BABYLON.Shape2D.SHAPE2D_FILLPARTID));
+            }
+            return res;
+        };
+        Ellipse2D.prototype.refreshInstanceDataPart = function (part) {
+            if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
+                return false;
+            }
+            if (part.id === BABYLON.Shape2D.SHAPE2D_BORDERPARTID) {
+                var d = part;
+                var size = this.size;
+                d.properties = new BABYLON.Vector3(size.width, size.height, this.subdivisions);
+            }
+            else if (part.id === BABYLON.Shape2D.SHAPE2D_FILLPARTID) {
+                var d = part;
+                var size = this.size;
+                d.properties = new BABYLON.Vector3(size.width, size.height, this.subdivisions);
+            }
+            return true;
+        };
+        __decorate([
+            BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Ellipse2D.sizeProperty = pi; }, false, true)
+        ], Ellipse2D.prototype, "size", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Ellipse2D.subdivisionsProperty = pi; })
+        ], Ellipse2D.prototype, "subdivisions", null);
+        Ellipse2D = __decorate([
+            BABYLON.className("Ellipse2D")
+        ], Ellipse2D);
+        return Ellipse2D;
+    }(BABYLON.Shape2D));
+    BABYLON.Ellipse2D = Ellipse2D;
+})(BABYLON || (BABYLON = {}));

+ 321 - 0
src/Canvas2d/babylon.ellipse2d.ts

@@ -0,0 +1,321 @@
+module BABYLON {
+    export class Ellipse2DRenderCache extends ModelRenderCache {
+        fillVB: WebGLBuffer;
+        fillIB: WebGLBuffer;
+        fillIndicesCount: number;
+        instancingFillAttributes: InstancingAttributeInfo[];
+        effectFill: Effect;
+
+        borderVB: WebGLBuffer;
+        borderIB: WebGLBuffer;
+        borderIndicesCount: number;
+        instancingBorderAttributes: InstancingAttributeInfo[];
+        effectBorder: Effect;
+
+        constructor(engine: Engine, modelKey: string, isTransparent: boolean) {
+            super(engine, modelKey, isTransparent);
+        }
+
+        render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
+            // Do nothing if the shader is still loading/preparing 
+            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
+                return false;
+            }
+
+            var engine = instanceInfo._owner.owner.engine;
+
+            let depthFunction = 0;
+            if (this.effectFill && this.effectBorder) {
+                depthFunction = engine.getDepthFunction();
+                engine.setDepthFunctionToLessOrEqual();
+            }
+
+            var cur: number;
+            if (this.isTransparent) {
+                cur = engine.getAlphaMode();
+                engine.setAlphaMode(Engine.ALPHA_COMBINE);
+            }
+
+            if (this.effectFill) {
+                let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
+
+                engine.enableEffect(this.effectFill);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, this.effectFill);
+                let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingFillAttributes) {
+                        // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+                        this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
+                    }
+
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                } else {
+                    for (let i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.fillIndicesCount);                        
+                    }
+                }
+            }
+
+            if (this.effectBorder) {
+                let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_BORDERPARTID.toString());
+
+                engine.enableEffect(this.effectBorder);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
+                let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingBorderAttributes) {
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                    }
+
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                } else {
+                    for (let i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.borderIndicesCount);
+                    }
+                }
+            }
+
+            if (this.isTransparent) {
+                engine.setAlphaMode(cur);
+            }
+
+            if (this.effectFill && this.effectBorder) {
+                engine.setDepthFunction(depthFunction);
+            }
+            return true;
+        }
+
+        public dispose(): boolean {
+            if (!super.dispose()) {
+                return false;
+            }
+
+            if (this.fillVB) {
+                this._engine._releaseBuffer(this.fillVB);
+                this.fillVB = null;
+            }
+
+            if (this.fillIB) {
+                this._engine._releaseBuffer(this.fillIB);
+                this.fillIB = null;
+            }
+
+            if (this.effectFill) {
+                this._engine._releaseEffect(this.effectFill);
+                this.effectFill = null;
+            }
+
+            if (this.borderVB) {
+                this._engine._releaseBuffer(this.borderVB);
+                this.borderVB = null;
+            }
+
+            if (this.borderIB) {
+                this._engine._releaseBuffer(this.borderIB);
+                this.borderIB = null;
+            }
+
+            if (this.effectBorder) {
+                this._engine._releaseEffect(this.effectBorder);
+                this.effectBorder = null;
+            }
+
+            return true;
+        }
+    }
+
+    export class Ellipse2DInstanceData extends Shape2DInstanceData {
+        constructor(partId: number) {
+            super(partId, 1);
+        }
+
+        @instanceData()
+        get properties(): Vector3 {
+            return null;
+        }
+    }
+
+    @className("Ellipse2D")
+    export class Ellipse2D extends Shape2D {
+
+        public static sizeProperty: Prim2DPropInfo;
+        public static subdivisionsProperty: Prim2DPropInfo;
+
+        public get actualSize(): Size {
+            return this.size;
+        }
+
+        @instanceLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Ellipse2D.sizeProperty = pi, false, true)
+        public get size(): Size {
+            return this._size;
+        }
+
+        public set size(value: Size) {
+            this._size = value;
+        }
+
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Ellipse2D.subdivisionsProperty = pi)
+        public get subdivisions(): number {
+            return this._subdivisions;
+        }
+
+        public set subdivisions(value: number) {
+            this._subdivisions = value;
+        }
+
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            let x = intersectInfo._localPickPosition.x;
+            let y = intersectInfo._localPickPosition.y;
+            let w = this.size.width/2;
+            let h = this.size.height/2;
+            return ((x * x) / (w * w) + (y * y) / (h * h)) <= 1;
+        }
+
+        protected updateLevelBoundingInfo() {
+            BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+        }
+
+        protected setupEllipse2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, subdivisions: number=64, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
+            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+            this.size = size;
+            this.subdivisions = subdivisions;
+        }
+
+        /**
+         * Create an Ellipse 2D Shape primitive
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id: a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - width: the width of the ellipse, default is 10
+         *  - height: the height of the ellipse, default is 10
+         *  - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
+         *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
+         *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
+         *  - borderThickness: the thickness of the drawn border, default is 1.
+         */
+        public static Create(parent: Prim2DBase, options: { id?: string, x?: number, y?: number, origin?: Vector2, width?: number, height?: number, subdivisions?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number }): Ellipse2D {
+            Prim2DBase.CheckParent(parent);
+
+            let fill: IBrush2D;
+            if (options && options.fill !== undefined) {
+                fill = options.fill;
+            } else {
+                fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+            }
+
+            let ellipse = new Ellipse2D();
+            ellipse.setupEllipse2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, new Size(options && options.width || 10, options && options.height || 10), options && options.subdivisions || 64, fill, options && options.border || null, options && options.borderThickness || 1);
+            return ellipse;
+        }
+
+        protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
+            let renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey, isTransparent);
+            return renderCache;
+        }
+
+        protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
+            let renderCache = <Ellipse2DRenderCache>modelRenderCache;
+            let engine = this.owner.engine;
+
+            // Need to create WebGL resources for fill part?
+            if (this.fill) {
+                let vbSize = this.subdivisions + 1;
+                let vb = new Float32Array(vbSize);
+                for (let i = 0; i < vbSize; i++) {
+                    vb[i] = i;
+                }
+                renderCache.fillVB = engine.createVertexBuffer(vb);
+
+                let triCount = vbSize - 1;
+                let ib = new Float32Array(triCount * 3);
+                for (let i = 0; i < triCount; i++) {
+                    ib[i * 3 + 0] = 0;
+                    ib[i * 3 + 2] = i + 1;
+                    ib[i * 3 + 1] = i + 2;
+                }
+                ib[triCount * 3 - 2] = 1;
+
+                renderCache.fillIB = engine.createIndexBuffer(ib);
+                renderCache.fillIndicesCount = triCount * 3;
+
+                let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["index"]);
+                renderCache.effectFill = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+
+            // Need to create WebGL resource for border part?
+            if (this.border) {
+                let vbSize = this.subdivisions * 2;
+                let vb = new Float32Array(vbSize);
+                for (let i = 0; i < vbSize; i++) {
+                    vb[i] = i;
+                }
+                renderCache.borderVB = engine.createVertexBuffer(vb);
+
+                let triCount = vbSize;
+                let rs = triCount / 2;
+                let ib = new Float32Array(triCount * 3);
+                for (let i = 0; i < rs; i++) {
+                    let r0 = i;
+                    let r1 = (i + 1) % rs;
+
+                    ib[i * 6 + 0] = rs + r1;
+                    ib[i * 6 + 1] = rs + r0;
+                    ib[i * 6 + 2] = r0;
+
+                    ib[i * 6 + 3] = r1;
+                    ib[i * 6 + 4] = rs + r1;
+                    ib[i * 6 + 5] = r0;
+                }
+
+                renderCache.borderIB = engine.createIndexBuffer(ib);
+                renderCache.borderIndicesCount = (triCount* 3);
+
+                let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["index"]);
+                renderCache.effectBorder = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+
+            return renderCache;
+        }
+
+
+        protected createInstanceDataParts(): InstanceDataBase[] {
+            var res = new Array<InstanceDataBase>();
+            if (this.border) {
+                res.push(new Ellipse2DInstanceData(Shape2D.SHAPE2D_BORDERPARTID));
+            }
+            if (this.fill) {
+                res.push(new Ellipse2DInstanceData(Shape2D.SHAPE2D_FILLPARTID));
+            }
+            return res;
+        }
+
+        protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
+            if (!super.refreshInstanceDataPart(part)) {
+                return false;
+            }
+            if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
+                let d = <Ellipse2DInstanceData>part;
+                let size = this.size;
+                d.properties = new Vector3(size.width, size.height, this.subdivisions);
+            }
+            else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
+                let d = <Ellipse2DInstanceData>part;
+                let size = this.size;
+                d.properties = new Vector3(size.width, size.height, this.subdivisions);
+            }
+            return true;
+        }
+
+        private _size: Size;
+        private _subdivisions: number;
+    }
+}

+ 21 - 11
src/Canvas2d/babylon.group2d.js

@@ -22,16 +22,25 @@ var BABYLON;
             this._childrenRenderableGroups = new Array();
             this._renderGroupInstancesInfo = new BABYLON.StringDictionary();
         }
-        Group2D.CreateGroup2D = function (parent, id, position, size, cacheBehabior) {
-            if (cacheBehabior === void 0) { cacheBehabior = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY; }
+        /**
+         * Create an Logical or Renderable Group.
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - position: the X & Y positions relative to its parent, default is [0;0]
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - size: the size of the group, if null the size will be computed from its content, default is null.
+         *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
+         */
+        Group2D.CreateGroup2D = function (parent, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             var g = new Group2D();
-            g.setupGroup2D(parent.owner, parent, id, position, size, cacheBehabior);
+            g.setupGroup2D(parent.owner, parent, options && options.id || null, options && options.position || BABYLON.Vector2.Zero(), options && options.origin || null, options && options.size || null, options && options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
             return g;
         };
         Group2D._createCachedCanvasGroup = function (owner) {
             var g = new Group2D();
-            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero());
+            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero(), null);
             g.origin = BABYLON.Vector2.Zero();
             return g;
         };
@@ -76,10 +85,10 @@ var BABYLON;
             }
             return true;
         };
-        Group2D.prototype.setupGroup2D = function (owner, parent, id, position, size, cacheBehavior) {
+        Group2D.prototype.setupGroup2D = function (owner, parent, id, position, origin, size, cacheBehavior) {
             if (cacheBehavior === void 0) { cacheBehavior = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY; }
             this._cacheBehavior = cacheBehavior;
-            this.setupPrim2DBase(owner, parent, id, position);
+            this.setupPrim2DBase(owner, parent, id, position, origin);
             this.size = size;
             this._viewportPosition = BABYLON.Vector2.Zero();
         };
@@ -247,6 +256,7 @@ var BABYLON;
                         }
                     });
                     // Everything is updated, clear the dirty list
+                    this._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
                     this._primDirtyList.splice(0);
                 }
             }
@@ -273,7 +283,7 @@ var BABYLON;
                     var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                 }
                 // For each different model of primitive to render
-                var totalRenderCount = 0;
+                var totalRenderCount_1 = 0;
                 this._renderGroupInstancesInfo.forEach(function (k, v) {
                     // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
                     // Skip it if instanced arrays are not supported
@@ -282,7 +292,7 @@ var BABYLON;
                             // If the instances of the model was changed, pack the data
                             var array = v._instancesPartsData[i];
                             var instanceData_1 = array.pack();
-                            totalRenderCount += array.usedElementCount;
+                            totalRenderCount_1 += array.usedElementCount;
                             // Compute the size the instance buffer should have
                             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
@@ -300,12 +310,12 @@ var BABYLON;
                                 // Update the WebGL buffer to match the new content of the instances data
                                 engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
                                 engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData_1);
-                                v._dirtyInstancesData = false;
                             }
                         }
+                        v._dirtyInstancesData = false;
                     }
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
-                    if (!_this.owner.supportInstancedArray || totalRenderCount > 0) {
+                    if (!_this.owner.supportInstancedArray || totalRenderCount_1 > 0) {
                         // 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);
                         // Update dirty flag/related
@@ -460,6 +470,6 @@ var BABYLON;
             BABYLON.className("Group2D")
         ], Group2D);
         return Group2D;
-    })(BABYLON.Prim2DBase);
+    }(BABYLON.Prim2DBase));
     BABYLON.Group2D = Group2D;
 })(BABYLON || (BABYLON = {}));

+ 17 - 6
src/Canvas2d/babylon.group2d.ts

@@ -32,17 +32,27 @@
             this._renderGroupInstancesInfo = new StringDictionary<GroupInstanceInfo>();
         }
 
-        static CreateGroup2D(parent: Prim2DBase, id: string, position: Vector2, size?: Size, cacheBehabior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY): Group2D {
+        /**
+         * Create an Logical or Renderable Group.
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - position: the X & Y positions relative to its parent, default is [0;0]
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - size: the size of the group, if null the size will be computed from its content, default is null.
+         *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
+         */
+        static CreateGroup2D(parent: Prim2DBase, options: { id?: string, position?: Vector2; origin?: Vector2, size?: Size, cacheBehavior?: number}): Group2D {
             Prim2DBase.CheckParent(parent);
             var g = new Group2D();
-            g.setupGroup2D(parent.owner, parent, id, position, size, cacheBehabior);
+            g.setupGroup2D(parent.owner, parent, options && options.id || null, options && options.position || Vector2.Zero(), options && options.origin || null, options && options.size || null, options && options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
 
             return g;
         }
 
         static _createCachedCanvasGroup(owner: Canvas2D): Group2D {
             var g = new Group2D();
-            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", Vector2.Zero());
+            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", Vector2.Zero(), null);
             g.origin = Vector2.Zero();
             return g;
             
@@ -98,9 +108,9 @@
             return true;
         }
 
-        protected setupGroup2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, size?: Size, cacheBehavior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
+        protected setupGroup2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size?: Size, cacheBehavior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
             this._cacheBehavior = cacheBehavior;
-            this.setupPrim2DBase(owner, parent, id, position);
+            this.setupPrim2DBase(owner, parent, id, position, origin);
             this.size = size;
             this._viewportPosition = Vector2.Zero();
         }
@@ -278,6 +288,7 @@
                     });
 
                     // Everything is updated, clear the dirty list
+                    this._primDirtyList.forEach(p => p._resetPropertiesDirty());
                     this._primDirtyList.splice(0);
                 }
             }
@@ -337,9 +348,9 @@
                                 engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
                                 engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData);
 
-                                v._dirtyInstancesData = false;
                             }
                         }
+                        v._dirtyInstancesData = false;
                     }
 
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)

+ 991 - 0
src/Canvas2d/babylon.lines2d.js

@@ -0,0 +1,991 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var Lines2DRenderCache = (function (_super) {
+        __extends(Lines2DRenderCache, _super);
+        function Lines2DRenderCache(engine, modelKey, isTransparent) {
+            _super.call(this, engine, modelKey, isTransparent);
+        }
+        Lines2DRenderCache.prototype.render = function (instanceInfo, context) {
+            // Do nothing if the shader is still loading/preparing 
+            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
+                return false;
+            }
+            var engine = instanceInfo._owner.owner.engine;
+            var depthFunction = 0;
+            if (this.effectFill && this.effectBorder) {
+                depthFunction = engine.getDepthFunction();
+                engine.setDepthFunctionToLessOrEqual();
+            }
+            var cur;
+            if (this.isTransparent) {
+                cur = engine.getAlphaMode();
+                engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+            }
+            if (this.effectFill) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
+                engine.enableEffect(this.effectFill);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [2], 2 * 4, this.effectFill);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingFillAttributes) {
+                        // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+                        this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.fillIndicesCount);
+                    }
+                }
+            }
+            if (this.effectBorder) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
+                engine.enableEffect(this.effectBorder);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [2], 2 * 4, this.effectBorder);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingBorderAttributes) {
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.borderIndicesCount);
+                    }
+                }
+            }
+            if (this.isTransparent) {
+                engine.setAlphaMode(cur);
+            }
+            if (this.effectFill && this.effectBorder) {
+                engine.setDepthFunction(depthFunction);
+            }
+            return true;
+        };
+        Lines2DRenderCache.prototype.dispose = function () {
+            if (!_super.prototype.dispose.call(this)) {
+                return false;
+            }
+            if (this.fillVB) {
+                this._engine._releaseBuffer(this.fillVB);
+                this.fillVB = null;
+            }
+            if (this.fillIB) {
+                this._engine._releaseBuffer(this.fillIB);
+                this.fillIB = null;
+            }
+            if (this.effectFill) {
+                this._engine._releaseEffect(this.effectFill);
+                this.effectFill = null;
+            }
+            if (this.borderVB) {
+                this._engine._releaseBuffer(this.borderVB);
+                this.borderVB = null;
+            }
+            if (this.borderIB) {
+                this._engine._releaseBuffer(this.borderIB);
+                this.borderIB = null;
+            }
+            if (this.effectBorder) {
+                this._engine._releaseEffect(this.effectBorder);
+                this.effectBorder = null;
+            }
+            return true;
+        };
+        return Lines2DRenderCache;
+    }(BABYLON.ModelRenderCache));
+    BABYLON.Lines2DRenderCache = Lines2DRenderCache;
+    var Lines2DInstanceData = (function (_super) {
+        __extends(Lines2DInstanceData, _super);
+        function Lines2DInstanceData(partId) {
+            _super.call(this, partId, 1);
+        }
+        Object.defineProperty(Lines2DInstanceData.prototype, "boundingMin", {
+            get: function () {
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2DInstanceData.prototype, "boundingMax", {
+            get: function () {
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        __decorate([
+            BABYLON.instanceData()
+        ], Lines2DInstanceData.prototype, "boundingMin", null);
+        __decorate([
+            BABYLON.instanceData()
+        ], Lines2DInstanceData.prototype, "boundingMax", null);
+        return Lines2DInstanceData;
+    }(BABYLON.Shape2DInstanceData));
+    BABYLON.Lines2DInstanceData = Lines2DInstanceData;
+    var Lines2D = (function (_super) {
+        __extends(Lines2D, _super);
+        function Lines2D() {
+            _super.apply(this, arguments);
+        }
+        Object.defineProperty(Lines2D, "NoCap", {
+            get: function () { return Lines2D._noCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "RoundCap", {
+            get: function () { return Lines2D._roundCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "TriangleCap", {
+            get: function () { return Lines2D._triangleCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "SquareAnchorCap", {
+            get: function () { return Lines2D._squareAnchorCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "RoundAnchorCap", {
+            get: function () { return Lines2D._roundAnchorCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "DiamondAnchorCap", {
+            get: function () { return Lines2D._diamondAnchorCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "ArrowCap", {
+            get: function () { return Lines2D._arrowCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "actualSize", {
+            get: function () {
+                return this.size;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "points", {
+            get: function () {
+                return this._points;
+            },
+            set: function (value) {
+                this._points = value;
+                this._levelBoundingInfoDirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "fillThickness", {
+            get: function () {
+                return this._fillThickness;
+            },
+            set: function (value) {
+                this._fillThickness = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "closed", {
+            get: function () {
+                return this._closed;
+            },
+            set: function (value) {
+                this._closed = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "startCap", {
+            get: function () {
+                return this._startCap;
+            },
+            set: function (value) {
+                this._startCap = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "endCap", {
+            get: function () {
+                return this._endCap;
+            },
+            set: function (value) {
+                this._endCap = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Lines2D.prototype.levelIntersect = function (intersectInfo) {
+            var pl = this.points.length;
+            var l = this.closed ? pl + 1 : pl;
+            var originOffset = new BABYLON.Vector2(-0.5, -0.5);
+            var p = intersectInfo._localPickPosition;
+            var prevA = this.transformPointWithOrigin(this._contour[0], originOffset);
+            var prevB = this.transformPointWithOrigin(this._contour[1], originOffset);
+            for (var i = 1; i < l; i++) {
+                var curA = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 0], originOffset);
+                var curB = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 1], originOffset);
+                if (BABYLON.Vector2.PointInTriangle(p, prevA, prevB, curA)) {
+                    return true;
+                }
+                if (BABYLON.Vector2.PointInTriangle(p, curA, prevB, curB)) {
+                    return true;
+                }
+                prevA = curA;
+                prevB = curB;
+            }
+            return false;
+        };
+        Object.defineProperty(Lines2D.prototype, "size", {
+            get: function () {
+                return this._size;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "boundingMin", {
+            get: function () {
+                return this._boundingMin;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "boundingMax", {
+            get: function () {
+                return this._boundingMax;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Lines2D.prototype.getUsedShaderCategories = function (dataPart) {
+            var res = _super.prototype.getUsedShaderCategories.call(this, dataPart);
+            // Remove the BORDER category, we don't use it in the VertexShader
+            var i = res.indexOf(BABYLON.Shape2D.SHAPE2D_CATEGORY_BORDER);
+            if (i !== -1) {
+                res.splice(i, 1);
+            }
+            return res;
+        };
+        Lines2D.prototype.updateLevelBoundingInfo = function () {
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+        };
+        Lines2D.prototype.setupLines2D = function (owner, parent, id, position, origin, points, fillThickness, startCap, endCap, fill, border, borderThickness, closed) {
+            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+            this.fillThickness = fillThickness;
+            this.startCap = startCap;
+            this.endCap = endCap;
+            this.points = points;
+            this.closed = closed;
+            this._size = BABYLON.Size.Zero();
+            this._boundingMin = BABYLON.Vector2.Zero();
+            this._boundingMax = BABYLON.Vector2.Zero();
+        };
+        /**
+         * Create an 2D Lines Shape primitive. The defined lines may be opened or closed (see below)
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * @param points an array that describe the points to use to draw the line, must contain at least two entries.
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - fillThickness: the thickness of the fill part of the line, can be null to draw nothing (but a border brush must be given), default is 1.
+         *  - closed: if false the lines are said to be opened, the first point and the latest DON'T connect. if true the lines are said to be closed, the first and last point will be connected by a line. For instance you can define the 4 points of a rectangle, if you set closed to true a 4 edges rectangle will be drawn. If you set false, only three edges will be drawn, the edge formed by the first and last point won't exist. Default is false.
+         *  - Draw a cap of the given type at the start of the first line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
+         *  - Draw a cap of the given type at the end of the last line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
+         *  - fill: the brush used to draw the fill content of the lines, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
+         *  - border: the brush used to draw the border of the lines, you can set null to draw nothing (but you will have to set a fill brush), default is null.
+         *  - borderThickness: the thickness of the drawn border, default is 1.
+         */
+        Lines2D.Create = function (parent, points, options) {
+            BABYLON.Prim2DBase.CheckParent(parent);
+            var fill;
+            if (options && options.fill !== undefined) {
+                fill = options.fill;
+            }
+            else {
+                fill = BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+            }
+            var lines = new Lines2D();
+            lines.setupLines2D(parent.owner, parent, options && options.id || null, new BABYLON.Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, points, options && options.fillThickness || 1, options && options.startCap || 0, options && options.endCap || 0, fill, options && options.border || null, options && options.borderThickness || 0, options && options.closed || false);
+            return lines;
+        };
+        Lines2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
+            var renderCache = new Lines2DRenderCache(this.owner.engine, modelKey, isTransparent);
+            return renderCache;
+        };
+        Lines2D.prototype.setupModelRenderCache = function (modelRenderCache) {
+            var _this = this;
+            var renderCache = modelRenderCache;
+            var engine = this.owner.engine;
+            // Init min/max because their being computed here
+            this.boundingMin = new BABYLON.Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
+            this.boundingMax = new BABYLON.Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
+            var perp = function (v, res) {
+                res.x = v.y;
+                res.y = -v.x;
+            };
+            var direction = function (a, b, res) {
+                a.subtractToRef(b, res);
+                res.normalize();
+            };
+            var tps = BABYLON.Vector2.Zero();
+            var computeMiter = function (tangent, miter, a, b) {
+                a.addToRef(b, tangent);
+                tangent.normalize();
+                miter.x = -tangent.y;
+                miter.y = tangent.x;
+                tps.x = -a.y;
+                tps.y = a.x;
+                return 1 / BABYLON.Vector2.Dot(miter, tps);
+            };
+            var intersect = function (x1, y1, x2, y2, x3, y3, x4, y4) {
+                var d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+                if (d === 0)
+                    return false;
+                var xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; // Intersection point is xi/yi, just in case...
+                //let yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; // That's why I left it commented
+                if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2))
+                    return false;
+                if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4))
+                    return false;
+                return true;
+            };
+            var startDir = BABYLON.Vector2.Zero();
+            var endDir = BABYLON.Vector2.Zero();
+            var updateMinMax = function (array, offset) {
+                if (offset >= array.length) {
+                    return;
+                }
+                _this._boundingMin.x = Math.min(_this._boundingMin.x, array[offset]);
+                _this._boundingMax.x = Math.max(_this._boundingMax.x, array[offset]);
+                _this._boundingMin.y = Math.min(_this._boundingMin.y, array[offset + 1]);
+                _this._boundingMax.y = Math.max(_this._boundingMax.y, array[offset + 1]);
+            };
+            var store = function (array, contour, index, max, p, n, halfThickness, borderThickness, detectFlip) {
+                var borderMode = borderThickness != null && !isNaN(borderThickness);
+                var off = index * (borderMode ? 8 : 4);
+                // Mandatory because we'll be out of bound in case of closed line, for the very last point (which is a duplicate of the first that we don't store in the vb)
+                if (off >= array.length) {
+                    return;
+                }
+                // Store start/end normal, we need it for the cap construction
+                if (index === 0) {
+                    perp(n, startDir);
+                }
+                else if (index === max - 1) {
+                    perp(n, endDir);
+                    endDir.x *= -1;
+                    endDir.y *= -1;
+                }
+                var swap = false;
+                array[off + 0] = p.x + n.x * halfThickness;
+                array[off + 1] = p.y + n.y * halfThickness;
+                array[off + 2] = p.x + n.x * -halfThickness;
+                array[off + 3] = p.y + n.y * -halfThickness;
+                updateMinMax(array, off);
+                updateMinMax(array, off + 2);
+                // If an index is given we check if the two segments formed between [index+0;detectFlip+0] and [index+2;detectFlip+2] intersect themselves.
+                // It should not be the case, they should be parallel, so if they cross, we switch the order of storage to ensure we'll have parallel lines
+                if (detectFlip !== undefined) {
+                    // Flip if intersect
+                    var flipOff = detectFlip * (borderMode ? 8 : 4);
+                    if (intersect(array[off + 0], array[off + 1], array[flipOff + 0], array[flipOff + 1], array[off + 2], array[off + 3], array[flipOff + 2], array[flipOff + 3])) {
+                        swap = true;
+                        var tps_1 = array[off + 0];
+                        array[off + 0] = array[off + 2];
+                        array[off + 2] = tps_1;
+                        tps_1 = array[off + 1];
+                        array[off + 1] = array[off + 3];
+                        array[off + 3] = tps_1;
+                    }
+                }
+                if (borderMode) {
+                    var t = halfThickness + borderThickness;
+                    array[off + 4] = p.x + n.x * (swap ? -t : t);
+                    array[off + 5] = p.y + n.y * (swap ? -t : t);
+                    array[off + 6] = p.x + n.x * (swap ? t : -t);
+                    array[off + 7] = p.y + n.y * (swap ? t : -t);
+                    updateMinMax(array, off + 4);
+                    updateMinMax(array, off + 6);
+                }
+                if (contour) {
+                    off += borderMode ? 4 : 0;
+                    contour.push(new BABYLON.Vector2(array[off + 0], array[off + 1]));
+                    contour.push(new BABYLON.Vector2(array[off + 2], array[off + 3]));
+                }
+            };
+            var sd = Lines2D._roundCapSubDiv;
+            var getCapSize = function (type, border) {
+                if (border === void 0) { border = false; }
+                // If no array given, we call this to get the size
+                var vbsize = 0, ibsize = 0;
+                switch (type) {
+                    case Lines2D.NoCap:
+                        // If the line is not close and we're computing border, we add the size to generate the edge border
+                        if (!_this.closed && border) {
+                            vbsize = 4;
+                            ibsize = 6;
+                        }
+                        else {
+                            vbsize = ibsize = 0;
+                        }
+                        break;
+                    case Lines2D.RoundCap:
+                        if (border) {
+                            vbsize = sd;
+                            ibsize = (sd - 2) * 3;
+                        }
+                        else {
+                            vbsize = (sd / 2) + 1;
+                            ibsize = (sd / 2) * 3;
+                        }
+                        break;
+                    case Lines2D.ArrowCap:
+                        if (border) {
+                            vbsize = 12;
+                            ibsize = 24;
+                        }
+                        else {
+                            vbsize = 3;
+                            ibsize = 3;
+                        }
+                        break;
+                    case Lines2D.TriangleCap:
+                        if (border) {
+                            vbsize = 6;
+                            ibsize = 12;
+                        }
+                        else {
+                            vbsize = 3;
+                            ibsize = 3;
+                        }
+                        break;
+                    case Lines2D.DiamondAnchorCap:
+                        if (border) {
+                            vbsize = 10;
+                            ibsize = 24;
+                        }
+                        else {
+                            vbsize = 5;
+                            ibsize = 9;
+                        }
+                        break;
+                    case Lines2D.SquareAnchorCap:
+                        if (border) {
+                            vbsize = 12;
+                            ibsize = 30;
+                        }
+                        else {
+                            vbsize = 4;
+                            ibsize = 6;
+                        }
+                        break;
+                    case Lines2D.RoundAnchorCap:
+                        if (border) {
+                            vbsize = sd * 2;
+                            ibsize = (sd - 1) * 6;
+                        }
+                        else {
+                            vbsize = sd + 1;
+                            ibsize = (sd + 1) * 3;
+                        }
+                        break;
+                }
+                return { vbsize: vbsize * 2, ibsize: ibsize };
+            };
+            var v = BABYLON.Vector2.Zero();
+            var storeVertex = function (vb, baseOffset, index, basePos, rotation, vertex) {
+                var c = Math.cos(rotation);
+                var s = Math.sin(rotation);
+                v.x = (c * vertex.x) + (-s * vertex.y) + basePos.x;
+                v.y = (s * vertex.x) + (c * vertex.y) + basePos.y;
+                var offset = baseOffset + (index * 2);
+                vb[offset + 0] = v.x;
+                vb[offset + 1] = v.y;
+                updateMinMax(vb, offset);
+                return (baseOffset + index * 2) / 2;
+            };
+            var storeIndex = function (ib, baseOffset, index, vertexIndex) {
+                ib[baseOffset + index] = vertexIndex;
+            };
+            var buildCap = function (vb, vbi, ib, ibi, pos, thickness, borderThickness, type, capDir) {
+                // Compute the transformation from the direction of the cap to build relative to our default orientation [1;0] (our cap are by default pointing toward right, horizontal
+                var dir = new BABYLON.Vector2(1, 0);
+                var angle = Math.atan2(capDir.y, capDir.x) - Math.atan2(dir.y, dir.x);
+                var ht = thickness / 2;
+                var t = thickness;
+                var borderMode = borderThickness != null;
+                var bt = borderThickness;
+                switch (type) {
+                    case Lines2D.NoCap:
+                        if (borderMode && !_this.closed) {
+                            var vi = 0;
+                            var ii = 0;
+                            var v1 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, ht + bt));
+                            var v2 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(bt, ht + bt));
+                            var v3 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(bt, -(ht + bt)));
+                            var v4 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -(ht + bt)));
+                            storeIndex(ib, ibi, ii++, v1);
+                            storeIndex(ib, ibi, ii++, v2);
+                            storeIndex(ib, ibi, ii++, v3);
+                            storeIndex(ib, ibi, ii++, v1);
+                            storeIndex(ib, ibi, ii++, v3);
+                            storeIndex(ib, ibi, ii++, v4);
+                        }
+                        break;
+                    case Lines2D.ArrowCap:
+                        ht *= 2;
+                    case Lines2D.TriangleCap:
+                        {
+                            if (borderMode) {
+                                var f = type === Lines2D.TriangleCap ? bt : Math.sqrt(bt * bt * 2);
+                                var v1 = storeVertex(vb, vbi, 0, pos, angle, new BABYLON.Vector2(0, ht));
+                                var v2 = storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(ht, 0));
+                                var v3 = storeVertex(vb, vbi, 2, pos, angle, new BABYLON.Vector2(0, -ht));
+                                var v4 = storeVertex(vb, vbi, 3, pos, angle, new BABYLON.Vector2(0, ht + f));
+                                var v5 = storeVertex(vb, vbi, 4, pos, angle, new BABYLON.Vector2(ht + f, 0));
+                                var v6 = storeVertex(vb, vbi, 5, pos, angle, new BABYLON.Vector2(0, -(ht + f)));
+                                var ii = 0;
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v5);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v5);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v5);
+                                if (type === Lines2D.ArrowCap) {
+                                    var rht = thickness / 2;
+                                    var v7 = storeVertex(vb, vbi, 6, pos, angle, new BABYLON.Vector2(0, rht + bt));
+                                    var v8 = storeVertex(vb, vbi, 7, pos, angle, new BABYLON.Vector2(-bt, rht + bt));
+                                    var v9 = storeVertex(vb, vbi, 8, pos, angle, new BABYLON.Vector2(-bt, ht + f));
+                                    var v10 = storeVertex(vb, vbi, 9, pos, angle, new BABYLON.Vector2(0, -(rht + bt)));
+                                    var v11 = storeVertex(vb, vbi, 10, pos, angle, new BABYLON.Vector2(-bt, -(rht + bt)));
+                                    var v12 = storeVertex(vb, vbi, 11, pos, angle, new BABYLON.Vector2(-bt, -(ht + f)));
+                                    storeIndex(ib, ibi, ii++, v7);
+                                    storeIndex(ib, ibi, ii++, v8);
+                                    storeIndex(ib, ibi, ii++, v9);
+                                    storeIndex(ib, ibi, ii++, v7);
+                                    storeIndex(ib, ibi, ii++, v9);
+                                    storeIndex(ib, ibi, ii++, v4);
+                                    storeIndex(ib, ibi, ii++, v10);
+                                    storeIndex(ib, ibi, ii++, v12);
+                                    storeIndex(ib, ibi, ii++, v11);
+                                    storeIndex(ib, ibi, ii++, v10);
+                                    storeIndex(ib, ibi, ii++, v6);
+                                    storeIndex(ib, ibi, ii++, v12);
+                                }
+                            }
+                            else {
+                                var v1 = storeVertex(vb, vbi, 0, pos, angle, new BABYLON.Vector2(0, ht));
+                                var v2 = storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(ht, 0));
+                                var v3 = storeVertex(vb, vbi, 2, pos, angle, new BABYLON.Vector2(0, -ht));
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
+                            }
+                            break;
+                        }
+                    case Lines2D.RoundCap:
+                        {
+                            if (borderMode) {
+                                var curA = -Math.PI / 2;
+                                var incA = Math.PI / (sd / 2 - 1);
+                                var ii = 0;
+                                for (var i = 0; i < (sd / 2); i++) {
+                                    var v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new BABYLON.Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                    var v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new BABYLON.Vector2(Math.cos(curA) * (ht + bt), Math.sin(curA) * (ht + bt)));
+                                    if (i > 0) {
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1);
+                                    }
+                                    curA += incA;
+                                }
+                            }
+                            else {
+                                var c = storeVertex(vb, vbi, 0, pos, angle, new BABYLON.Vector2(0, 0));
+                                var curA = -Math.PI / 2;
+                                var incA = Math.PI / (sd / 2 - 1);
+                                storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                curA += incA;
+                                for (var i = 1; i < (sd / 2); i++) {
+                                    var v2 = storeVertex(vb, vbi, i + 1, pos, angle, new BABYLON.Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                    storeIndex(ib, ibi, i * 3 + 0, c);
+                                    storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                    storeIndex(ib, ibi, i * 3 + 2, v2);
+                                    curA += incA;
+                                }
+                            }
+                            break;
+                        }
+                    case Lines2D.SquareAnchorCap:
+                        {
+                            var vi = 0;
+                            var v1 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, t));
+                            var v2 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2, t));
+                            var v3 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2, -t));
+                            var v4 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -t));
+                            if (borderMode) {
+                                var v5 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, ht + bt));
+                                var v6 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, ht + bt));
+                                var v7 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, t + bt));
+                                var v8 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2 + bt, t + bt));
+                                var v9 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2 + bt, -(t + bt)));
+                                var v10 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, -(t + bt)));
+                                var v11 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, -(ht + bt)));
+                                var v12 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -(ht + bt)));
+                                var ii = 0;
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v5);
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v11);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v11);
+                                storeIndex(ib, ibi, ii++, v12);
+                                storeIndex(ib, ibi, ii++, v4);
+                            }
+                            else {
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
+                                storeIndex(ib, ibi, 3, v1);
+                                storeIndex(ib, ibi, 4, v3);
+                                storeIndex(ib, ibi, 5, v4);
+                            }
+                            break;
+                        }
+                    case Lines2D.RoundAnchorCap:
+                        {
+                            var cpos = Math.sqrt(t * t - ht * ht);
+                            var center = new BABYLON.Vector2(cpos, 0);
+                            var curA = BABYLON.Tools.ToRadians(-150);
+                            var incA = BABYLON.Tools.ToRadians(300) / (sd - 1);
+                            if (borderMode) {
+                                var ii = 0;
+                                for (var i = 0; i < sd; i++) {
+                                    var v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                    var v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * (t + bt), Math.sin(curA) * (t + bt)));
+                                    if (i > 0) {
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1);
+                                    }
+                                    curA += incA;
+                                }
+                            }
+                            else {
+                                var c = storeVertex(vb, vbi, 0, pos, angle, center);
+                                storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                curA += incA;
+                                for (var i = 1; i < sd; i++) {
+                                    var v2 = storeVertex(vb, vbi, i + 1, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                    storeIndex(ib, ibi, i * 3 + 0, c);
+                                    storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                    storeIndex(ib, ibi, i * 3 + 2, v2);
+                                    curA += incA;
+                                }
+                                storeIndex(ib, ibi, sd * 3 + 0, c);
+                                storeIndex(ib, ibi, sd * 3 + 1, c + 1);
+                                storeIndex(ib, ibi, sd * 3 + 2, c + sd);
+                            }
+                            break;
+                        }
+                    case Lines2D.DiamondAnchorCap:
+                        {
+                            var vi = 0;
+                            var v1 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, ht));
+                            var v2 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, t));
+                            var v3 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht * 3, 0));
+                            var v4 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, -t));
+                            var v5 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -ht));
+                            if (borderMode) {
+                                var f = Math.sqrt(bt * bt * 2);
+                                var v6 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-f, ht));
+                                var v7 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, t + f));
+                                var v8 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht * 3 + f, 0));
+                                var v9 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, -(t + f)));
+                                var v10 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-f, -ht));
+                                var ii = 0;
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v5);
+                            }
+                            else {
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
+                                storeIndex(ib, ibi, 3, v1);
+                                storeIndex(ib, ibi, 4, v3);
+                                storeIndex(ib, ibi, 5, v5);
+                                storeIndex(ib, ibi, 6, v5);
+                                storeIndex(ib, ibi, 7, v3);
+                                storeIndex(ib, ibi, 8, v4);
+                            }
+                            break;
+                        }
+                }
+                return null;
+            };
+            var buildLine = function (vb, contour, ht, bt) {
+                var lineA = BABYLON.Vector2.Zero();
+                var lineB = BABYLON.Vector2.Zero();
+                var tangent = BABYLON.Vector2.Zero();
+                var miter = BABYLON.Vector2.Zero();
+                var curNormal = null;
+                if (_this.closed) {
+                    _this.points.push(_this.points[0]);
+                }
+                var total = _this.points.length;
+                for (var i = 1; i < total; i++) {
+                    var last = _this.points[i - 1];
+                    var cur = _this.points[i];
+                    var next = (i < (_this.points.length - 1)) ? _this.points[i + 1] : null;
+                    direction(cur, last, lineA);
+                    if (!curNormal) {
+                        curNormal = BABYLON.Vector2.Zero();
+                        perp(lineA, curNormal);
+                    }
+                    if (i === 1) {
+                        store(vb, contour, 0, total, _this.points[0], curNormal, ht, bt);
+                    }
+                    if (!next) {
+                        perp(lineA, curNormal);
+                        store(vb, contour, i, total, _this.points[i], curNormal, ht, bt, i - 1);
+                    }
+                    else {
+                        direction(next, cur, lineB);
+                        var miterLen = computeMiter(tangent, miter, lineA, lineB);
+                        store(vb, contour, i, total, _this.points[i], miter, miterLen * ht, miterLen * bt, i - 1);
+                    }
+                }
+                if (_this.points.length > 2 && _this.closed) {
+                    var last2 = _this.points[total - 2];
+                    var cur2 = _this.points[0];
+                    var next2 = _this.points[1];
+                    direction(cur2, last2, lineA);
+                    direction(next2, cur2, lineB);
+                    perp(lineA, curNormal);
+                    var miterLen2 = computeMiter(tangent, miter, lineA, lineB);
+                    store(vb, null, 0, total, _this.points[0], miter, miterLen2 * ht, miterLen2 * bt, 1);
+                    // Patch contour
+                    if (contour) {
+                        var off = (bt == null) ? 0 : 4;
+                        contour[0].x = vb[off + 0];
+                        contour[0].y = vb[off + 1];
+                        contour[1].x = vb[off + 2];
+                        contour[1].y = vb[off + 3];
+                    }
+                }
+                // Remove the point we added at the beginning
+                if (_this.closed) {
+                    _this.points.splice(total - 1);
+                }
+            };
+            var contour = new Array();
+            // Need to create WebGL resources for fill part?
+            if (this.fill) {
+                var startCapInfo = getCapSize(this.startCap);
+                var endCapInfo = getCapSize(this.endCap);
+                var count = this.points.length;
+                var vbSize = (count * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
+                var vb = new Float32Array(vbSize);
+                var ht = this.fillThickness / 2;
+                var total = this.points.length;
+                buildLine(vb, this.border ? null : contour, ht);
+                var max = total * 2;
+                var triCount = (count - (this.closed ? 0 : 1)) * 2;
+                var ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
+                for (var i = 0; i < triCount; i += 2) {
+                    ib[i * 3 + 0] = i + 0;
+                    ib[i * 3 + 1] = i + 1;
+                    ib[i * 3 + 2] = (i + 2) % max;
+                    ib[i * 3 + 3] = i + 1;
+                    ib[i * 3 + 4] = (i + 3) % max;
+                    ib[i * 3 + 5] = (i + 2) % max;
+                }
+                buildCap(vb, count * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, null, this.startCap, startDir);
+                buildCap(vb, (count * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, null, this.endCap, endDir);
+                renderCache.fillVB = engine.createVertexBuffer(vb);
+                renderCache.fillIB = engine.createIndexBuffer(ib);
+                renderCache.fillIndicesCount = ib.length;
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["position"]);
+                renderCache.effectFill = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            // Need to create WebGL resources for border part?
+            if (this.border) {
+                var startCapInfo = getCapSize(this.startCap, true);
+                var endCapInfo = getCapSize(this.endCap, true);
+                var count = this.points.length;
+                var vbSize = (count * 2 * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
+                var vb = new Float32Array(vbSize);
+                var ht = this.fillThickness / 2;
+                var bt = this.borderThickness;
+                var total = this.points.length;
+                buildLine(vb, contour, ht, bt);
+                var max = total * 2 * 2;
+                var triCount = (count - (this.closed ? 0 : 1)) * 2 * 2;
+                var ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
+                for (var i = 0; i < triCount; i += 4) {
+                    ib[i * 3 + 0] = i + 0;
+                    ib[i * 3 + 1] = i + 2;
+                    ib[i * 3 + 2] = (i + 6) % max;
+                    ib[i * 3 + 3] = i + 0;
+                    ib[i * 3 + 4] = (i + 6) % max;
+                    ib[i * 3 + 5] = (i + 4) % max;
+                    ib[i * 3 + 6] = i + 3;
+                    ib[i * 3 + 7] = i + 1;
+                    ib[i * 3 + 8] = (i + 5) % max;
+                    ib[i * 3 + 9] = i + 3;
+                    ib[i * 3 + 10] = (i + 5) % max;
+                    ib[i * 3 + 11] = (i + 7) % max;
+                }
+                buildCap(vb, count * 2 * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, this.borderThickness, this.startCap, startDir);
+                buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, endDir);
+                renderCache.borderVB = engine.createVertexBuffer(vb);
+                renderCache.borderIB = engine.createIndexBuffer(ib);
+                renderCache.borderIndicesCount = ib.length;
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["position"]);
+                renderCache.effectBorder = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            this._contour = contour;
+            var bs = this._boundingMax.subtract(this._boundingMin);
+            this._size.width = bs.x;
+            this._size.height = bs.y;
+            return renderCache;
+        };
+        Lines2D.prototype.createInstanceDataParts = function () {
+            var res = new Array();
+            if (this.border) {
+                res.push(new Lines2DInstanceData(BABYLON.Shape2D.SHAPE2D_BORDERPARTID));
+            }
+            if (this.fill) {
+                res.push(new Lines2DInstanceData(BABYLON.Shape2D.SHAPE2D_FILLPARTID));
+            }
+            return res;
+        };
+        Lines2D.prototype.refreshInstanceDataPart = function (part) {
+            if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
+                return false;
+            }
+            if (part.id === BABYLON.Shape2D.SHAPE2D_BORDERPARTID) {
+                var d = part;
+                d.boundingMin = this.boundingMin;
+                d.boundingMax = this.boundingMax;
+            }
+            else if (part.id === BABYLON.Shape2D.SHAPE2D_FILLPARTID) {
+                var d = part;
+                d.boundingMin = this.boundingMin;
+                d.boundingMax = this.boundingMax;
+            }
+            return true;
+        };
+        Lines2D._noCap = 0;
+        Lines2D._roundCap = 1;
+        Lines2D._triangleCap = 2;
+        Lines2D._squareAnchorCap = 3;
+        Lines2D._roundAnchorCap = 4;
+        Lines2D._diamondAnchorCap = 5;
+        Lines2D._arrowCap = 6;
+        Lines2D._roundCapSubDiv = 36;
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Lines2D.pointsProperty = pi; })
+        ], Lines2D.prototype, "points", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Lines2D.fillThicknessProperty = pi; })
+        ], Lines2D.prototype, "fillThickness", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 3, function (pi) { return Lines2D.closedProperty = pi; })
+        ], Lines2D.prototype, "closed", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 4, function (pi) { return Lines2D.startCapProperty = pi; })
+        ], Lines2D.prototype, "startCap", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 5, function (pi) { return Lines2D.endCapProperty = pi; })
+        ], Lines2D.prototype, "endCap", null);
+        Lines2D = __decorate([
+            BABYLON.className("Lines2D")
+        ], Lines2D);
+        return Lines2D;
+    }(BABYLON.Shape2D));
+    BABYLON.Lines2D = Lines2D;
+})(BABYLON || (BABYLON = {}));

+ 973 - 0
src/Canvas2d/babylon.lines2d.ts

@@ -0,0 +1,973 @@
+module BABYLON {
+    export class Lines2DRenderCache extends ModelRenderCache {
+        fillVB: WebGLBuffer;
+        fillIB: WebGLBuffer;
+        fillIndicesCount: number;
+        instancingFillAttributes: InstancingAttributeInfo[];
+        effectFill: Effect;
+
+        borderVB: WebGLBuffer;
+        borderIB: WebGLBuffer;
+        borderIndicesCount: number;
+        instancingBorderAttributes: InstancingAttributeInfo[];
+        effectBorder: Effect;
+
+        constructor(engine: Engine, modelKey: string, isTransparent: boolean) {
+            super(engine, modelKey, isTransparent);
+        }
+
+        render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
+            // Do nothing if the shader is still loading/preparing 
+            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
+                return false;
+            }
+
+            var engine = instanceInfo._owner.owner.engine;
+
+            let depthFunction = 0;
+            if (this.effectFill && this.effectBorder) {
+                depthFunction = engine.getDepthFunction();
+                engine.setDepthFunctionToLessOrEqual();
+            }
+
+            var cur: number;
+            if (this.isTransparent) {
+                cur = engine.getAlphaMode();
+                engine.setAlphaMode(Engine.ALPHA_COMBINE);
+            }
+
+            if (this.effectFill) {
+                let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
+             
+                engine.enableEffect(this.effectFill);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [2], 2*4, this.effectFill);
+                let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingFillAttributes) {
+                        // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+                        this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
+                    }
+
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                } else {
+                    for (let i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.fillIndicesCount);                        
+                    }
+                }
+            }
+
+            if (this.effectBorder) {
+                let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_BORDERPARTID.toString());
+
+                engine.enableEffect(this.effectBorder);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [2], 2 * 4, this.effectBorder);
+                let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingBorderAttributes) {
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                    }
+
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.unbindInstanceAttributes();
+                } else {
+                    for (let i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.borderIndicesCount);
+                    }
+                }
+            }
+
+            if (this.isTransparent) {
+                engine.setAlphaMode(cur);
+            }
+
+            if (this.effectFill && this.effectBorder) {
+                engine.setDepthFunction(depthFunction);
+            }
+            return true;
+        }
+
+        public dispose(): boolean {
+            if (!super.dispose()) {
+                return false;
+            }
+
+            if (this.fillVB) {
+                this._engine._releaseBuffer(this.fillVB);
+                this.fillVB = null;
+            }
+
+            if (this.fillIB) {
+                this._engine._releaseBuffer(this.fillIB);
+                this.fillIB = null;
+            }
+
+            if (this.effectFill) {
+                this._engine._releaseEffect(this.effectFill);
+                this.effectFill = null;
+            }
+
+            if (this.borderVB) {
+                this._engine._releaseBuffer(this.borderVB);
+                this.borderVB = null;
+            }
+
+            if (this.borderIB) {
+                this._engine._releaseBuffer(this.borderIB);
+                this.borderIB = null;
+            }
+
+            if (this.effectBorder) {
+                this._engine._releaseEffect(this.effectBorder);
+                this.effectBorder = null;
+            }
+
+            return true;
+        }
+    }
+
+    export class Lines2DInstanceData extends Shape2DInstanceData {
+        constructor(partId: number) {
+            super(partId, 1);
+        }
+
+        @instanceData()
+        get boundingMin(): Vector2 {
+            return null;
+        }
+
+        @instanceData()
+        get boundingMax(): Vector2 {
+            return null;
+        }
+    }
+
+    @className("Lines2D")
+    export class Lines2D extends Shape2D {
+        static get NoCap            () { return Lines2D._noCap;            }
+        static get RoundCap         () { return Lines2D._roundCap;         }
+        static get TriangleCap      () { return Lines2D._triangleCap;      }
+        static get SquareAnchorCap  () { return Lines2D._squareAnchorCap;  }
+        static get RoundAnchorCap   () { return Lines2D._roundAnchorCap;   }
+        static get DiamondAnchorCap () { return Lines2D._diamondAnchorCap; }
+        static get ArrowCap         () { return Lines2D._arrowCap;         }
+
+        public static pointsProperty: Prim2DPropInfo;
+        public static fillThicknessProperty: Prim2DPropInfo;
+        public static closedProperty: Prim2DPropInfo;
+        public static startCapProperty: Prim2DPropInfo;
+        public static endCapProperty: Prim2DPropInfo;
+
+        public get actualSize(): Size {
+            return this.size;
+        }
+
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 1, pi => Lines2D.pointsProperty = pi)
+        public get points(): Vector2[] {
+            return this._points;
+        }
+
+        public set points(value: Vector2[]) {
+            this._points = value;
+            this._levelBoundingInfoDirty = true;
+        }
+
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Lines2D.fillThicknessProperty = pi)
+        public get fillThickness(): number {
+            return this._fillThickness;
+        }
+
+        public set fillThickness(value: number) {
+            this._fillThickness = value;
+        }
+
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 3, pi => Lines2D.closedProperty = pi)
+        public get closed(): boolean {
+            return this._closed;
+        }
+
+        public set closed(value: boolean) {
+            this._closed = value;
+        }
+
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 4, pi => Lines2D.startCapProperty = pi)
+        public get startCap(): number {
+            return this._startCap;
+        }
+
+        public set startCap(value: number) {
+            this._startCap = value;
+        }
+
+        @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 5, pi => Lines2D.endCapProperty = pi)
+        public get endCap(): number {
+            return this._endCap;
+        }
+
+        public set endCap(value: number) {
+            this._endCap = value;
+        }
+
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            let pl = this.points.length;
+            let l = this.closed ? pl + 1 : pl;
+
+            let originOffset = new Vector2(-0.5, -0.5);
+            let p = intersectInfo._localPickPosition;
+
+            let prevA = this.transformPointWithOrigin(this._contour[0], originOffset);
+            let prevB = this.transformPointWithOrigin(this._contour[1], originOffset);
+            for (let i = 1; i < l; i++) {
+                let curA = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 0], originOffset);
+                let curB = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 1], originOffset);
+
+                if (Vector2.PointInTriangle(p, prevA, prevB, curA)) {
+                    return true;
+                }
+                if (Vector2.PointInTriangle(p, curA, prevB, curB)) {
+                    return true;
+                }
+
+                prevA = curA;
+                prevB = curB;
+            }
+            return false;
+        }
+
+        protected get size(): Size {
+            return this._size;
+        }
+
+        protected get boundingMin(): Vector2 {
+            return this._boundingMin;
+        }
+
+        protected get boundingMax(): Vector2 {
+            return this._boundingMax;
+        }
+
+        protected getUsedShaderCategories(dataPart: InstanceDataBase): string[] {
+            let res = super.getUsedShaderCategories(dataPart);
+
+            // Remove the BORDER category, we don't use it in the VertexShader
+            let i = res.indexOf(Shape2D.SHAPE2D_CATEGORY_BORDER);
+            if (i !== -1) {
+                res.splice(i, 1);
+            }
+            return res;
+        }
+
+        protected updateLevelBoundingInfo() {
+            BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+        }
+
+        protected setupLines2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, points: Vector2[], fillThickness: number, startCap: number, endCap: number, fill: IBrush2D, border: IBrush2D, borderThickness: number, closed: boolean) {
+            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+            this.fillThickness = fillThickness;
+            this.startCap = startCap;
+            this.endCap = endCap;
+            this.points = points;
+            this.closed = closed;
+            this._size = Size.Zero();
+            this._boundingMin = Vector2.Zero();
+            this._boundingMax = Vector2.Zero();
+        }
+
+        /**
+         * Create an 2D Lines Shape primitive. The defined lines may be opened or closed (see below)
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * @param points an array that describe the points to use to draw the line, must contain at least two entries.
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - fillThickness: the thickness of the fill part of the line, can be null to draw nothing (but a border brush must be given), default is 1.
+         *  - closed: if false the lines are said to be opened, the first point and the latest DON'T connect. if true the lines are said to be closed, the first and last point will be connected by a line. For instance you can define the 4 points of a rectangle, if you set closed to true a 4 edges rectangle will be drawn. If you set false, only three edges will be drawn, the edge formed by the first and last point won't exist. Default is false.
+         *  - Draw a cap of the given type at the start of the first line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
+         *  - Draw a cap of the given type at the end of the last line, you can't define a Cap if the Lines2D is closed. Default is Lines2D.NoCap.
+         *  - fill: the brush used to draw the fill content of the lines, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
+         *  - border: the brush used to draw the border of the lines, you can set null to draw nothing (but you will have to set a fill brush), default is null.
+         *  - borderThickness: the thickness of the drawn border, default is 1.
+         */
+        public static Create(parent: Prim2DBase, points: Vector2[], options: { id?: string, x?: number, y?: number, origin?: Vector2, fillThickness?: number, closed?: boolean, startCap?: number, endCap?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number }): Lines2D {
+            Prim2DBase.CheckParent(parent);
+
+            let fill: IBrush2D;
+            if (options && options.fill !== undefined) {
+                fill = options.fill;
+            } else {
+                fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+            }
+
+            let lines = new Lines2D();
+            lines.setupLines2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, points, options && options.fillThickness || 1, options && options.startCap || 0, options && options.endCap || 0, fill, options && options.border || null, options && options.borderThickness || 0, options && options.closed || false);
+            return lines;
+        }
+
+        protected createModelRenderCache(modelKey: string, isTransparent: boolean): ModelRenderCache {
+            let renderCache = new Lines2DRenderCache(this.owner.engine, modelKey, isTransparent);
+            return renderCache;
+        }
+
+        protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
+            let renderCache = <Lines2DRenderCache>modelRenderCache;
+            let engine = this.owner.engine;
+
+            // Init min/max because their being computed here
+            this.boundingMin = new Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
+            this.boundingMax = new Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
+
+            let perp = (v: Vector2, res: Vector2) => {
+                res.x = v.y;
+                res.y = -v.x;
+            };
+
+            let direction = (a: Vector2, b: Vector2, res: Vector2) => {
+                a.subtractToRef(b, res);
+                res.normalize();
+            }
+
+            let tps = Vector2.Zero();
+            let computeMiter = (tangent: Vector2, miter: Vector2, a: Vector2, b: Vector2): number => {
+                a.addToRef(b, tangent);
+                tangent.normalize();
+
+                miter.x = -tangent.y;
+                miter.y = tangent.x;
+
+                tps.x = -a.y;
+                tps.y = a.x;
+
+                return 1 / Vector2.Dot(miter, tps);
+            }
+
+            let intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): boolean => {
+                let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+                if (d === 0) return false;
+
+                let xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;   // Intersection point is xi/yi, just in case...
+                //let yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; // That's why I left it commented
+
+                if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)) return false;
+                if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4)) return false;
+                return true;
+            }
+
+            let startDir: Vector2 = Vector2.Zero();
+            let endDir: Vector2 = Vector2.Zero();
+
+            let updateMinMax = (array: Float32Array, offset: number) => {
+                if (offset >= array.length) {
+                    return;
+                }
+                this._boundingMin.x = Math.min(this._boundingMin.x, array[offset]);
+                this._boundingMax.x = Math.max(this._boundingMax.x, array[offset]);
+                this._boundingMin.y = Math.min(this._boundingMin.y, array[offset+1]);
+                this._boundingMax.y = Math.max(this._boundingMax.y, array[offset+1]);
+            }
+
+            let store = (array: Float32Array, contour: Vector2[], index: number, max: number, p: Vector2, n: Vector2, halfThickness: number, borderThickness: number, detectFlip?: number) => {
+                let borderMode = borderThickness != null && !isNaN(borderThickness);
+                let off = index * (borderMode ? 8 : 4);
+
+                // Mandatory because we'll be out of bound in case of closed line, for the very last point (which is a duplicate of the first that we don't store in the vb)
+                if (off >= array.length) {
+                    return;
+                }
+
+                // Store start/end normal, we need it for the cap construction
+                if (index === 0) {
+                    perp(n, startDir);
+                } else if (index === max - 1) {
+                    perp(n, endDir);
+                    endDir.x *= -1;
+                    endDir.y *= -1;
+                }
+
+                let swap = false;
+
+                array[off + 0] = p.x + n.x * halfThickness;
+                array[off + 1] = p.y + n.y * halfThickness;
+                array[off + 2] = p.x + n.x * -halfThickness;
+                array[off + 3] = p.y + n.y * -halfThickness;
+
+                updateMinMax(array, off);
+                updateMinMax(array, off + 2);
+
+                // If an index is given we check if the two segments formed between [index+0;detectFlip+0] and [index+2;detectFlip+2] intersect themselves.
+                // It should not be the case, they should be parallel, so if they cross, we switch the order of storage to ensure we'll have parallel lines
+                if (detectFlip !== undefined) {
+                    // Flip if intersect
+                    let flipOff = detectFlip * (borderMode ? 8 : 4);
+                    if (intersect(array[off + 0], array[off + 1], array[flipOff + 0], array[flipOff + 1], array[off + 2], array[off + 3], array[flipOff + 2], array[flipOff + 3])) {
+                        swap = true;
+                        let tps = array[off + 0];
+                        array[off + 0] = array[off + 2];
+                        array[off + 2] = tps;
+
+                        tps = array[off + 1];
+                        array[off + 1] = array[off + 3];
+                        array[off + 3] = tps;
+                    }
+                }
+
+                if (borderMode) {
+                    let t = halfThickness + borderThickness;
+                    array[off + 4] = p.x + n.x * (swap ? -t : t);
+                    array[off + 5] = p.y + n.y * (swap ? -t : t);
+                    array[off + 6] = p.x + n.x * (swap ? t : -t);
+                    array[off + 7] = p.y + n.y * (swap ? t : -t);
+
+                    updateMinMax(array, off + 4);
+                    updateMinMax(array, off + 6);
+                }
+
+                if (contour) {
+                    off += borderMode ? 4 : 0;
+                    contour.push(new Vector2(array[off + 0], array[off + 1]));
+                    contour.push(new Vector2(array[off + 2], array[off + 3]));
+                }
+            }
+
+            let sd = Lines2D._roundCapSubDiv;
+            let getCapSize = (type: number, border: boolean = false): { vbsize: number; ibsize: number } => {
+                // If no array given, we call this to get the size
+                let vbsize: number = 0, ibsize: number = 0;
+                switch (type) {
+                    case Lines2D.NoCap:
+                        // If the line is not close and we're computing border, we add the size to generate the edge border
+                        if (!this.closed && border) {
+                            vbsize = 4;
+                            ibsize = 6;
+                        } else {
+                            vbsize = ibsize = 0;
+                        }
+                        break;
+                    case Lines2D.RoundCap:
+                        if (border) {
+                            vbsize = sd;
+                            ibsize = (sd-2) * 3;
+                        } else {
+                            vbsize = (sd / 2) + 1;
+                            ibsize = (sd / 2) * 3;
+                        }
+                        break;
+                    case Lines2D.ArrowCap:
+                        if (border) {
+                            vbsize = 12;
+                            ibsize = 24;
+                        } else {
+                            vbsize = 3;
+                            ibsize = 3;
+                        }
+                        break;
+                    case Lines2D.TriangleCap:
+                        if (border) {
+                            vbsize = 6;
+                            ibsize = 12;
+                        } else {
+                            vbsize = 3;
+                            ibsize = 3;
+                        }
+                        break;
+                    case Lines2D.DiamondAnchorCap:
+                        if (border) {
+                            vbsize = 10;
+                            ibsize = 24;
+                        } else {
+                            vbsize = 5;
+                            ibsize = 9;
+                        }
+                        break;
+                    case Lines2D.SquareAnchorCap:
+                        if (border) {
+                            vbsize = 12;
+                            ibsize = 30;
+                        } else {
+                            vbsize = 4;
+                            ibsize = 6;
+                        }
+                        break;
+                    case Lines2D.RoundAnchorCap:
+                        if (border) {
+                            vbsize = sd*2;
+                            ibsize = (sd - 1) * 6;
+                        } else {
+                            vbsize = sd + 1;
+                            ibsize = (sd + 1) * 3;
+                        }
+                        break;
+                }
+
+                return { vbsize: vbsize*2, ibsize: ibsize };
+            }
+
+            let v = Vector2.Zero();
+            let storeVertex = (vb: Float32Array, baseOffset: number, index: number, basePos: Vector2, rotation: number, vertex: Vector2): number => {
+                let c = Math.cos(rotation);
+                let s = Math.sin(rotation);
+
+                v.x = (c * vertex.x) + (-s * vertex.y) + basePos.x;
+                v.y = (s * vertex.x) + ( c * vertex.y) + basePos.y;
+                let offset = baseOffset + (index*2);
+                vb[offset + 0] = v.x;
+                vb[offset + 1] = v.y;
+
+                updateMinMax(vb, offset);
+                return (baseOffset + index*2) / 2;
+            }
+
+            let storeIndex = (ib: Float32Array, baseOffset: number, index: number, vertexIndex: number) => {
+                ib[baseOffset + index] = vertexIndex;
+            }
+
+            let buildCap = (vb: Float32Array, vbi: number, ib: Float32Array, ibi: number, pos: Vector2, thickness: number, borderThickness: number, type: number, capDir: Vector2): { vbsize: number; ibsize: number } => {
+
+                // Compute the transformation from the direction of the cap to build relative to our default orientation [1;0] (our cap are by default pointing toward right, horizontal
+                let dir = new Vector2(1, 0);
+                let angle = Math.atan2(capDir.y, capDir.x) - Math.atan2(dir.y, dir.x);
+
+                let ht = thickness / 2;
+                let t = thickness;
+                let borderMode = borderThickness != null;
+                let bt = borderThickness;
+                switch (type) {
+                    case Lines2D.NoCap:
+                        if (borderMode && !this.closed) {
+                            let vi = 0;
+                            let ii = 0;
+                            let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht + bt));
+                            let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, ht + bt));
+                            let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, -(ht + bt)));
+                            let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht + bt)));
+
+                            storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v3);
+                            storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v4);
+                        }
+                        break;
+                    case Lines2D.ArrowCap:
+                        ht *= 2;
+                    case Lines2D.TriangleCap:
+                    {
+                        if (borderMode) {
+                            let f = type===Lines2D.TriangleCap ? bt : Math.sqrt(bt * bt * 2);
+                            let v1 = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, ht));
+                            let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0));
+                            let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht));
+                            let v4 = storeVertex(vb, vbi, 3, pos, angle, new Vector2(0, ht+f));
+                            let v5 = storeVertex(vb, vbi, 4, pos, angle, new Vector2(ht+f, 0));
+                            let v6 = storeVertex(vb, vbi, 5, pos, angle, new Vector2(0, -(ht+f)));
+
+                            let ii = 0;
+                            storeIndex(ib, ibi, ii++, v1);  storeIndex(ib, ibi, ii++, v4);  storeIndex(ib, ibi, ii++, v5);
+                            storeIndex(ib, ibi, ii++, v1);  storeIndex(ib, ibi, ii++, v5);  storeIndex(ib, ibi, ii++, v2);
+                            storeIndex(ib, ibi, ii++, v6);  storeIndex(ib, ibi, ii++, v3);  storeIndex(ib, ibi, ii++, v2);
+                            storeIndex(ib, ibi, ii++, v6);  storeIndex(ib, ibi, ii++, v2);  storeIndex(ib, ibi, ii++, v5);
+
+                            if (type === Lines2D.ArrowCap) {
+                                let rht = thickness / 2;
+                                let v7 = storeVertex(vb, vbi, 6, pos, angle, new Vector2(0, rht+bt));
+                                let v8 = storeVertex(vb, vbi, 7, pos, angle, new Vector2(-bt, rht+bt));
+                                let v9 = storeVertex(vb, vbi, 8, pos, angle, new Vector2(-bt, ht+f));
+
+                                let v10 = storeVertex(vb, vbi, 9, pos, angle, new Vector2(0, -(rht+bt)));
+                                let v11 = storeVertex(vb, vbi, 10, pos, angle, new Vector2(-bt, -(rht+bt)));
+                                let v12 = storeVertex(vb, vbi, 11, pos, angle, new Vector2(-bt, -(ht+f)));
+
+                                storeIndex(ib, ibi, ii++, v7);  storeIndex(ib, ibi, ii++, v8);  storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v7);  storeIndex(ib, ibi, ii++, v9);  storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v12); storeIndex(ib, ibi, ii++, v11);
+                                storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v6);  storeIndex(ib, ibi, ii++, v12);
+                            }
+                        } else {
+                            let v1 = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, ht));
+                            let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0));
+                            let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht));
+
+                            storeIndex(ib, ibi, 0, v1);
+                            storeIndex(ib, ibi, 1, v2);
+                            storeIndex(ib, ibi, 2, v3);
+                        }
+                        break;
+                    }
+                    case Lines2D.RoundCap:
+                    {
+                        if (borderMode) {
+                            let curA = -Math.PI / 2;
+                            let incA = Math.PI / (sd / 2 - 1);
+                            let ii = 0;
+
+                            for (let i = 0; i < (sd / 2); i++) {
+                                let v1 = storeVertex(vb, vbi, i*2 + 0, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                let v2 = storeVertex(vb, vbi, i*2 + 1, pos, angle, new Vector2(Math.cos(curA) * (ht+bt), Math.sin(curA) * (ht+bt)));
+
+                                if (i > 0) {
+                                    storeIndex(ib, ibi, ii++, v1 - 2);
+                                    storeIndex(ib, ibi, ii++, v2 - 2);
+                                    storeIndex(ib, ibi, ii++, v2);
+
+                                    storeIndex(ib, ibi, ii++, v1 - 2);
+                                    storeIndex(ib, ibi, ii++, v2);
+                                    storeIndex(ib, ibi, ii++, v1);
+                                }
+                                curA += incA;
+                            }
+                        } else {
+                            let c = storeVertex(vb, vbi, 0, pos, angle, new Vector2(0, 0));
+                            let curA = -Math.PI / 2;
+                            let incA = Math.PI / (sd / 2 - 1);
+
+                            storeVertex(vb, vbi, 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                            curA += incA;
+                            for (let i = 1; i < (sd / 2); i++) {
+                                let v2 = storeVertex(vb, vbi, i + 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+
+                                storeIndex(ib, ibi, i * 3 + 0, c);
+                                storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                storeIndex(ib, ibi, i * 3 + 2, v2);
+                                curA += incA;
+                            }
+                        }
+                        break;
+                    }
+                    case Lines2D.SquareAnchorCap:
+                    {
+                        let vi = 0;
+                        let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, t));
+                        let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, t));
+                        let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, -t));
+                        let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -t));
+
+                        if (borderMode) {
+                            let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht+bt));
+                            let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, ht+bt));
+                            let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, t + bt));
+                            let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, t + bt));
+
+                            let v9 =  storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, -(t+bt)));
+                            let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(t + bt)));
+                            let v11 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(ht+bt)));
+                            let v12 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht+bt)));
+
+                            let ii = 0;
+                            storeIndex(ib, ibi, ii++, v6);  storeIndex(ib, ibi, ii++, v1);  storeIndex(ib, ibi, ii++, v5);
+                            storeIndex(ib, ibi, ii++, v6);  storeIndex(ib, ibi, ii++, v7);  storeIndex(ib, ibi, ii++, v1);
+                            storeIndex(ib, ibi, ii++, v1);  storeIndex(ib, ibi, ii++, v7);  storeIndex(ib, ibi, ii++, v8);
+                            storeIndex(ib, ibi, ii++, v1);  storeIndex(ib, ibi, ii++, v8);  storeIndex(ib, ibi, ii++, v2);
+                            storeIndex(ib, ibi, ii++, v2);  storeIndex(ib, ibi, ii++, v8);  storeIndex(ib, ibi, ii++, v9);
+                            storeIndex(ib, ibi, ii++, v2);  storeIndex(ib, ibi, ii++, v9);  storeIndex(ib, ibi, ii++, v3);
+                            storeIndex(ib, ibi, ii++, v3);  storeIndex(ib, ibi, ii++, v9);  storeIndex(ib, ibi, ii++, v10);
+                            storeIndex(ib, ibi, ii++, v3);  storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v4);
+                            storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v11); storeIndex(ib, ibi, ii++, v4);
+                            storeIndex(ib, ibi, ii++, v11); storeIndex(ib, ibi, ii++, v12); storeIndex(ib, ibi, ii++, v4);
+
+                        } else {
+                            storeIndex(ib, ibi, 0, v1);
+                            storeIndex(ib, ibi, 1, v2);
+                            storeIndex(ib, ibi, 2, v3);
+
+                            storeIndex(ib, ibi, 3, v1);
+                            storeIndex(ib, ibi, 4, v3);
+                            storeIndex(ib, ibi, 5, v4);
+                        }
+                        break;
+                    }
+                    case Lines2D.RoundAnchorCap:
+                    {
+                        let cpos = Math.sqrt(t * t - ht * ht);
+                        let center = new Vector2(cpos, 0);
+                        let curA = Tools.ToRadians(-150);
+                        let incA = Tools.ToRadians(300) / (sd - 1);
+
+                        if (borderMode) {
+                            let ii = 0;
+
+                            for (let i = 0; i < sd; i++) {
+                                let v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                let v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * (t + bt), Math.sin(curA) * (t + bt)));
+
+                                if (i > 0) {
+                                    storeIndex(ib, ibi, ii++, v1 - 2);
+                                    storeIndex(ib, ibi, ii++, v2 - 2);
+                                    storeIndex(ib, ibi, ii++, v2);
+
+                                    storeIndex(ib, ibi, ii++, v1 - 2);
+                                    storeIndex(ib, ibi, ii++, v2);
+                                    storeIndex(ib, ibi, ii++, v1);
+                                }
+                                curA += incA;
+                            }
+                        } else {
+                            let c = storeVertex(vb, vbi, 0, pos, angle, center);
+                            storeVertex(vb, vbi, 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                            curA += incA;
+                            for (let i = 1; i < sd; i++) {
+                                let v2 = storeVertex(vb, vbi, i + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+
+                                storeIndex(ib, ibi, i * 3 + 0, c);
+                                storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                storeIndex(ib, ibi, i * 3 + 2, v2);
+                                curA += incA;
+                            }
+                            storeIndex(ib, ibi, sd * 3 + 0, c);
+                            storeIndex(ib, ibi, sd * 3 + 1, c + 1);
+                            storeIndex(ib, ibi, sd * 3 + 2, c + sd);
+                        }
+                        break;
+                    }
+                    case Lines2D.DiamondAnchorCap:
+                    {
+                        let vi = 0;
+                        let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht));
+                        let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, t));
+                        let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht * 3, 0));
+                        let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, -t));
+                        let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -ht));
+
+                        if (borderMode) {
+                            let f = Math.sqrt(bt * bt * 2);
+                            let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f,ht));
+                            let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht,t+f));
+                            let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht*3+f,0));
+                            let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht,-(t+f)));
+                            let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f, -ht));
+
+                            let ii = 0;
+                            storeIndex(ib, ibi, ii++, v6); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v1);
+                            storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v2);
+
+                            storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v7); storeIndex(ib, ibi, ii++, v8);
+                            storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v3);
+
+                            storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v8); storeIndex(ib, ibi, ii++, v9);
+                            storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v4);
+
+                            storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v9); storeIndex(ib, ibi, ii++, v10);
+                            storeIndex(ib, ibi, ii++, v4); storeIndex(ib, ibi, ii++, v10); storeIndex(ib, ibi, ii++, v5);
+
+                        } else {
+                            storeIndex(ib, ibi, 0, v1); storeIndex(ib, ibi, 1, v2); storeIndex(ib, ibi, 2, v3);
+                            storeIndex(ib, ibi, 3, v1); storeIndex(ib, ibi, 4, v3); storeIndex(ib, ibi, 5, v5);
+                            storeIndex(ib, ibi, 6, v5); storeIndex(ib, ibi, 7, v3); storeIndex(ib, ibi, 8, v4);
+                        }
+                        break;
+                    }
+                }
+
+                return null;
+            }
+
+            let buildLine = (vb: Float32Array, contour: Vector2[], ht: number, bt?: number) => {
+                let lineA = Vector2.Zero();
+                let lineB = Vector2.Zero();
+                let tangent = Vector2.Zero();
+                let miter = Vector2.Zero();
+                let curNormal: Vector2 = null;
+
+                if (this.closed) {
+                    this.points.push(this.points[0]);
+                }
+
+                var total = this.points.length;
+                for (let i = 1; i < total; i++) {
+                    let last = this.points[i - 1];
+                    let cur = this.points[i];
+                    let next = (i < (this.points.length - 1)) ? this.points[i + 1] : null;
+
+                    direction(cur, last, lineA);
+                    if (!curNormal) {
+                        curNormal = Vector2.Zero();
+                        perp(lineA, curNormal);
+                    }
+
+                    if (i === 1) {
+                        store(vb, contour, 0, total, this.points[0], curNormal, ht, bt);
+                    }
+
+                    if (!next) {
+                        perp(lineA, curNormal);
+                        store(vb, contour, i, total, this.points[i], curNormal, ht, bt, i - 1);
+                    } else {
+                        direction(next, cur, lineB);
+
+                        var miterLen = computeMiter(tangent, miter, lineA, lineB);
+                        store(vb, contour, i, total, this.points[i], miter, miterLen*ht, miterLen*bt, i - 1);
+                    }
+                }
+
+                if (this.points.length > 2 && this.closed) {
+                    let last2 = this.points[total - 2];
+                    let cur2 = this.points[0];
+                    let next2 = this.points[1];
+
+                    direction(cur2, last2, lineA);
+                    direction(next2, cur2, lineB);
+                    perp(lineA, curNormal);
+
+                    var miterLen2 = computeMiter(tangent, miter, lineA, lineB);
+                    store(vb, null, 0, total, this.points[0], miter, miterLen2 * ht, miterLen2 * bt, 1);
+
+                    // Patch contour
+                    if (contour) {
+                        let off = (bt == null) ? 0 : 4;
+                        contour[0].x = vb[off + 0];
+                        contour[0].y = vb[off + 1];
+                        contour[1].x = vb[off + 2];
+                        contour[1].y = vb[off + 3];
+                    }
+                }
+
+                // Remove the point we added at the beginning
+                if (this.closed) {
+                    this.points.splice(total - 1);
+                }
+            }
+
+            let contour = new Array<Vector2>();
+
+            // Need to create WebGL resources for fill part?
+            if (this.fill) {
+                let startCapInfo = getCapSize(this.startCap);
+                let endCapInfo = getCapSize(this.endCap);
+                let count = this.points.length;
+                let vbSize = (count * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
+                let vb = new Float32Array(vbSize);
+                let ht = this.fillThickness / 2;
+                let total = this.points.length;
+
+                buildLine(vb, this.border ? null : contour, ht);
+
+                let max = total * 2;
+                let triCount = (count - (this.closed ? 0 : 1)) * 2;
+                let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
+                for (let i = 0; i < triCount; i+=2) {
+                    ib[i * 3 + 0] = i + 0;
+                    ib[i * 3 + 1] = i + 1;
+                    ib[i * 3 + 2] = (i + 2) % max;
+
+                    ib[i * 3 + 3] = i + 1;
+                    ib[i * 3 + 4] = (i + 3) % max;
+                    ib[i * 3 + 5] = (i + 2) % max;
+                }
+
+                buildCap(vb, count * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, null, this.startCap, startDir);
+                buildCap(vb, (count * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, null, this.endCap, endDir);
+
+                renderCache.fillVB = engine.createVertexBuffer(vb);
+                renderCache.fillIB = engine.createIndexBuffer(ib);
+                renderCache.fillIndicesCount = ib.length;
+
+                let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_FILLPARTID, ["position"]);
+                renderCache.effectFill = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+
+            // Need to create WebGL resources for border part?
+            if (this.border) {
+                let startCapInfo = getCapSize(this.startCap, true);
+                let endCapInfo = getCapSize(this.endCap, true);
+                let count = this.points.length;
+                let vbSize = (count * 2 * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
+                let vb = new Float32Array(vbSize);
+                let ht = this.fillThickness / 2;
+                let bt = this.borderThickness;
+                let total = this.points.length;
+
+                buildLine(vb, contour, ht, bt);
+
+                let max = total * 2 * 2;
+                let triCount = (count - (this.closed ? 0 : 1)) * 2 * 2;
+                let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
+                for (let i = 0; i < triCount; i += 4) {
+                    ib[i * 3 + 0] = i + 0;
+                    ib[i * 3 + 1] = i + 2;
+                    ib[i * 3 + 2] = (i + 6) % max;
+
+                    ib[i * 3 + 3] = i + 0;
+                    ib[i * 3 + 4] = (i + 6) % max;
+                    ib[i * 3 + 5] = (i + 4) % max;
+
+                    ib[i * 3 + 6] = i + 3;
+                    ib[i * 3 + 7] = i + 1;
+                    ib[i * 3 + 8] = (i + 5) % max;
+
+                    ib[i * 3 + 9] = i + 3;
+                    ib[i * 3 + 10] = (i + 5) % max;
+                    ib[i * 3 + 11] = (i + 7) % max;
+                }
+
+                buildCap(vb, count * 2 * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, this.borderThickness, this.startCap, startDir);
+                buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, endDir);
+
+                renderCache.borderVB = engine.createVertexBuffer(vb);
+                renderCache.borderIB = engine.createIndexBuffer(ib);
+                renderCache.borderIndicesCount = ib.length;
+
+                let ei = this.getDataPartEffectInfo(Shape2D.SHAPE2D_BORDERPARTID, ["position"]);
+                renderCache.effectBorder = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+
+            this._contour = contour;
+            let bs = this._boundingMax.subtract(this._boundingMin);
+            this._size.width = bs.x;
+            this._size.height = bs.y;
+            return renderCache;
+        }
+
+
+        protected createInstanceDataParts(): InstanceDataBase[] {
+            var res = new Array<InstanceDataBase>();
+            if (this.border) {
+                res.push(new Lines2DInstanceData(Shape2D.SHAPE2D_BORDERPARTID));
+            }
+            if (this.fill) {
+                res.push(new Lines2DInstanceData(Shape2D.SHAPE2D_FILLPARTID));
+            }
+            return res;
+        }
+
+        protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
+            if (!super.refreshInstanceDataPart(part)) {
+                return false;
+            }
+            if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
+                let d = <Lines2DInstanceData>part;
+                d.boundingMin = this.boundingMin;
+                d.boundingMax = this.boundingMax;
+            }
+            else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
+                let d = <Lines2DInstanceData>part;
+                d.boundingMin = this.boundingMin;
+                d.boundingMax = this.boundingMax;
+            }
+            return true;
+        }
+
+        private static _noCap            = 0;
+        private static _roundCap         = 1;
+        private static _triangleCap      = 2;
+        private static _squareAnchorCap  = 3;
+        private static _roundAnchorCap   = 4;
+        private static _diamondAnchorCap = 5;
+        private static _arrowCap         = 6;
+
+        private static _roundCapSubDiv = 36;
+
+        private _boundingMin: Vector2;
+        private _boundingMax: Vector2;
+        private _size: Size;
+        private _contour: Vector2[];
+
+        private _closed: boolean;
+        private _startCap: number;
+        private _endCap: number;
+        private _fillThickness: number;
+        private _points: Vector2[];
+
+
+    }
+}

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

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

+ 1 - 1
src/Canvas2d/babylon.modelRenderCache.ts

@@ -1,6 +1,6 @@
 module BABYLON {
     export const enum ShaderDataType {
-        Vector2, Vector3, Vector4, Matrix, float, Color3, Color4
+        Vector2, Vector3, Vector4, Matrix, float, Color3, Color4, Size
     }
 
     export class GroupInstanceInfo {

+ 9 - 9
src/Canvas2d/babylon.prim2dBase.js

@@ -15,7 +15,7 @@ var BABYLON;
         function Render2DContext() {
         }
         return Render2DContext;
-    })();
+    }());
     BABYLON.Render2DContext = Render2DContext;
     /**
      * This class store information for the pointerEventObservable Observable.
@@ -175,7 +175,7 @@ var BABYLON;
         PrimitivePointerInfo._pointerLostCapture = 0x0200;
         PrimitivePointerInfo._mouseWheelPrecision = 3.0;
         return PrimitivePointerInfo;
-    })();
+    }());
     BABYLON.PrimitivePointerInfo = PrimitivePointerInfo;
     /**
      * Stores information about a Primitive that was intersected
@@ -186,7 +186,7 @@ var BABYLON;
             this.intersectionLocation = intersectionLocation;
         }
         return PrimitiveIntersectedInfo;
-    })();
+    }());
     BABYLON.PrimitiveIntersectedInfo = PrimitiveIntersectedInfo;
     /**
      * Main class used for the Primitive Intersection API
@@ -223,14 +223,14 @@ var BABYLON;
             }
         };
         return IntersectInfo2D;
-    })();
+    }());
     BABYLON.IntersectInfo2D = IntersectInfo2D;
     var Prim2DBase = (function (_super) {
         __extends(Prim2DBase, _super);
         function Prim2DBase() {
             _super.apply(this, arguments);
         }
-        Prim2DBase.prototype.setupPrim2DBase = function (owner, parent, id, position, isVisible) {
+        Prim2DBase.prototype.setupPrim2DBase = function (owner, parent, id, position, origin, isVisible) {
             if (isVisible === void 0) { isVisible = true; }
             if (!(this instanceof BABYLON.Group2D) && !(this instanceof BABYLON.Sprite2D && id !== null && id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
                 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");
@@ -265,7 +265,7 @@ var BABYLON;
             this.rotation = 0;
             this.scale = 1;
             this.levelVisible = isVisible;
-            this.origin = new BABYLON.Vector2(0.5, 0.5);
+            this.origin = origin || new BABYLON.Vector2(0.5, 0.5);
         };
         Object.defineProperty(Prim2DBase.prototype, "actionManager", {
             get: function () {
@@ -696,8 +696,8 @@ var BABYLON;
             }
         };
         Prim2DBase.prototype.updateGlobalTransVisOf = function (list, recurse) {
-            for (var _i = 0; _i < list.length; _i++) {
-                var cur = list[_i];
+            for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
+                var cur = list_1[_i];
                 cur.updateGlobalTransVis(recurse);
             }
         };
@@ -778,6 +778,6 @@ var BABYLON;
             BABYLON.className("Prim2DBase")
         ], Prim2DBase);
         return Prim2DBase;
-    })(BABYLON.SmartPropertyPrim);
+    }(BABYLON.SmartPropertyPrim));
     BABYLON.Prim2DBase = Prim2DBase;
 })(BABYLON || (BABYLON = {}));

+ 2 - 2
src/Canvas2d/babylon.prim2dBase.ts

@@ -296,7 +296,7 @@
     export class Prim2DBase extends SmartPropertyPrim {
         static PRIM2DBASE_PROPCOUNT: number = 10;
 
-        protected setupPrim2DBase(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, isVisible: boolean = true) {
+        protected setupPrim2DBase(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean = true) {
             if (!(this instanceof Group2D) && !(this instanceof Sprite2D && id !== null && id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
                 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");
             }
@@ -333,7 +333,7 @@
             this.rotation = 0;
             this.scale = 1;
             this.levelVisible = isVisible;
-            this.origin = new Vector2(0.5, 0.5);
+            this.origin = origin || new Vector2(0.5, 0.5);
         }
 
 

+ 34 - 22
src/Canvas2d/babylon.rectangle2d.js

@@ -35,7 +35,7 @@ var BABYLON;
             if (this.effectFill) {
                 var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
                 engine.enableEffect(this.effectFill);
-                engine.bindBuffers(this.fillVB, this.fillIB, [1], 4, this.effectFill);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, this.effectFill);
                 var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
                 if (instanceInfo._owner.owner.supportInstancedArray) {
                     if (!this.instancingFillAttributes) {
@@ -44,7 +44,7 @@ var BABYLON;
                     }
                     engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, count);
-                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingFillAttributes);
+                    engine.unbindInstanceAttributes();
                 }
                 else {
                     for (var i = 0; i < count; i++) {
@@ -56,7 +56,7 @@ var BABYLON;
             if (this.effectBorder) {
                 var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
                 engine.enableEffect(this.effectBorder);
-                engine.bindBuffers(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
                 var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
                 if (instanceInfo._owner.owner.supportInstancedArray) {
                     if (!this.instancingBorderAttributes) {
@@ -64,7 +64,7 @@ var BABYLON;
                     }
                     engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, count);
-                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingBorderAttributes);
+                    engine.unbindInstanceAttributes();
                 }
                 else {
                     for (var i = 0; i < count; i++) {
@@ -112,7 +112,7 @@ var BABYLON;
             return true;
         };
         return Rectangle2DRenderCache;
-    })(BABYLON.ModelRenderCache);
+    }(BABYLON.ModelRenderCache));
     BABYLON.Rectangle2DRenderCache = Rectangle2DRenderCache;
     var Rectangle2DInstanceData = (function (_super) {
         __extends(Rectangle2DInstanceData, _super);
@@ -130,7 +130,7 @@ var BABYLON;
             BABYLON.instanceData()
         ], Rectangle2DInstanceData.prototype, "properties", null);
         return Rectangle2DInstanceData;
-    })(BABYLON.Shape2DInstanceData);
+    }(BABYLON.Shape2DInstanceData));
     BABYLON.Rectangle2DInstanceData = Rectangle2DInstanceData;
     var Rectangle2D = (function (_super) {
         __extends(Rectangle2D, _super);
@@ -187,29 +187,41 @@ var BABYLON;
         Rectangle2D.prototype.updateLevelBoundingInfo = function () {
             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, origin, size, roundRadius, fill, border, borderThickness) {
             if (roundRadius === void 0) { roundRadius = 0; }
             if (borderThickness === void 0) { borderThickness = 1; }
-            this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
+            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
             this.size = size;
             this.notRounded = !roundRadius;
             this.roundRadius = roundRadius;
         };
-        Rectangle2D.Create = function (parent, id, x, y, width, height, fill, border) {
+        /**
+         * Create an Rectangle 2D Shape primitive. May be a sharp rectangle (with sharp corners), or a rounded one.
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - width: the width of the rectangle, default is 10
+         *  - height: the height of the rectangle, default is 10
+         *  - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp rectangle).
+         *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
+         *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
+         *  - borderThickness: the thickness of the drawn border, default is 1.
+         */
+        Rectangle2D.Create = function (parent, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             var rect = new Rectangle2D();
-            rect.setupRectangle2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), new BABYLON.Size(width, height), null);
-            rect.fill = fill;
-            rect.border = border;
-            return rect;
-        };
-        Rectangle2D.CreateRounded = function (parent, id, x, y, width, height, roundRadius, fill, border) {
-            if (roundRadius === void 0) { roundRadius = 0; }
-            BABYLON.Prim2DBase.CheckParent(parent);
-            var rect = new Rectangle2D();
-            rect.setupRectangle2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), new BABYLON.Size(width, height), roundRadius);
-            rect.fill = fill || BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
-            rect.border = border;
+            rect.setupRectangle2D(parent.owner, parent, options && options.id || null, new BABYLON.Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, new BABYLON.Size(options && options.width || 10, options && options.height || 10), options && options.roundRadius || 0);
+            if (options && options.fill !== undefined) {
+                rect.fill = options.fill;
+            }
+            else {
+                rect.fill = BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+            }
+            rect.border = options && options.border || null;
+            rect.borderThickness = options && options.borderThickness || 1;
             return rect;
         };
         Rectangle2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
@@ -312,6 +324,6 @@ var BABYLON;
             BABYLON.className("Rectangle2D")
         ], Rectangle2D);
         return Rectangle2D;
-    })(BABYLON.Shape2D);
+    }(BABYLON.Shape2D));
     BABYLON.Rectangle2D = Rectangle2D;
 })(BABYLON || (BABYLON = {}));

+ 30 - 19
src/Canvas2d/babylon.rectangle2d.ts

@@ -40,7 +40,7 @@
                 let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
 
                 engine.enableEffect(this.effectFill);
-                engine.bindBuffers(this.fillVB, this.fillIB, [1], 4, this.effectFill);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, this.effectFill);
                 let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
                 if (instanceInfo._owner.owner.supportInstancedArray) {
                     if (!this.instancingFillAttributes) {
@@ -50,7 +50,7 @@
 
                     engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
                     engine.draw(true, 0, this.fillIndicesCount, count);
-                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingFillAttributes);
+                    engine.unbindInstanceAttributes();
                 } else {
                     for (let i = 0; i < count; i++) {
                         this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
@@ -63,7 +63,7 @@
                 let partIndex = instanceInfo._partIndexFromId.get(Shape2D.SHAPE2D_BORDERPARTID.toString());
 
                 engine.enableEffect(this.effectBorder);
-                engine.bindBuffers(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
                 let count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
                 if (instanceInfo._owner.owner.supportInstancedArray) {
                     if (!this.instancingBorderAttributes) {
@@ -72,7 +72,7 @@
 
                     engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
                     engine.draw(true, 0, this.borderIndicesCount, count);
-                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingBorderAttributes);
+                    engine.unbindInstanceAttributes();
                 } else {
                     for (let i = 0; i < count; i++) {
                         this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
@@ -195,30 +195,41 @@
             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) {
-            this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
+        protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
+            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
             this.size = size;
             this.notRounded = !roundRadius;
             this.roundRadius = roundRadius;
         }
 
-        public static Create(parent: Prim2DBase, id: string, x: number, y: number, width: number, height: number, fill?: IBrush2D, border?: IBrush2D): Rectangle2D {
+        /**
+         * Create an Rectangle 2D Shape primitive. May be a sharp rectangle (with sharp corners), or a rounded one.
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - width: the width of the rectangle, default is 10
+         *  - height: the height of the rectangle, default is 10
+         *  - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp rectangle).
+         *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
+         *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
+         *  - borderThickness: the thickness of the drawn border, default is 1.
+         */
+        public static Create(parent: Prim2DBase, options: { id?: string, x?: number, y?: number, origin?: Vector2, width?: number, height?: number, roundRadius?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number}): Rectangle2D {
             Prim2DBase.CheckParent(parent);
 
             let rect = new Rectangle2D();
-            rect.setupRectangle2D(parent.owner, parent, id, new Vector2(x, y), new Size(width, height), null);
-            rect.fill = fill;
-            rect.border = border;
-            return rect;
-        }
-
-        public static CreateRounded(parent: Prim2DBase, id: string, x: number, y: number, width: number, height: number, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D): Rectangle2D {
-            Prim2DBase.CheckParent(parent);
+            rect.setupRectangle2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, new Size(options && options.width || 10, options && options.height || 10), options && options.roundRadius || 0);
 
-            let rect = new Rectangle2D();
-            rect.setupRectangle2D(parent.owner, parent, id, new Vector2(x, y), new Size(width, height), roundRadius);
-            rect.fill = fill || Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
-            rect.border = border;
+            if (options && options.fill !== undefined) {
+                rect.fill = options.fill;
+            } else {
+                rect.fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");                
+            }
+            rect.border = options && options.border || null;
+            rect.borderThickness = options && options.borderThickness || 1;
             return rect;
         }
 

+ 48 - 12
src/Canvas2d/babylon.renderablePrim2d.js

@@ -27,7 +27,7 @@ var BABYLON;
             }
         };
         InstanceClassInfo.prototype.getInstancingAttributeInfos = function (effect, categories) {
-            var catInline = categories.join(";");
+            var catInline = ";" + categories.join(";") + ";";
             var res = new Array();
             var curInfo = this;
             while (curInfo) {
@@ -73,10 +73,9 @@ var BABYLON;
             return curOffset;
         };
         return InstanceClassInfo;
-    })();
+    }());
     BABYLON.InstanceClassInfo = InstanceClassInfo;
     var InstancePropInfo = (function () {
-        //uniformLocation: WebGLUniformLocation;
         function InstancePropInfo() {
             this.instanceOffset = new BABYLON.StringDictionary();
         }
@@ -114,6 +113,11 @@ var BABYLON;
                 this.dataType = 6 /* Color4 */;
                 return;
             }
+            if (val instanceof BABYLON.Size) {
+                this.size = 8;
+                this.dataType = 7 /* Size */;
+                return;
+            }
             return;
         };
         InstancePropInfo.prototype.writeData = function (array, offset, val) {
@@ -173,10 +177,17 @@ var BABYLON;
                         }
                         break;
                     }
+                case 7 /* Size */:
+                    {
+                        var s = val;
+                        array[offset + 0] = s.width;
+                        array[offset + 1] = s.height;
+                        break;
+                    }
             }
         };
         return InstancePropInfo;
-    })();
+    }());
     BABYLON.InstancePropInfo = InstancePropInfo;
     function instanceData(category, shaderAttributeName) {
         return function (target, propName, descriptor) {
@@ -191,11 +202,19 @@ var BABYLON;
             info = new InstancePropInfo();
             info.attributeName = shaderAttributeName;
             info.category = category || null;
+            if (info.category) {
+                info.delimitedCategory = ";" + info.category + ";";
+            }
             node.levelContent.add(instanceDataName, info);
             descriptor.get = function () {
                 return null;
             };
             descriptor.set = function (val) {
+                // Check that we're not trying to set a property that belongs to a category that is not allowed (current)
+                // Quit if it's the case, otherwise we could overwrite data somewhere...
+                if (info.category && InstanceClassInfo._CurCategories.indexOf(info.delimitedCategory) === -1) {
+                    return;
+                }
                 if (!info.size) {
                     info.setSize(val);
                     node.classContent.mapProperty(info, true);
@@ -300,7 +319,7 @@ var BABYLON;
             instanceData()
         ], InstanceDataBase.prototype, "origin", null);
         return InstanceDataBase;
-    })();
+    }());
     BABYLON.InstanceDataBase = InstanceDataBase;
     var RenderablePrim2D = (function (_super) {
         __extends(RenderablePrim2D, _super);
@@ -317,8 +336,8 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        RenderablePrim2D.prototype.setupRenderablePrim2D = function (owner, parent, id, position, isVisible) {
-            this.setupPrim2DBase(owner, parent, id, position);
+        RenderablePrim2D.prototype.setupRenderablePrim2D = function (owner, parent, id, position, origin, isVisible) {
+            this.setupPrim2DBase(owner, parent, id, position, origin);
             this._isTransparent = false;
         };
         RenderablePrim2D.prototype.dispose = function () {
@@ -382,8 +401,8 @@ var BABYLON;
                     var usedCatList = new Array();
                     var partIdList = new Array();
                     var joinedUsedCatList = new Array();
-                    for (var _i = 0; _i < parts.length; _i++) {
-                        var dataPart = parts[_i];
+                    for (var _i = 0, parts_1 = parts; _i < parts_1.length; _i++) {
+                        var dataPart = parts_1[_i];
                         var cat = this.getUsedShaderCategories(dataPart);
                         var cti = dataPart.getClassTreeInfo();
                         // Make sure the instance is visible other the properties won't be set and their size/offset wont be computed
@@ -391,7 +410,7 @@ var BABYLON;
                         this.isVisible = true;
                         // We manually trigger refreshInstanceData for the only sake of evaluating each instance property size and offset in the instance data, this can only be made at runtime. Once it's done we have all the information to create the instance data buffer.
                         //console.log("Build Prop Layout for " + Tools.getClassName(this._instanceDataParts[0]));
-                        var joinCat = cat.join(";");
+                        var joinCat = ";" + cat.join(";") + ";";
                         joinedUsedCatList.push(joinCat);
                         InstanceClassInfo._CurCategories = joinCat;
                         var obj = this.beforeRefreshForLayoutConstruction(dataPart);
@@ -432,7 +451,7 @@ var BABYLON;
                         gii._partIndexFromId.add(this._modelRenderCache._partIdList[j].toString(), j);
                         for (var _a = 0, _b = this._instanceDataParts; _a < _b.length; _a++) {
                             var part = _b[_a];
-                            gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())] = this.getUsedShaderCategories(part).join(";");
+                            gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())] = ";" + this.getUsedShaderCategories(part).join(";") + ";";
                         }
                     }
                 }
@@ -477,6 +496,23 @@ var BABYLON;
                 this._visibilityChanged = false; // Reset the flag as we've handled the case
             }
         };
+        /**
+         * Transform a given point using the Primitive's origin setting.
+         * This method requires the Primitive's actualSize to be accurate
+         * @param p the point to transform
+         * @param originOffset an offset applied on the current origin before performing the transformation. Depending on which frame of reference your data is expressed you may have to apply a offset. (if you data is expressed from the bottom/left, no offset is required. If it's expressed from the center the a [-0.5;-0.5] offset has to be applied.
+         * @param res an allocated Vector2 that will receive the transformed content
+         */
+        RenderablePrim2D.prototype.transformPointWithOriginByRef = function (p, originOffset, res) {
+            var actualSize = this.actualSize;
+            res.x = p.x - ((this.origin.x + (originOffset ? originOffset.x : 0)) * actualSize.width);
+            res.y = p.y - ((this.origin.y + (originOffset ? originOffset.y : 0)) * actualSize.height);
+        };
+        RenderablePrim2D.prototype.transformPointWithOrigin = function (p, originOffset) {
+            var res = new BABYLON.Vector2(0, 0);
+            this.transformPointWithOriginByRef(p, originOffset, res);
+            return res;
+        };
         RenderablePrim2D.prototype.getDataPartEffectInfo = function (dataPartId, vertexBufferAttributes) {
             var dataPart = BABYLON.Tools.first(this._instanceDataParts, function (i) { return i.id === dataPartId; });
             if (!dataPart) {
@@ -571,6 +607,6 @@ var BABYLON;
             BABYLON.className("RenderablePrim2D")
         ], RenderablePrim2D);
         return RenderablePrim2D;
-    })(BABYLON.Prim2DBase);
+    }(BABYLON.Prim2DBase));
     BABYLON.RenderablePrim2D = RenderablePrim2D;
 })(BABYLON || (BABYLON = {}));

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

@@ -19,7 +19,7 @@
         }
 
         getInstancingAttributeInfos(effect: Effect, categories: string[]): InstancingAttributeInfo[] {
-            let catInline = categories.join(";");
+            let catInline = ";" + categories.join(";") + ";";
             let res = new Array<InstancingAttributeInfo>();
             let curInfo: InstanceClassInfo = this;
             while (curInfo) {
@@ -82,6 +82,8 @@
         dataType: ShaderDataType;
         //uniformLocation: WebGLUniformLocation;
 
+        delimitedCategory: string;
+
         constructor() {
             this.instanceOffset = new StringDictionary<number>();
         }
@@ -120,7 +122,11 @@
                 this.dataType = ShaderDataType.Color4;
                 return;
             }
-            return;
+            if (val instanceof Size) {
+                this.size = 8;
+                this.dataType = ShaderDataType.Size;
+                return;
+            }            return;
         }
 
         writeData(array: Float32Array, offset: number, val) {
@@ -180,6 +186,13 @@
                         }
                         break;
                     }
+                case ShaderDataType.Size:
+                    {
+                        let s = <Size>val;
+                        array[offset + 0] = s.width;
+                        array[offset + 1] = s.height;
+                        break;
+                    }
             }
         }
     }
@@ -201,6 +214,9 @@
             info = new InstancePropInfo();
             info.attributeName = shaderAttributeName;
             info.category = category || null;
+            if (info.category) {
+                info.delimitedCategory = ";" + info.category + ";";
+            }
 
             node.levelContent.add(instanceDataName, info);
 
@@ -209,6 +225,11 @@
             }
 
             descriptor.set = function (val) {
+                // Check that we're not trying to set a property that belongs to a category that is not allowed (current)
+                // Quit if it's the case, otherwise we could overwrite data somewhere...
+                if (info.category && InstanceClassInfo._CurCategories.indexOf(info.delimitedCategory) === -1) {
+                    return;
+                }
                 if (!info.size) {
                     info.setSize(val);
                     node.classContent.mapProperty(info, true);
@@ -322,8 +343,8 @@
             this._isTransparent = value;
         }
 
-        setupRenderablePrim2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, isVisible: boolean) {
-            this.setupPrim2DBase(owner, parent, id, position);
+        setupRenderablePrim2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean) {
+            this.setupPrim2DBase(owner, parent, id, position, origin);
             this._isTransparent = false;
         }
 
@@ -408,7 +429,7 @@
                         this.isVisible = true;
                         // We manually trigger refreshInstanceData for the only sake of evaluating each instance property size and offset in the instance data, this can only be made at runtime. Once it's done we have all the information to create the instance data buffer.
                         //console.log("Build Prop Layout for " + Tools.getClassName(this._instanceDataParts[0]));
-                        let joinCat = cat.join(";");
+                        let joinCat = ";" + cat.join(";") + ";";
                         joinedUsedCatList.push(joinCat);
                         InstanceClassInfo._CurCategories = joinCat;
                         let obj = this.beforeRefreshForLayoutConstruction(dataPart);
@@ -451,7 +472,7 @@
                         gii._partIndexFromId.add(this._modelRenderCache._partIdList[j].toString(), j);
 
                         for (let part of this._instanceDataParts) {
-                            gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())] = this.getUsedShaderCategories(part).join(";");
+                            gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())] = ";" + this.getUsedShaderCategories(part).join(";") + ";";
                         }
                     }
                 }
@@ -506,6 +527,25 @@
             }
         }
 
+        /**
+         * Transform a given point using the Primitive's origin setting.
+         * This method requires the Primitive's actualSize to be accurate
+         * @param p the point to transform
+         * @param originOffset an offset applied on the current origin before performing the transformation. Depending on which frame of reference your data is expressed you may have to apply a offset. (if you data is expressed from the bottom/left, no offset is required. If it's expressed from the center the a [-0.5;-0.5] offset has to be applied.
+         * @param res an allocated Vector2 that will receive the transformed content
+         */
+        protected transformPointWithOriginByRef(p: Vector2, originOffset:Vector2, res: Vector2) {
+            let actualSize = this.actualSize;
+            res.x = p.x - ((this.origin.x + (originOffset ? originOffset.x : 0)) * actualSize.width);
+            res.y = p.y - ((this.origin.y + (originOffset ? originOffset.y : 0)) * actualSize.height);
+        }
+
+        protected transformPointWithOrigin(p: Vector2, originOffset: Vector2): Vector2 {
+            let res = new Vector2(0, 0);
+            this.transformPointWithOriginByRef(p, originOffset, res);
+            return res;
+        }
+
         protected getDataPartEffectInfo(dataPartId: number, vertexBufferAttributes: string[]): { attributes: string[], uniforms: string[], defines: string } {
             let dataPart = Tools.first(this._instanceDataParts, i => i.id === dataPartId);
             if (!dataPart) {

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

@@ -48,9 +48,9 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Shape2D.prototype.setupShape2D = function (owner, parent, id, position, isVisible, fill, border, borderThickness) {
+        Shape2D.prototype.setupShape2D = function (owner, parent, id, position, origin, isVisible, fill, border, borderThickness) {
             if (borderThickness === void 0) { borderThickness = 1.0; }
-            this.setupRenderablePrim2D(owner, parent, id, position, isVisible);
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible);
             this.border = border;
             this.fill = fill;
             this.borderThickness = borderThickness;
@@ -144,7 +144,7 @@ var BABYLON;
             BABYLON.className("Shape2D")
         ], Shape2D);
         return Shape2D;
-    })(BABYLON.RenderablePrim2D);
+    }(BABYLON.RenderablePrim2D));
     BABYLON.Shape2D = Shape2D;
     var Shape2DInstanceData = (function (_super) {
         __extends(Shape2DInstanceData, _super);
@@ -244,6 +244,6 @@ var BABYLON;
             BABYLON.instanceData(Shape2D.SHAPE2D_CATEGORY_BORDERGRADIENT)
         ], Shape2DInstanceData.prototype, "borderGradientTY", null);
         return Shape2DInstanceData;
-    })(BABYLON.InstanceDataBase);
+    }(BABYLON.InstanceDataBase));
     BABYLON.Shape2DInstanceData = Shape2DInstanceData;
 })(BABYLON || (BABYLON = {}));

+ 2 - 4
src/Canvas2d/babylon.shape2d.ts

@@ -44,8 +44,8 @@
             this._borderThickness = value;
         }
 
-        setupShape2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, isVisible: boolean, fill: IBrush2D, border: IBrush2D, borderThickness: number = 1.0) {
-            this.setupRenderablePrim2D(owner, parent, id, position, isVisible);
+        setupShape2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean, fill: IBrush2D, border: IBrush2D, borderThickness: number = 1.0) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible);
             this.border = border;
             this.fill = fill;
             this.borderThickness = borderThickness;
@@ -123,8 +123,6 @@
                         d.borderGradientTY = ty;
                     }
                 }
-
-
             }
 
             return true;

+ 21 - 8
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -10,7 +10,7 @@ var BABYLON;
         function Prim2DClassInfo() {
         }
         return Prim2DClassInfo;
-    })();
+    }());
     BABYLON.Prim2DClassInfo = Prim2DClassInfo;
     var Prim2DPropInfo = (function () {
         function Prim2DPropInfo() {
@@ -19,13 +19,13 @@ var BABYLON;
         Prim2DPropInfo.PROPKIND_INSTANCE = 2;
         Prim2DPropInfo.PROPKIND_DYNAMIC = 3;
         return Prim2DPropInfo;
-    })();
+    }());
     BABYLON.Prim2DPropInfo = Prim2DPropInfo;
     var PropertyChangedInfo = (function () {
         function PropertyChangedInfo() {
         }
         return PropertyChangedInfo;
-    })();
+    }());
     BABYLON.PropertyChangedInfo = PropertyChangedInfo;
     var ClassTreeInfo = (function () {
         function ClassTreeInfo(baseClass, type, classContentFactory) {
@@ -62,13 +62,13 @@ var BABYLON;
         Object.defineProperty(ClassTreeInfo.prototype, "fullContent", {
             get: function () {
                 if (!this._fullContent) {
-                    var dic = new BABYLON.StringDictionary();
+                    var dic_1 = new BABYLON.StringDictionary();
                     var curLevel = this;
                     while (curLevel) {
-                        curLevel.levelContent.forEach(function (k, v) { return dic.add(k, v); });
+                        curLevel.levelContent.forEach(function (k, v) { return dic_1.add(k, v); });
                         curLevel = curLevel._baseClass;
                     }
-                    this._fullContent = dic;
+                    this._fullContent = dic_1;
                 }
                 return this._fullContent;
             },
@@ -128,7 +128,7 @@ var BABYLON;
             return dic;
         };
         return ClassTreeInfo;
-    })();
+    }());
     BABYLON.ClassTreeInfo = ClassTreeInfo;
     var SmartPropertyPrim = (function () {
         function SmartPropertyPrim() {
@@ -190,6 +190,16 @@ var BABYLON;
                 propDic.forEach(function (k, v) {
                     if (v.kind === Prim2DPropInfo.PROPKIND_MODEL) {
                         var propVal = _this[v.name];
+                        // Special case, array, this WON'T WORK IN ALL CASES, all entries have to be of the same type and it must be a BJS well known one
+                        if (propVal && propVal.constructor === Array) {
+                            var firstVal = propVal[0];
+                            if (!firstVal) {
+                                propVal = 0;
+                            }
+                            else {
+                                propVal = BABYLON.Tools.hashCodeFromStream(BABYLON.Tools.arrayOrStringFeeder(propVal));
+                            }
+                        }
                         modelKey += v.name + ":" + ((propVal != null) ? ((v.typeLevelCompare) ? BABYLON.Tools.getClassName(propVal) : propVal.toString()) : "[null]") + ";";
                     }
                 });
@@ -350,6 +360,9 @@ var BABYLON;
             this._instanceDirtyFlags &= ~flags;
             return this._instanceDirtyFlags;
         };
+        SmartPropertyPrim.prototype._resetPropertiesDirty = function () {
+            this._instanceDirtyFlags = 0;
+        };
         Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
             /**
              * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
@@ -406,7 +419,7 @@ var BABYLON;
             BABYLON.className("SmartPropertyPrim")
         ], SmartPropertyPrim);
         return SmartPropertyPrim;
-    })();
+    }());
     BABYLON.SmartPropertyPrim = SmartPropertyPrim;
     function modelLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo) {
         if (typeLevelCompare === void 0) { typeLevelCompare = false; }

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

@@ -205,6 +205,17 @@
             propDic.forEach((k, v) => {
                 if (v.kind === Prim2DPropInfo.PROPKIND_MODEL) {
                     let propVal = this[v.name];
+
+                    // Special case, array, this WON'T WORK IN ALL CASES, all entries have to be of the same type and it must be a BJS well known one
+                    if (propVal && propVal.constructor === Array) {
+                        let firstVal = propVal[0];
+                        if (!firstVal) {
+                            propVal = 0;
+                        } else {
+                            propVal = Tools.hashCodeFromStream(Tools.arrayOrStringFeeder(propVal));
+                        }
+                    }
+
                     modelKey += v.name + ":" + ((propVal != null) ? ((v.typeLevelCompare) ? Tools.getClassName(propVal) : propVal.toString()) : "[null]") + ";";
                 }
             });
@@ -383,6 +394,10 @@
             return this._instanceDirtyFlags;
         }
 
+        public _resetPropertiesDirty() {
+            this._instanceDirtyFlags = 0;
+        }
+
         /**
          * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
          * @returns {} 
@@ -445,8 +460,8 @@
 
         private _modelKey; string;
         private _propInfo: StringDictionary<Prim2DPropInfo>;
-        private _levelBoundingInfoDirty: boolean;
         private _isDisposed: boolean;
+        protected _levelBoundingInfoDirty: boolean;
         protected _levelBoundingInfo: BoundingInfo2D;
         protected _boundingInfo: BoundingInfo2D;
         protected _modelDirty: boolean;

+ 30 - 14
src/Canvas2d/babylon.sprite2d.js

@@ -25,7 +25,7 @@ var BABYLON;
             var engine = instanceInfo._owner.owner.engine;
             engine.enableEffect(this.effect);
             this.effect.setTexture("diffuseSampler", this.texture);
-            engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
+            engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, this.effect);
             var cur = engine.getAlphaMode();
             engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
             var count = instanceInfo._instancesPartsData[0].usedElementCount;
@@ -35,7 +35,7 @@ var BABYLON;
                 }
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
                 engine.draw(true, 0, 6, count);
-                engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], this.instancingAttributes);
+                engine.unbindInstanceAttributes();
             }
             else {
                 for (var i = 0; i < count; i++) {
@@ -69,7 +69,7 @@ var BABYLON;
             return true;
         };
         return Sprite2DRenderCache;
-    })(BABYLON.ModelRenderCache);
+    }(BABYLON.ModelRenderCache));
     BABYLON.Sprite2DRenderCache = Sprite2DRenderCache;
     var Sprite2DInstanceData = (function (_super) {
         __extends(Sprite2DInstanceData, _super);
@@ -127,7 +127,7 @@ var BABYLON;
             BABYLON.instanceData()
         ], Sprite2DInstanceData.prototype, "invertY", null);
         return Sprite2DInstanceData;
-    })(BABYLON.InstanceDataBase);
+    }(BABYLON.InstanceDataBase));
     BABYLON.Sprite2DInstanceData = Sprite2DInstanceData;
     var Sprite2D = (function (_super) {
         __extends(Sprite2D, _super);
@@ -205,27 +205,43 @@ var BABYLON;
             // 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) {
-            this.setupRenderablePrim2D(owner, parent, id, position, true);
+        Sprite2D.prototype.setupSprite2D = function (owner, parent, id, position, origin, texture, spriteSize, spriteLocation, invertY) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, true);
             this.texture = texture;
             this.texture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
-            this.spriteSize = spriteSize;
-            this.spriteLocation = spriteLocation;
+            this.spriteSize = spriteSize || null;
+            this.spriteLocation = spriteLocation || new BABYLON.Vector2(0, 0);
             this.spriteFrame = 0;
             this.invertY = invertY;
             this._isTransparent = true;
+            if (!this.spriteSize) {
+                var s = texture.getSize();
+                this.spriteSize = new BABYLON.Size(s.width, s.height);
+            }
         };
-        Sprite2D.Create = function (parent, id, x, y, texture, spriteSize, spriteLocation, invertY) {
-            if (invertY === void 0) { invertY = false; }
+        /**
+         * Create an 2D Sprite primitive
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * @param texture the texture that stores the sprite to render
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - spriteSize: the size of the sprite, if null the size of the given texture will be used, default is null.
+         *  - spriteLocation: the location in the texture of the top/left corner of the Sprite to display, default is null (0,0)
+         *  - invertY: if true the texture Y will be inverted, default is false.
+         */
+        Sprite2D.Create = function (parent, texture, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             var sprite = new Sprite2D();
-            sprite.setupSprite2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), texture, spriteSize, spriteLocation, invertY);
+            sprite.setupSprite2D(parent.owner, parent, options && options.id || null, new BABYLON.Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, texture, options && options.spriteSize || null, options && options.spriteLocation || null, options && options.invertY || false);
             return sprite;
         };
         Sprite2D._createCachedCanvasSprite = function (owner, texture, size, pos) {
             var sprite = new Sprite2D();
-            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new BABYLON.Vector2(0, 0), texture, size, pos, false);
+            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new BABYLON.Vector2(0, 0), null, texture, size, pos, false);
             return sprite;
         };
         Sprite2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
@@ -264,7 +280,7 @@ var BABYLON;
             }
             if (part.id === Sprite2D.SPRITE2D_MAINPARTID) {
                 var d = this._instanceDataParts[0];
-                var ts = this.texture.getSize();
+                var ts = this.texture.getBaseSize();
                 var sl = this.spriteLocation;
                 var ss = this.spriteSize;
                 d.topLeftUV = new BABYLON.Vector2(sl.x / ts.width, sl.y / ts.height);
@@ -296,6 +312,6 @@ var BABYLON;
             BABYLON.className("Sprite2D")
         ], Sprite2D);
         return Sprite2D;
-    })(BABYLON.RenderablePrim2D);
+    }(BABYLON.RenderablePrim2D));
     BABYLON.Sprite2D = Sprite2D;
 })(BABYLON || (BABYLON = {}));

+ 28 - 10
src/Canvas2d/babylon.sprite2d.ts

@@ -18,7 +18,7 @@
 
             engine.enableEffect(this.effect);
             this.effect.setTexture("diffuseSampler", this.texture);
-            engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
+            engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, this.effect);
 
             var cur = engine.getAlphaMode();
             engine.setAlphaMode(Engine.ALPHA_COMBINE);
@@ -29,7 +29,7 @@
                 }
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
                 engine.draw(true, 0, 6, count);
-                engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], this.instancingAttributes);
+                engine.unbindInstanceAttributes();
             } else {
                 for (let i = 0; i < count; i++) {
                     this.setupUniforms(this.effect, 0, instanceInfo._instancesPartsData[0], i);
@@ -179,30 +179,48 @@
             return true;
         }
 
-        protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean) {
-            this.setupRenderablePrim2D(owner, parent, id, position, true);
+        protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, true);
             this.texture = texture;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this.spriteSize = spriteSize;
-            this.spriteLocation = spriteLocation;
+            this.spriteSize = spriteSize || null;
+            this.spriteLocation = spriteLocation || new Vector2(0,0);
             this.spriteFrame = 0;
             this.invertY = invertY;
             this._isTransparent = true;
+
+            if (!this.spriteSize) {
+                var s = texture.getSize();
+                this.spriteSize = new Size(s.width, s.height);
+            }
         }
 
-        public static Create(parent: Prim2DBase, id: string, x: number, y: number, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean = false): Sprite2D {
+        /**
+         * Create an 2D Sprite primitive
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * @param texture the texture that stores the sprite to render
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - spriteSize: the size of the sprite, if null the size of the given texture will be used, default is null.
+         *  - spriteLocation: the location in the texture of the top/left corner of the Sprite to display, default is null (0,0)
+         *  - invertY: if true the texture Y will be inverted, default is false.
+         */
+        public static Create(parent: Prim2DBase, texture: Texture, options: { id?: string, x?: number, y?: number, origin?: Vector2, spriteSize?: Size, spriteLocation?: Vector2, invertY?: boolean}): Sprite2D {
             Prim2DBase.CheckParent(parent);
 
             let sprite = new Sprite2D();
-            sprite.setupSprite2D(parent.owner, parent, id, new Vector2(x, y), texture, spriteSize, spriteLocation, invertY);
+            sprite.setupSprite2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, texture, options && options.spriteSize || null, options && options.spriteLocation || null, options && options.invertY || false);
             return sprite;
         }
 
         static _createCachedCanvasSprite(owner: Canvas2D, texture: MapTexture, size: Size, pos: Vector2): Sprite2D {
 
             let sprite = new Sprite2D();
-            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new Vector2(0, 0), texture, size, pos, false);
+            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new Vector2(0, 0), null, texture, size, pos, false);
 
             return sprite;
         }
@@ -253,7 +271,7 @@
 
             if (part.id === Sprite2D.SPRITE2D_MAINPARTID) {
                 let d = <Sprite2DInstanceData>this._instanceDataParts[0];
-                let ts = this.texture.getSize();
+                let ts = this.texture.getBaseSize();
                 let sl = this.spriteLocation;
                 let ss = this.spriteSize;
                 d.topLeftUV = new Vector2(sl.x / ts.width, sl.y / ts.height);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 25 - 12
src/Canvas2d/babylon.text2d.js


+ 22 - 6
src/Canvas2d/babylon.text2d.ts

@@ -22,7 +22,7 @@
 
             engine.enableEffect(this.effect);
             this.effect.setTexture("diffuseSampler", this.fontTexture);
-            engine.bindBuffers(this.vb, this.ib, [1], 4, this.effect);
+            engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, this.effect);
 
             var cur = engine.getAlphaMode();
             engine.setAlphaMode(Engine.ALPHA_ADD);
@@ -30,7 +30,7 @@
             if (instanceInfo._owner.owner.supportInstancedArray) {
                 engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], null, this.instancingAttributes);
                 engine.draw(true, 0, 6, count);
-                engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[0], this.instancingAttributes);
+                engine.unbindInstanceAttributes();
             } else {
                 for (let i = 0; i < count; i++) {
                     this.setupUniforms(this.effect, 0, instanceInfo._instancesPartsData[0], i);
@@ -216,8 +216,8 @@
             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) {
-            this.setupRenderablePrim2D(owner, parent, id, position, true);
+        protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, fontName: string, text: string, areaSize: Size, defaultFontColor: Color4, vAlign, hAlign, tabulationSize: number) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, true);
 
             this.fontName = fontName;
             this.defaultFontColor = defaultFontColor;
@@ -229,11 +229,27 @@
             this._isTransparent = true;
         }
 
-        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 {
+        /**
+         * Create a Text primitive
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * @param text the text to display
+         * Options:
+         *  - id a text identifier, for information purpose
+         *  - x: the X position relative to its parent, default is 0
+         *  - y: the Y position relative to its parent, default is 0
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - fontName: the name/size/style of the font to use, following the CSS notation. Default is "12pt Arial".
+         *  - defaultColor: the color by default to apply on each letter of the text to display, default is plain white.
+         *  - areaSize: the size of the area in which to display the text, default is auto-fit from text content.
+         *  - vAlign: vertical alignment (areaSize must be specified), default is Text2D.TEXT2D_VALIGN_CENTER
+         *  - hAlign: horizontal alignment (areaSize must be specified), default is Text2D.TEXT2D_HALIGN_CENTER
+         *  - tabulationSize: number of space character to insert when a tabulation is encountered, default is 4
+         */
+        public static Create(parent: Prim2DBase, text: string, options?: { id?: string, x?: number, y?: number, origin?:Vector2, fontName?: string, defaultFontColor?: Color4, areaSize?: Size, vAlign?: number, hAlign?: number, tabulationSize?: number}): Text2D {
             Prim2DBase.CheckParent(parent);
 
             let text2d = new Text2D();
-            text2d.setupText2D(parent.owner, parent, id, new Vector2(x, y), fontName, text, areaSize, defaultFontColor || new Color4(0,0,0,1), vAlign, hAlign, tabulationSize);
+            text2d.setupText2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, options && options.fontName || "12pt Arial", text, options && options.areaSize, options && options.defaultFontColor || new Color4(1, 1, 1, 1), options && options.vAlign || Text2D.TEXT2D_VALIGN_CENTER, options && options.hAlign || Text2D.TEXT2D_HALIGN_CENTER, options && options.tabulationSize || 4);
             return text2d;
         }
 

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

@@ -8,20 +8,20 @@ var BABYLON;
     /**
      * This is the class that is used to display a World Space Canvas into a scene
      */
-    var WorldSpaceCanvas2d = (function (_super) {
-        __extends(WorldSpaceCanvas2d, _super);
-        function WorldSpaceCanvas2d(name, scene, canvas) {
+    var WorldSpaceCanvas2D = (function (_super) {
+        __extends(WorldSpaceCanvas2D, _super);
+        function WorldSpaceCanvas2D(name, scene, canvas) {
             _super.call(this, name, scene);
             this._canvas = canvas;
         }
-        WorldSpaceCanvas2d.prototype.dispose = function () {
+        WorldSpaceCanvas2D.prototype.dispose = function () {
             _super.prototype.dispose.call(this);
             if (this._canvas) {
                 this._canvas.dispose();
                 this._canvas = null;
             }
         };
-        return WorldSpaceCanvas2d;
-    })(BABYLON.Mesh);
-    BABYLON.WorldSpaceCanvas2d = WorldSpaceCanvas2d;
+        return WorldSpaceCanvas2D;
+    }(BABYLON.Mesh));
+    BABYLON.WorldSpaceCanvas2D = WorldSpaceCanvas2D;
 })(BABYLON || (BABYLON = {}));

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

@@ -2,7 +2,7 @@
     /**
      * This is the class that is used to display a World Space Canvas into a scene
      */
-    export class WorldSpaceCanvas2d extends Mesh {
+    export class WorldSpaceCanvas2D extends Mesh {
         constructor(name: string, scene: Scene, canvas: Canvas2D) {
             super(name, scene);
 

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

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

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

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

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

@@ -32,7 +32,7 @@ var BABYLON;
             delete this._geometries[id];
         };
         return CollisionCache;
-    })();
+    }());
     BABYLON.CollisionCache = CollisionCache;
     var CollideWorker = (function () {
         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 CollideWorker;
-    })();
+    }());
     BABYLON.CollideWorker = CollideWorker;
     var CollisionDetectorTransferable = (function () {
         function CollisionDetectorTransferable() {
@@ -206,7 +206,7 @@ var BABYLON;
             postMessage(reply, undefined);
         };
         return CollisionDetectorTransferable;
-    })();
+    }());
     BABYLON.CollisionDetectorTransferable = CollisionDetectorTransferable;
     //check if we are in a web worker, as this code should NOT run on the main UI thread
     try {

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

@@ -9,7 +9,7 @@ var BABYLON;
             this.subMeshId = 0;
         }
         return IntersectionInfo;
-    })();
+    }());
     BABYLON.IntersectionInfo = IntersectionInfo;
     var PickingInfo = (function () {
         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 PickingInfo;
-    })();
+    }());
     BABYLON.PickingInfo = PickingInfo;
 })(BABYLON || (BABYLON = {}));

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

@@ -83,6 +83,6 @@ var BABYLON;
             }
         };
         return Octree;
-    })();
+    }());
     BABYLON.Octree = Octree;
 })(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);
         };
         return OctreeBlock;
-    })();
+    }());
     BABYLON.OctreeBlock = OctreeBlock;
 })(BABYLON || (BABYLON = {}));

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

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

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

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

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

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

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

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

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

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

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

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

+ 13 - 11
src/Layer/babylon.layer.js

@@ -6,8 +6,7 @@ var BABYLON;
             this.scale = new BABYLON.Vector2(1, 1);
             this.offset = new BABYLON.Vector2(0, 0);
             this.alphaBlendingMode = BABYLON.Engine.ALPHA_COMBINE;
-            this._vertexDeclaration = [2];
-            this._vertexStrideSize = 2 * 4;
+            this._vertexBuffers = {};
             // Events
             /**
             * An event triggered when the layer is disposed.
@@ -29,13 +28,15 @@ var BABYLON;
             this.color = color === undefined ? new BABYLON.Color4(1, 1, 1, 1) : color;
             this._scene = scene;
             this._scene.layers.push(this);
+            var engine = scene.getEngine();
             // VBO
             var vertices = [];
             vertices.push(1, 1);
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            var vertexBuffer = new BABYLON.VertexBuffer(engine, vertices, BABYLON.VertexBuffer.PositionKind, false, false, 2);
+            this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = vertexBuffer;
             // Indices
             var indices = [];
             indices.push(0);
@@ -44,10 +45,10 @@ var BABYLON;
             indices.push(0);
             indices.push(2);
             indices.push(3);
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = engine.createIndexBuffer(indices);
             // Effects
-            this._effect = this._scene.getEngine().createEffect("layer", ["position"], ["textureMatrix", "color", "scale", "offset"], ["textureSampler"], "");
-            this._alphaTestEffect = this._scene.getEngine().createEffect("layer", ["position"], ["textureMatrix", "color", "scale", "offset"], ["textureSampler"], "#define ALPHATEST");
+            this._effect = engine.createEffect("layer", [BABYLON.VertexBuffer.PositionKind], ["textureMatrix", "color", "scale", "offset"], ["textureSampler"], "");
+            this._alphaTestEffect = engine.createEffect("layer", [BABYLON.VertexBuffer.PositionKind], ["textureMatrix", "color", "scale", "offset"], ["textureSampler"], "#define ALPHATEST");
         }
         Object.defineProperty(Layer.prototype, "onDispose", {
             set: function (callback) {
@@ -98,7 +99,7 @@ var BABYLON;
             currentEffect.setVector2("offset", this.offset);
             currentEffect.setVector2("scale", this.scale);
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, currentEffect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
             // Draw order
             if (!this._alphaTestEffect) {
                 engine.setAlphaMode(this.alphaBlendingMode);
@@ -111,9 +112,10 @@ var BABYLON;
             this.onAfterRenderObservable.notifyObservers(this);
         };
         Layer.prototype.dispose = function () {
-            if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
-                this._vertexBuffer = null;
+            var vertexBuffer = this._vertexBuffers[BABYLON.VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = null;
             }
             if (this._indexBuffer) {
                 this._scene.getEngine()._releaseBuffer(this._indexBuffer);
@@ -133,6 +135,6 @@ var BABYLON;
             this.onBeforeRenderObservable.clear();
         };
         return Layer;
-    })();
+    }());
     BABYLON.Layer = Layer;
 })(BABYLON || (BABYLON = {}));

+ 0 - 0
src/Layer/babylon.layer.ts


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است