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

update from upstream

nockawa 9 éve
szülő
commit
b21817c7a4
100 módosított fájl, 9139 hozzáadás és 5634 törlés
  1. 1 0
      Tools/Gulp/config.json
  2. 20 20
      dist/preview release/babylon.core.js
  3. 3473 3143
      dist/preview release/babylon.d.ts
  4. 38 37
      dist/preview release/babylon.js
  5. 2894 1096
      dist/preview release/babylon.max.js
  6. 38 37
      dist/preview release/babylon.noworker.js
  7. 6 4
      dist/preview release/what's new.md
  8. 0 113
      external references/poly2tri.d.ts
  9. 1 1
      src/Actions/babylon.action.js
  10. 2 2
      src/Actions/babylon.actionManager.js
  11. 4 4
      src/Actions/babylon.condition.js
  12. 12 12
      src/Actions/babylon.directActions.js
  13. 1 1
      src/Actions/babylon.interpolateValueAction.js
  14. 1 1
      src/Animations/babylon.animatable.js
  15. 4 4
      src/Animations/babylon.animation.js
  16. 13 13
      src/Animations/babylon.easing.js
  17. 1 1
      src/Audio/babylon.analyser.js
  18. 1 1
      src/Audio/babylon.audioEngine.js
  19. 9 1
      src/Audio/babylon.sound.js
  20. 9 0
      src/Audio/babylon.sound.ts
  21. 1 1
      src/Audio/babylon.soundtrack.js
  22. 24 12
      src/Bones/babylon.bone.js
  23. 27 12
      src/Bones/babylon.bone.ts
  24. 2 1
      src/Bones/babylon.skeleton.js
  25. 2 0
      src/Bones/babylon.skeleton.ts
  26. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.gamepad.js
  27. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js
  28. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.mousewheel.js
  29. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js
  30. 1 1
      src/Cameras/Inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js
  31. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.js
  32. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.gamepad.js
  33. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.keyboard.js
  34. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.mouse.js
  35. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.touch.js
  36. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.virtualjoystick.js
  37. 1 1
      src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.js
  38. 1 1
      src/Cameras/VR/babylon.vrCameraMetrics.js
  39. 2 2
      src/Cameras/VR/babylon.vrDeviceOrientationCamera.js
  40. 1 1
      src/Cameras/VR/babylon.webVRCamera.js
  41. 1 1
      src/Cameras/babylon.arcRotateCamera.js
  42. 1 1
      src/Cameras/babylon.arcRotateCameraInputsManager.js
  43. 1 1
      src/Cameras/babylon.camera.js
  44. 1 1
      src/Cameras/babylon.cameraInputsManager.js
  45. 1 1
      src/Cameras/babylon.deviceOrientationCamera.js
  46. 2 2
      src/Cameras/babylon.followCamera.js
  47. 1 1
      src/Cameras/babylon.freeCamera.js
  48. 1 1
      src/Cameras/babylon.freeCameraInputsManager.js
  49. 1 1
      src/Cameras/babylon.gamepadCamera.js
  50. 8 8
      src/Cameras/babylon.stereoscopicCameras.js
  51. 1 1
      src/Cameras/babylon.targetCamera.js
  52. 1 1
      src/Cameras/babylon.touchCamera.js
  53. 1 1
      src/Cameras/babylon.universalCamera.js
  54. 1 1
      src/Cameras/babylon.virtualJoysticksCamera.js
  55. 13 8
      src/Canvas2d/babylon.bounding2d.js
  56. 11 5
      src/Canvas2d/babylon.bounding2d.ts
  57. 3 3
      src/Canvas2d/babylon.brushes2d.js
  58. 134 53
      src/Canvas2d/babylon.canvas2d.js
  59. 106 38
      src/Canvas2d/babylon.canvas2d.ts
  60. 101 56
      src/Canvas2d/babylon.ellipse2d.js
  61. 20 2
      src/Canvas2d/babylon.ellipse2d.ts
  62. 363 98
      src/Canvas2d/babylon.group2d.js
  63. 18 3
      src/Canvas2d/babylon.group2d.ts
  64. 218 121
      src/Canvas2d/babylon.lines2d.js
  65. 330 247
      src/Canvas2d/babylon.lines2d.ts
  66. 168 40
      src/Canvas2d/babylon.modelRenderCache.js
  67. 258 25
      src/Canvas2d/babylon.prim2dBase.js
  68. 21 19
      src/Canvas2d/babylon.prim2dBase.ts
  69. 150 64
      src/Canvas2d/babylon.rectangle2d.js
  70. 87 3
      src/Canvas2d/babylon.rectangle2d.ts
  71. 261 122
      src/Canvas2d/babylon.renderablePrim2d.js
  72. 4 5
      src/Canvas2d/babylon.renderablePrim2d.ts
  73. 4 5
      src/Canvas2d/babylon.shape2d.js
  74. 11 8
      src/Canvas2d/babylon.smartPropertyPrim.js
  75. 82 44
      src/Canvas2d/babylon.sprite2d.js
  76. 49 14
      src/Canvas2d/babylon.sprite2d.ts
  77. 60 69
      src/Canvas2d/babylon.text2d.js
  78. 20 2
      src/Canvas2d/babylon.text2d.ts
  79. 1 1
      src/Canvas2d/babylon.worldSpaceCanvas2d.js
  80. 1 1
      src/Collisions/babylon.collider.js
  81. 2 2
      src/Collisions/babylon.collisionCoordinator.js
  82. 3 3
      src/Collisions/babylon.collisionWorker.js
  83. 2 2
      src/Collisions/babylon.pickingInfo.js
  84. 1 1
      src/Culling/Octrees/babylon.octree.js
  85. 1 1
      src/Culling/Octrees/babylon.octreeBlock.js
  86. 1 1
      src/Culling/babylon.boundingBox.js
  87. 1 1
      src/Culling/babylon.boundingInfo.js
  88. 1 1
      src/Culling/babylon.boundingSphere.js
  89. 1 1
      src/Culling/babylon.ray.js
  90. 1 1
      src/Debug/babylon.debugLayer.js
  91. 1 1
      src/Debug/babylon.skeletonViewer.js
  92. 1 1
      src/Layer/babylon.layer.js
  93. 1 1
      src/LensFlare/babylon.lensFlare.js
  94. 1 1
      src/LensFlare/babylon.lensFlareSystem.js
  95. 1 1
      src/Lights/Shadows/babylon.shadowGenerator.js
  96. 1 1
      src/Lights/babylon.directionalLight.js
  97. 1 1
      src/Lights/babylon.hemisphericLight.js
  98. 1 1
      src/Lights/babylon.light.js
  99. 1 1
      src/Lights/babylon.pointLight.js
  100. 0 0
      src/Lights/babylon.spotLight.js

+ 1 - 0
Tools/Gulp/config.json

@@ -180,6 +180,7 @@
       "../../src/PostProcess/babylon.displayPassPostProcess.js",
       "../../src/PostProcess/babylon.displayPassPostProcess.js",
       "../../src/Mesh/babylon.meshSimplification.js",
       "../../src/Mesh/babylon.meshSimplification.js",
       "../../src/Tools/babylon.sceneSerializer.js",
       "../../src/Tools/babylon.sceneSerializer.js",
+      "../../src/Tools/babylon.earcut.js",
       "../../src/Mesh/babylon.csg.js",
       "../../src/Mesh/babylon.csg.js",
       "../../src/PostProcess/babylon.vrDistortionCorrectionPostProcess.js",
       "../../src/PostProcess/babylon.vrDistortionCorrectionPostProcess.js",
       "../../src/Tools/babylon.virtualJoystick.js",
       "../../src/Tools/babylon.virtualJoystick.js",

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


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


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


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


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


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

@@ -17,9 +17,10 @@
     - 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 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))
     - 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))
     - 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))
     - 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)) 
+    - 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)) 
+    - Now supporting the [Earcut](https://github.com/mapbox/earcut) polygon triangulation library as part of babylon.js library. (Look for the `Earcut` module). The `PolygonMeshBuilder` class now relies on Earcut. ([nockawa](https://github.com/nockawa))	
   - **Updates**
   - **Updates**
     - Added `renderTargetTexture.useCameraPostProcesses` to control postprocesses for render targets ([deltakosh](https://github.com/deltakosh))
     - Added `renderTargetTexture.useCameraPostProcesses` to control postprocesses for render targets ([deltakosh](https://github.com/deltakosh))
     - Added `mesh.toLefthanded()` to convert a mesh from right handed system ([kesshi](https://github.com/Kesshi))
     - Added `mesh.toLefthanded()` to convert a mesh from right handed system ([kesshi](https://github.com/Kesshi))
@@ -27,6 +28,7 @@
     - Added support for various normal maps conventions ([deltakosh](https://github.com/deltakosh))
     - Added support for various normal maps conventions ([deltakosh](https://github.com/deltakosh))
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
     - Enabled other post processes to be used when also using a 3D Rig ([jcpalmer](https://github.com/Palmer-JC))
     - Enabled other post processes to be used when also using a 3D Rig ([jcpalmer](https://github.com/Palmer-JC))
+    - Got Skeleton.copyAminationRange scaling better for different bone lengths ([jcpalmer](https://github.com/Palmer-JC))
     - Added skeleton.getBoneIndexByName(boneName: string) ([dad72](https://github.com/dad72))
     - Added skeleton.getBoneIndexByName(boneName: string) ([dad72](https://github.com/dad72))
     - Added node._children to track children hierarchy ([deltakosh](https://github.com/deltakosh))
     - Added node._children to track children hierarchy ([deltakosh](https://github.com/deltakosh))
     - Added Camera.ForceAttachControlToAlwaysPreventDefault to help embedding Babylon.js in iFrames ([deltakosh](https://github.com/deltakosh))
     - Added Camera.ForceAttachControlToAlwaysPreventDefault to help embedding Babylon.js in iFrames ([deltakosh](https://github.com/deltakosh))
@@ -60,7 +62,7 @@
     - Unity exporter now support skeletons ([sebavan](https://github.com/sebavan))
     - Unity exporter now support skeletons ([sebavan](https://github.com/sebavan))
     - Support for 3dsmax 2017 ([deltakosh](https://github.com/deltakosh))
     - Support for 3dsmax 2017 ([deltakosh](https://github.com/deltakosh))
     - Added support for up to 8 bones influences per vertex for 3dsmax exporter ([deltakosh](https://github.com/deltakosh))
     - Added support for up to 8 bones influences per vertex for 3dsmax exporter ([deltakosh](https://github.com/deltakosh))
-    - Added console logging for .babylon file loading ([jcpalmer](https://github.com/Palmer-JC))
+    - Added console logging for .babylon file loading & depreciated SceneLoader.Load() in favor of Append() ([jcpalmer](https://github.com/Palmer-JC))
   - **API doc**
   - **API doc**
     - class `SolidParticleSystem` documented ([jerome](https://github.com/jbousquie))
     - class `SolidParticleSystem` documented ([jerome](https://github.com/jbousquie))
     - class `MeshBuilder` documented ([jerome](https://github.com/jbousquie))
     - class `MeshBuilder` documented ([jerome](https://github.com/jbousquie))

+ 0 - 113
external references/poly2tri.d.ts

@@ -1,113 +0,0 @@
-// Type definitions for poly2tri v0.9.10
-// Project: http://github.com/r3mi/poly2tri.js/
-// Definitions by: Elemar Junior <https://github.com/elemarjr/>
-// Definitions: https://github.com/borisyankov/DefinitelyTyped
-
-declare module poly2tri {
-
-    interface IPointLike {
-        x: number;
-        y: number;
-    }
-
-    class Point implements IPointLike {
-
-        x: number;
-        y: number;
-
-        constructor(x: number, y: number);
-
-        toString(): string;
-
-        toJSON(): JSON;
-
-        clone(): Point;
-
-        set_zero(): Point;
-
-        set(x: number, y: number): Point;
-
-        negate(): Point;
-
-        add(n: IPointLike): Point;
-        
-        sub(n: IPointLike): Point;
-
-        mul(s: number): Point;
-
-        length(): number;
-
-        normalize(): number;
-
-        equals(p: IPointLike): boolean;
-
-        static negate(p: IPointLike): Point;
-
-        static add(a: IPointLike, b: IPointLike): Point;
-
-        static sub(a: IPointLike, b: IPointLike): Point;
-
-        static mul(s: number, p: IPointLike): Point;
-
-        static cross(a: number, b: number): number;
-
-        static cross(a: IPointLike, b: number): number;
-
-        static cross(a: IPointLike, b: IPointLike): number;
-
-        static cross(a: number, b: IPointLike): number;
-
-        static toStringBase(p: IPointLike): string;
-
-        static toString(p: IPointLike): string;
-
-        static compare(a: IPointLike, b: IPointLike): number;
-
-        static equals(a: IPointLike, b: IPointLike): boolean;
-
-        static dot(a: IPointLike, b: IPointLike): number;
-    }
-
-
-    class SweepContext {
-        constructor(contour: Array<IPointLike>);
-
-        constructor(contour: Array<IPointLike>, options: JSON);
-
-        addHole(polyline: Array<IPointLike>): SweepContext;
-
-        addHoles(holes: Array<Array<IPointLike>>): SweepContext;
-
-        addPoint(point: IPointLike): SweepContext;
-
-        addPoints(point: Array<IPointLike>): SweepContext;
-
-        triangulate(): SweepContext;
-
-        getBoundingBox(): { min: IPointLike; max: IPointLike; };
-
-        getTriangles(): Array<Triangle>;
-    }
-
-    class Triangle {
-        constructor(a: IPointLike, b: IPointLike, c: IPointLike);
-
-        toString(): string;
-
-        getPoint(index: number): IPointLike;
-
-        getPoints(): Array<IPointLike>;
-
-        containsPoint(point: IPointLike): boolean;
-
-        containsPoints(p1: IPointLike, p2: IPointLike): boolean;
-
-        isInterior(): boolean;
-    }
-}
-
-
-
-
-
-

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -381,6 +381,7 @@ var BABYLON;
                 else {
                 else {
                     var stopTime = time ? BABYLON.Engine.audioEngine.audioContext.currentTime + time : BABYLON.Engine.audioEngine.audioContext.currentTime;
                     var stopTime = time ? BABYLON.Engine.audioEngine.audioContext.currentTime + time : BABYLON.Engine.audioEngine.audioContext.currentTime;
                     this._soundSource.stop(stopTime);
                     this._soundSource.stop(stopTime);
+                    this._soundSource.onended = null;
                     if (!this.isPaused) {
                     if (!this.isPaused) {
                         this._startOffset = 0;
                         this._startOffset = 0;
                     }
                     }
@@ -446,6 +447,13 @@ var BABYLON;
             this._registerFunc = function (connectedMesh) { return _this._onRegisterAfterWorldMatrixUpdate(connectedMesh); };
             this._registerFunc = function (connectedMesh) { return _this._onRegisterAfterWorldMatrixUpdate(connectedMesh); };
             meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
             meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
         };
         };
+        Sound.prototype.detachFromMesh = function () {
+            if (this._connectedMesh) {
+                this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
+                this._registerFunc = null;
+                this._connectedMesh = null;
+            }
+        };
         Sound.prototype._onRegisterAfterWorldMatrixUpdate = function (connectedMesh) {
         Sound.prototype._onRegisterAfterWorldMatrixUpdate = function (connectedMesh) {
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
             if (BABYLON.Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {
             if (BABYLON.Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {
@@ -547,6 +555,6 @@ var BABYLON;
             return newSound;
             return newSound;
         };
         };
         return Sound;
         return Sound;
-    }());
+    })();
     BABYLON.Sound = Sound;
     BABYLON.Sound = Sound;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 9 - 0
src/Audio/babylon.sound.ts

@@ -416,6 +416,7 @@
                 else {
                 else {
                     var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                     var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                     this._soundSource.stop(stopTime);
                     this._soundSource.stop(stopTime);
+                    this._soundSource.onended = null;
                     if (!this.isPaused) {
                     if (!this.isPaused) {
                         this._startOffset = 0;
                         this._startOffset = 0;
                     }
                     }
@@ -486,6 +487,14 @@
             meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
             meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
         }
         }
 
 
+        public detachFromMesh() {
+            if (this._connectedMesh) {
+                this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
+                this._registerFunc = null;
+                this._connectedMesh = null;
+            }
+        }
+
         private _onRegisterAfterWorldMatrixUpdate(connectedMesh: AbstractMesh) {
         private _onRegisterAfterWorldMatrixUpdate(connectedMesh: AbstractMesh) {
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
             if (Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {
             if (Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {

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

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

+ 24 - 12
src/Bones/babylon.bone.js

@@ -80,8 +80,9 @@ var BABYLON;
             this._currentRenderId++;
             this._currentRenderId++;
             this._skeleton._markAsDirty();
             this._skeleton._markAsDirty();
         };
         };
-        Bone.prototype.copyAnimationRange = function (source, rangeName, frameOffset, rescaleAsRequired) {
+        Bone.prototype.copyAnimationRange = function (source, rangeName, frameOffset, rescaleAsRequired, skelDimensionsRatio) {
             if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
             if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
+            if (skelDimensionsRatio === void 0) { skelDimensionsRatio = null; }
             // all animation may be coming from a library skeleton, so may need to create animation
             // all animation may be coming from a library skeleton, so may need to create animation
             if (this.animations.length === 0) {
             if (this.animations.length === 0) {
                 this.animations.push(new BABYLON.Animation(this.name, "_matrix", source.animations[0].framePerSecond, BABYLON.Animation.ANIMATIONTYPE_MATRIX, 0));
                 this.animations.push(new BABYLON.Animation(this.name, "_matrix", source.animations[0].framePerSecond, BABYLON.Animation.ANIMATIONTYPE_MATRIX, 0));
@@ -97,22 +98,33 @@ var BABYLON;
             var sourceKeys = source.animations[0].getKeys();
             var sourceKeys = source.animations[0].getKeys();
             // rescaling prep
             // rescaling prep
             var sourceBoneLength = source.length;
             var sourceBoneLength = source.length;
-            var scalingReqd = rescaleAsRequired && sourceBoneLength && this.length && sourceBoneLength !== this.length;
-            var ratio = scalingReqd ? this.length / sourceBoneLength : null;
+            var sourceParent = source.getParent();
+            var parent = this.getParent();
+            var parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
+            var parentRatio = parentScalingReqd ? parent.length / sourceParent.length : null;
+            var dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);
             var destKeys = this.animations[0].getKeys();
             var destKeys = this.animations[0].getKeys();
-            // loop vars declaration / initialization
+            // loop vars declaration
             var orig;
             var orig;
-            var origScale = scalingReqd ? BABYLON.Vector3.Zero() : null;
-            var origRotation = scalingReqd ? new BABYLON.Quaternion() : null;
-            var origTranslation = scalingReqd ? BABYLON.Vector3.Zero() : null;
+            var origTranslation;
             var mat;
             var mat;
             for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
             for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
                 orig = sourceKeys[key];
                 orig = sourceKeys[key];
                 if (orig.frame >= from && orig.frame <= to) {
                 if (orig.frame >= from && orig.frame <= to) {
-                    if (scalingReqd) {
-                        orig.value.decompose(origScale, origRotation, origTranslation);
-                        origTranslation.scaleInPlace(ratio);
-                        mat = BABYLON.Matrix.Compose(origScale, origRotation, origTranslation);
+                    if (rescaleAsRequired) {
+                        mat = orig.value.clone();
+                        // scale based on parent ratio, when bone has parent
+                        if (parentScalingReqd) {
+                            origTranslation = mat.getTranslation();
+                            mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
+                        }
+                        else if (dimensionsScalingReqd) {
+                            origTranslation = mat.getTranslation();
+                            mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));
+                        }
+                        else {
+                            mat = orig.value;
+                        }
                     }
                     }
                     else {
                     else {
                         mat = orig.value;
                         mat = orig.value;
@@ -124,6 +136,6 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Bone;
         return Bone;
-    }(BABYLON.Node));
+    })(BABYLON.Node);
     BABYLON.Bone = Bone;
     BABYLON.Bone = Bone;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 27 - 12
src/Bones/babylon.bone.ts

@@ -98,7 +98,7 @@
             this._skeleton._markAsDirty();
             this._skeleton._markAsDirty();
         }
         }
 
 
-        public copyAnimationRange(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired = false): boolean {
+        public copyAnimationRange(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired = false, skelDimensionsRatio : Vector3 = null): boolean {
             // all animation may be coming from a library skeleton, so may need to create animation
             // all animation may be coming from a library skeleton, so may need to create animation
             if (this.animations.length === 0) {
             if (this.animations.length === 0) {
                 this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
                 this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
@@ -116,25 +116,40 @@
             
             
             // rescaling prep
             // rescaling prep
             var sourceBoneLength = source.length;
             var sourceBoneLength = source.length;
-            var scalingReqd = rescaleAsRequired && sourceBoneLength && this.length && sourceBoneLength !== this.length;
-            var ratio = scalingReqd ? this.length / sourceBoneLength : null;
-
+            var sourceParent = source.getParent();
+            var parent = this.getParent();
+            var parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
+            var parentRatio = parentScalingReqd ? parent.length / sourceParent.length : null;
+            
+            var dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);           
+            
             var destKeys = this.animations[0].getKeys();
             var destKeys = this.animations[0].getKeys();
             
             
-            // loop vars declaration / initialization
+            // loop vars declaration
             var orig: { frame: number, value: Matrix };
             var orig: { frame: number, value: Matrix };
-            var origScale = scalingReqd ? Vector3.Zero() : null;
-            var origRotation = scalingReqd ? new Quaternion() : null;
-            var origTranslation = scalingReqd ? Vector3.Zero() : null;
+            var origTranslation : Vector3;
             var mat: Matrix;
             var mat: Matrix;
 
 
             for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
             for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
                 orig = sourceKeys[key];
                 orig = sourceKeys[key];
                 if (orig.frame >= from && orig.frame <= to) {
                 if (orig.frame >= from && orig.frame <= to) {
-                    if (scalingReqd) {
-                        orig.value.decompose(origScale, origRotation, origTranslation);
-                        origTranslation.scaleInPlace(ratio);
-                        mat = Matrix.Compose(origScale, origRotation, origTranslation);
+                    if (rescaleAsRequired) {
+                        mat = orig.value.clone();
+                        
+                        // scale based on parent ratio, when bone has parent
+                        if (parentScalingReqd) {
+                            origTranslation = mat.getTranslation();
+                            mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
+                            
+                        // scale based on skeleton dimension ratio when root bone, and value is passed
+                        } else if (dimensionsScalingReqd) {
+                            origTranslation = mat.getTranslation();
+                            mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));                            
+
+                        // use original when root bone, and no data for skelDimensionsRatio
+                        } else {
+                            mat = orig.value;                            
+                        }
                     } else {
                     } else {
                         mat = orig.value;
                         mat = orig.value;
                     }
                     }

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

@@ -118,6 +118,7 @@ var BABYLON;
                 BABYLON.Tools.Warn("copyAnimationRange: this rig has " + this.bones.length + " bones, while source as " + sourceBones.length);
                 BABYLON.Tools.Warn("copyAnimationRange: this rig has " + this.bones.length + " bones, while source as " + sourceBones.length);
                 ret = false;
                 ret = false;
             }
             }
+            var skelDimensionsRatio = (rescaleAsRequired && this.dimensionsAtRest && source.dimensionsAtRest) ? this.dimensionsAtRest.divide(source.dimensionsAtRest) : null;
             for (i = 0, nBones = this.bones.length; i < nBones; i++) {
             for (i = 0, nBones = this.bones.length; i < nBones; i++) {
                 var boneName = this.bones[i].name;
                 var boneName = this.bones[i].name;
                 var sourceBone = boneDict[boneName];
                 var sourceBone = boneDict[boneName];
@@ -333,6 +334,6 @@ var BABYLON;
             return skeleton;
             return skeleton;
         };
         };
         return Skeleton;
         return Skeleton;
-    }());
+    })();
     BABYLON.Skeleton = Skeleton;
     BABYLON.Skeleton = Skeleton;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 2 - 0
src/Bones/babylon.skeleton.ts

@@ -135,6 +135,8 @@
                 Tools.Warn(`copyAnimationRange: this rig has ${this.bones.length} bones, while source as ${sourceBones.length}`);
                 Tools.Warn(`copyAnimationRange: this rig has ${this.bones.length} bones, while source as ${sourceBones.length}`);
                 ret = false;
                 ret = false;
             }
             }
+            
+            var skelDimensionsRatio = (rescaleAsRequired && this.dimensionsAtRest && source.dimensionsAtRest) ? this.dimensionsAtRest.divide(source.dimensionsAtRest) : null;
 
 
             for (i = 0, nBones = this.bones.length; i < nBones; i++) {
             for (i = 0, nBones = this.bones.length; i < nBones; i++) {
                 var boneName = this.bones[i].name;
                 var boneName = this.bones[i].name;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 13 - 8
src/Canvas2d/babylon.bounding2d.js

@@ -45,8 +45,8 @@ var BABYLON;
         };
         };
         BoundingInfo2D.CreateFromPointsToRef = function (points, b, origin) {
         BoundingInfo2D.CreateFromPointsToRef = function (points, b, origin) {
             var xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
             var xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
-            for (var _i = 0, points_1 = points; _i < points_1.length; _i++) {
-                var p = points_1[_i];
+            for (var _i = 0; _i < points.length; _i++) {
+                var p = points[_i];
                 xmin = Math.min(p.x, xmin);
                 xmin = Math.min(p.x, xmin);
                 xmax = Math.max(p.x, xmax);
                 xmax = Math.max(p.x, xmax);
                 ymin = Math.min(p.y, ymin);
                 ymin = Math.min(p.y, ymin);
@@ -113,11 +113,15 @@ var BABYLON;
          */
          */
         BoundingInfo2D.prototype.transformToRef = function (matrix, result) {
         BoundingInfo2D.prototype.transformToRef = function (matrix, result) {
             // Construct a bounding box based on the extent values
             // Construct a bounding box based on the extent values
-            var p = new Array(4);
-            p[0] = new BABYLON.Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
-            p[1] = new BABYLON.Vector2(this.center.x + this.extent.x, this.center.y - this.extent.y);
-            p[2] = new BABYLON.Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
-            p[3] = new BABYLON.Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
+            var p = BoundingInfo2D._transform;
+            p[0].x = this.center.x + this.extent.x;
+            p[0].y = this.center.y + this.extent.y;
+            p[1].x = this.center.x + this.extent.x;
+            p[1].y = this.center.y - this.extent.y;
+            p[2].x = this.center.x - this.extent.x;
+            p[2].y = this.center.y - this.extent.y;
+            p[3].x = this.center.x - this.extent.x;
+            p[3].y = this.center.y + this.extent.y;
             // Transform the four points of the bounding box with the matrix
             // Transform the four points of the bounding box with the matrix
             for (var i = 0; i < 4; i++) {
             for (var i = 0; i < 4; i++) {
                 BABYLON.Vector2.TransformToRef(p[i], matrix, p[i]);
                 BABYLON.Vector2.TransformToRef(p[i], matrix, p[i]);
@@ -146,7 +150,8 @@ var BABYLON;
             }
             }
             return false;
             return false;
         };
         };
+        BoundingInfo2D._transform = new Array(BABYLON.Vector2.Zero(), BABYLON.Vector2.Zero(), BABYLON.Vector2.Zero(), BABYLON.Vector2.Zero());
         return BoundingInfo2D;
         return BoundingInfo2D;
-    }());
+    })();
     BABYLON.BoundingInfo2D = BoundingInfo2D;
     BABYLON.BoundingInfo2D = BoundingInfo2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 11 - 5
src/Canvas2d/babylon.bounding2d.ts

@@ -135,6 +135,8 @@
             return r;
             return r;
         }
         }
 
 
+        private static _transform: Array<Vector2> = new Array<Vector2>(Vector2.Zero(), Vector2.Zero(), Vector2.Zero(), Vector2.Zero());
+
         /**
         /**
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
          * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
          * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
@@ -143,11 +145,15 @@
          */
          */
         public transformToRef(matrix: Matrix, result: BoundingInfo2D) {
         public transformToRef(matrix: Matrix, result: BoundingInfo2D) {
             // Construct a bounding box based on the extent values
             // Construct a bounding box based on the extent values
-            let p = new Array<Vector2>(4);
-            p[0] = new Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
-            p[1] = new Vector2(this.center.x + this.extent.x, this.center.y - this.extent.y);
-            p[2] = new Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
-            p[3] = new Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
+            let p = BoundingInfo2D._transform;
+            p[0].x = this.center.x + this.extent.x;
+            p[0].y = this.center.y + this.extent.y;
+            p[1].x = this.center.x + this.extent.x;
+            p[1].y = this.center.y - this.extent.y;
+            p[2].x = this.center.x - this.extent.x;
+            p[2].y = this.center.y - this.extent.y;
+            p[3].x = this.center.x - this.extent.x;
+            p[3].y = this.center.y + this.extent.y;
 
 
             // Transform the four points of the bounding box with the matrix
             // Transform the four points of the bounding box with the matrix
             for (let i = 0; i < 4; i++) {
             for (let i = 0; i < 4; i++) {

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

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

+ 134 - 53
src/Canvas2d/babylon.canvas2d.js

@@ -26,12 +26,33 @@ var BABYLON;
             return true;
             return true;
         };
         };
         return Canvas2DEngineBoundData;
         return Canvas2DEngineBoundData;
-    }());
+    })();
     BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
     BABYLON.Canvas2DEngineBoundData = Canvas2DEngineBoundData;
     var Canvas2D = (function (_super) {
     var Canvas2D = (function (_super) {
         __extends(Canvas2D, _super);
         __extends(Canvas2D, _super);
         function Canvas2D() {
         function Canvas2D() {
+            var _this = this;
             _super.apply(this, arguments);
             _super.apply(this, arguments);
+            /**
+             * If you set your own WorldSpaceNode to display the Canvas2D you have to provide your own implementation of this method which computes the local position in the Canvas based on the given 3D World one.
+             * Beware that you have to take under consideration the origin and the renderScaleFactor in your calculations! Good luck!
+             */
+            this.worldSpaceToNodeLocal = function (worldPos) {
+                var node = _this._worldSpaceNode;
+                if (!node) {
+                    return;
+                }
+                var mtx = node.getWorldMatrix().clone();
+                mtx.invert();
+                var v = BABYLON.Vector3.TransformCoordinates(worldPos, mtx);
+                var rsf = _this._renderScaleFactor;
+                var res = new BABYLON.Vector2(v.x * rsf, v.y * rsf);
+                var size = _this.actualSize;
+                var o = _this.origin;
+                res.x += size.width * o.x;
+                res.y += size.width * o.y;
+                return res;
+            };
             this._notifDebugMode = false;
             this._notifDebugMode = false;
             this._mapCounter = 0;
             this._mapCounter = 0;
         }
         }
@@ -43,16 +64,27 @@ var BABYLON;
          * @param scene the Scene that owns the Canvas
          * @param scene the Scene that owns the Canvas
          * Options:
          * Options:
          *  - id: a text identifier, for information purpose only
          *  - 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.
+         *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport. Alternatively you can set the x and y properties directly. Default value is [0, 0]
+         *  - size: the Size of the canvas. Alternatively the width and height properties can be set. 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_DONTCACHE
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is true.
+         *  - isVisible: true if the canvas must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
          */
         Canvas2D.CreateScreenSpace = function (scene, options) {
         Canvas2D.CreateScreenSpace = function (scene, options) {
             var c = new Canvas2D();
             var c = new Canvas2D();
-            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();
+            if (!options) {
+                c.setupCanvas(scene, null, null, 1, true, Canvas2D.CACHESTRATEGY_DONTCACHE, true, BABYLON.Vector2.Zero(), true, null, null, null, null, null, null);
+                c.position = BABYLON.Vector2.Zero();
+            }
+            else {
+                var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
+                var size = (!options.size && !options.width && !options.height) ? null : (options.size || (new BABYLON.Size(options.width || 0, options.height || 0)));
+                c.setupCanvas(scene, options.id || null, size, 1, true, options.cachingStrategy || Canvas2D.CACHESTRATEGY_DONTCACHE, options.enableInteraction || true, options.origin || BABYLON.Vector2.Zero(), options.isVisible || true, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || BABYLON.Prim2DBase.HAlignLeft, options.vAlignment || BABYLON.Prim2DBase.VAlignTop);
+                c.position = pos;
+            }
             return c;
             return c;
         };
         };
         /**
         /**
@@ -69,6 +101,9 @@ var BABYLON;
          * 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.
          * 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.
          * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
          * - 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.
          * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
+         * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
+         * - customWorldSpaceNode: if specified the Canvas will be rendered in this given Node. But it's the responsibility of the caller to set the "worldSpaceToNodeLocal" property to compute the hit of the mouse ray into the node (in world coordinate system) as well as rendering the cached bitmap in the node itself. The properties cachedRect and cachedTexture of Group2D will give you what you need to do that.
          */
          */
         Canvas2D.CreateWorldSpace = function (scene, size, options) {
         Canvas2D.CreateWorldSpace = function (scene, size, options) {
             var cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
             var cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
@@ -78,38 +113,55 @@ var BABYLON;
             //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
             //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
             //}
-            var id = options && options.id || null;
-            var rsf = options && options.renderScaleFactor || 1;
+            var enableInteraction = options ? options.enableInteraction : true;
+            var createWorldSpaceNode = !options || (options.customWorldSpaceNode == null);
+            var isVisible = options ? options.isVisible || true : true;
+            var id = options ? options.id || null : null;
+            var rsf = options ? options.renderScaleFactor || 1 : 1;
             var c = new Canvas2D();
             var c = new Canvas2D();
-            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 = options && options.position || BABYLON.Vector3.Zero();
-            plane.rotationQuaternion = options && options.rotation || BABYLON.Quaternion.Identity();
-            plane.material = mtl;
-            c._worldSpaceNode = plane;
+            c.setupCanvas(scene, id, new BABYLON.Size(size.width, size.height), rsf, false, cs, enableInteraction, new BABYLON.Vector2(0.5, 0.5), isVisible, null, null, null, null, null, null);
+            if (createWorldSpaceNode) {
+                var plane = new BABYLON.WorldSpaceCanvas2D(id, scene, c);
+                var vertexData = BABYLON.VertexData.CreatePlane({
+                    width: size.width,
+                    height: size.height,
+                    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 = options && options.position || BABYLON.Vector3.Zero();
+                plane.rotationQuaternion = options && options.rotation || BABYLON.Quaternion.Identity();
+                plane.material = mtl;
+                c._worldSpaceNode = plane;
+            }
+            else {
+                c._worldSpaceNode = options.customWorldSpaceNode;
+            }
             return c;
             return c;
         };
         };
-        Canvas2D.prototype.setupCanvas = function (scene, name, size, isScreenSpace, cachingstrategy, enableInteraction) {
+        Canvas2D.prototype.setupCanvas = function (scene, name, size, renderScaleFactor, isScreenSpace, cachingstrategy, enableInteraction, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign) {
             var _this = this;
             var _this = this;
             var engine = scene.getEngine();
             var engine = scene.getEngine();
             this._fitRenderingDevice = !size;
             this._fitRenderingDevice = !size;
             if (!size) {
             if (!size) {
                 size = new BABYLON.Size(engine.getRenderWidth(), engine.getRenderHeight());
                 size = new BABYLON.Size(engine.getRenderWidth(), engine.getRenderHeight());
             }
             }
+            else {
+                size.height *= renderScaleFactor;
+                size.width *= renderScaleFactor;
+            }
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", function (k) { return new Canvas2DEngineBoundData(); });
+            this._renderScaleFactor = renderScaleFactor;
             this._cachingStrategy = cachingstrategy;
             this._cachingStrategy = cachingstrategy;
             this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
             this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
             this._capturedPointers = new BABYLON.StringDictionary();
             this._capturedPointers = new BABYLON.StringDictionary();
             this._pickStartingPosition = BABYLON.Vector2.Zero();
             this._pickStartingPosition = BABYLON.Vector2.Zero();
-            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.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), origin, size, isVisible, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
+            this._hierarchyLevelMaxSiblingCount = 10;
             this._hierarchyDepthOffset = 0;
             this._hierarchyDepthOffset = 0;
             this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
             this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
             this._scene = scene;
             this._scene = scene;
@@ -156,17 +208,42 @@ var BABYLON;
             }
             }
             // Set the new state
             // Set the new state
             this._interactionEnabled = enable;
             this._interactionEnabled = enable;
-            // Disable interaction
-            if (!enable) {
-                if (this._scenePrePointerObserver) {
-                    this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
-                    this._scenePrePointerObserver = null;
+            // ScreenSpace mode
+            if (this._isScreeSpace) {
+                // Disable interaction
+                if (!enable) {
+                    if (this._scenePrePointerObserver) {
+                        this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
+                        this._scenePrePointerObserver = null;
+                    }
+                    return;
+                }
+                // Enable Interaction
+                // Register the observable
+                this._scenePrePointerObserver = this.scene.onPrePointerObservable.add(function (e, s) {
+                    var hs = 1 / _this.engine.getHardwareScalingLevel();
+                    var localPos = e.localPosition.multiplyByFloats(hs, hs);
+                    _this._handlePointerEventForInteraction(e, localPos, s);
+                });
+            }
+            else {
+                var scene = this.scene;
+                if (enable) {
+                    scene.constantlyUpdateMeshUnderPointer = true;
+                    this._scenePointerObserver = scene.onPointerObservable.add(function (e, s) {
+                        if (e.pickInfo.hit && e.pickInfo.pickedMesh === _this._worldSpaceNode && _this.worldSpaceToNodeLocal) {
+                            var localPos = _this.worldSpaceToNodeLocal(e.pickInfo.pickedPoint);
+                            _this._handlePointerEventForInteraction(e, localPos, s);
+                        }
+                    });
+                }
+                else {
+                    if (this._scenePointerObserver) {
+                        this.scene.onPointerObservable.remove(this._scenePointerObserver);
+                        this._scenePointerObserver = null;
+                    }
                 }
                 }
-                return;
             }
             }
-            // Enable Interaction
-            // Register the observable
-            this.scene.onPrePointerObservable.add(function (e, s) { return _this._handlePointerEventForInteraction(e, s); });
         };
         };
         /**
         /**
          * Internal method, you should use the Prim2DBase version instead
          * Internal method, you should use the Prim2DBase version instead
@@ -219,13 +296,13 @@ var BABYLON;
             }
             }
             return this._capturedPointers.get(pointerId.toString());
             return this._capturedPointers.get(pointerId.toString());
         };
         };
-        Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, eventState) {
+        Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, localPosition, eventState) {
             // Dispose check
             // Dispose check
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return;
                 return;
             }
             }
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
-            this._updatePointerInfo(eventData);
+            this._updatePointerInfo(eventData, localPosition);
             var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
             var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
             // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
             // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
             // Why before rendering the canvas? because some primitives may move and get away/under the mouse cursor (which is not moving). So we need to update at both location in order to always have an accurate list, which is needed for the hover state change.
             // Why before rendering the canvas? because some primitives may move and get away/under the mouse cursor (which is not moving). So we need to update at both location in order to always have an accurate list, which is needed for the hover state change.
@@ -254,20 +331,26 @@ var BABYLON;
                 this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData.event);
                 this._bubbleNotifyPrimPointerObserver(targetPrim, BABYLON.PrimitivePointerInfo.PointerUp, eventData.event);
             }
             }
         };
         };
-        Canvas2D.prototype._updatePointerInfo = function (eventData) {
+        Canvas2D.prototype._updatePointerInfo = function (eventData, localPosition) {
             var pii = this._primPointerInfo;
             var pii = this._primPointerInfo;
             if (!pii.canvasPointerPos) {
             if (!pii.canvasPointerPos) {
                 pii.canvasPointerPos = BABYLON.Vector2.Zero();
                 pii.canvasPointerPos = BABYLON.Vector2.Zero();
             }
             }
             var camera = this._scene.activeCamera;
             var camera = this._scene.activeCamera;
             var engine = this._scene.getEngine();
             var engine = this._scene.getEngine();
-            var cameraViewport = camera.viewport;
-            var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
-            // Moving coordinates to local viewport world
-            var x = eventData.localPosition.x / engine.getHardwareScalingLevel() - viewport.x;
-            var y = eventData.localPosition.y / engine.getHardwareScalingLevel() - viewport.y;
-            pii.canvasPointerPos.x = x - this.position.x;
-            pii.canvasPointerPos.y = engine.getRenderHeight() - y - this.position.y;
+            if (this._isScreeSpace) {
+                var cameraViewport = camera.viewport;
+                var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+                // Moving coordinates to local viewport world
+                var x = localPosition.x - viewport.x;
+                var y = localPosition.y - viewport.y;
+                pii.canvasPointerPos.x = x - this.position.x;
+                pii.canvasPointerPos.y = engine.getRenderHeight() - y - this.position.y;
+            }
+            else {
+                pii.canvasPointerPos.x = localPosition.x;
+                pii.canvasPointerPos.y = localPosition.y;
+            }
             pii.mouseWheelDelta = 0;
             pii.mouseWheelDelta = 0;
             if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
             if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
                 var event = eventData.event;
                 var event = eventData.event;
@@ -311,7 +394,6 @@ var BABYLON;
                 this._actualOverPrimitive = null;
                 this._actualOverPrimitive = null;
                 return;
                 return;
             }
             }
-            this._updateCanvasState();
             this.intersect(ii);
             this.intersect(ii);
             this._previousIntersectionList = this._actualIntersectionList;
             this._previousIntersectionList = this._actualIntersectionList;
             this._actualIntersectionList = ii.intersectedPrimitives;
             this._actualIntersectionList = ii.intersectedPrimitives;
@@ -565,7 +647,7 @@ var BABYLON;
         });
         });
         Object.defineProperty(Canvas2D.prototype, "worldSpaceCanvasNode", {
         Object.defineProperty(Canvas2D.prototype, "worldSpaceCanvasNode", {
             /**
             /**
-             * Only valid for World Space Canvas, returns the scene node that display the canvas
+             * Only valid for World Space Canvas, returns the scene node that displays the canvas
              */
              */
             get: function () {
             get: function () {
                 return this._worldSpaceNode;
                 return this._worldSpaceNode;
@@ -693,8 +775,7 @@ var BABYLON;
                     this._background.size = this.size;
                     this._background.size = this.size;
                 }
                 }
             }
             }
-            var context = new BABYLON.Render2DContext();
-            context.forceRefreshPrimitive = false;
+            var context = new BABYLON.PrepareRender2DContext();
             ++this._globalTransformProcessStep;
             ++this._globalTransformProcessStep;
             this.updateGlobalTransVis(false);
             this.updateGlobalTransVis(false);
             this._prepareGroupRender(context);
             this._prepareGroupRender(context);
@@ -709,11 +790,11 @@ var BABYLON;
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
             }
             }
-            var context = new BABYLON.Render2DContext();
-            this._groupRender(context);
+            this.engine.setState(false);
+            this._groupRender();
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
             if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS && this._cachedCanvasGroup) {
-                this._cachedCanvasGroup._renderCachedCanvas(context);
+                this._cachedCanvasGroup._renderCachedCanvas();
             }
             }
         };
         };
         /**
         /**
@@ -833,6 +914,6 @@ var BABYLON;
             BABYLON.className("Canvas2D")
             BABYLON.className("Canvas2D")
         ], Canvas2D);
         ], Canvas2D);
         return Canvas2D;
         return Canvas2D;
-    }(BABYLON.Group2D));
+    })(BABYLON.Group2D);
     BABYLON.Canvas2D = Canvas2D;
     BABYLON.Canvas2D = Canvas2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 106 - 38
src/Canvas2d/babylon.canvas2d.ts

@@ -58,7 +58,7 @@
          *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport. Alternatively you can set the x and y properties directly. Default value is [0, 0]
          *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport. Alternatively you can set the x and y properties directly. Default value is [0, 0]
          *  - size: the Size of the canvas. Alternatively the width and height properties can be set. 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
          *  - size: the Size of the canvas. Alternatively the width and height properties can be set. 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_DONTCACHE
          *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_DONTCACHE
-         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property.
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is true.
          *  - isVisible: true if the canvas must be visible, false for hidden. Default is true.
          *  - isVisible: true if the canvas must be visible, false for hidden. Default is true.
          *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
          *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
          *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
@@ -68,13 +68,13 @@
             let c = new Canvas2D();
             let c = new Canvas2D();
 
 
             if (!options) {
             if (!options) {
-                c.setupCanvas(scene, null, null, true, Canvas2D.CACHESTRATEGY_DONTCACHE, true, Vector2.Zero(), true, null, null, null, null, null, null);
+                c.setupCanvas(scene, null, null, 1, true, Canvas2D.CACHESTRATEGY_DONTCACHE, true, Vector2.Zero(), true, null, null, null, null, null, null);
                 c.position = Vector2.Zero();
                 c.position = Vector2.Zero();
             } else { 
             } else { 
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let size = (!options.size && !options.width && !options.height) ? null : (options.size || (new Size(options.width || 0, options.height || 0)));
                 let size = (!options.size && !options.width && !options.height) ? null : (options.size || (new Size(options.width || 0, options.height || 0)));
 
 
-                c.setupCanvas(scene, options.id || null, size, true, options.cachingStrategy || Canvas2D.CACHESTRATEGY_DONTCACHE, options.enableInteraction || true, options.origin || Vector2.Zero(), options.isVisible || true, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || Prim2DBase.HAlignLeft, options.vAlignment || Prim2DBase.VAlignTop);
+                c.setupCanvas(scene, options.id || null, size, 1, true, options.cachingStrategy || Canvas2D.CACHESTRATEGY_DONTCACHE, options.enableInteraction || true, options.origin || Vector2.Zero(), options.isVisible || true, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || Prim2DBase.HAlignLeft, options.vAlignment || Prim2DBase.VAlignTop);
                 c.position = pos;
                 c.position = pos;
             }
             }
 
 
@@ -95,10 +95,11 @@
          * 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.
          * 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.
          * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
          * - 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.
          * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
          * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
          * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
-         * - noWorldSpaceNode: if true there won't be a WorldSpaceNode created for this canvas, you'll be responsible of rendering the canvas texture in the scene.
+         * - customWorldSpaceNode: if specified the Canvas will be rendered in this given Node. But it's the responsibility of the caller to set the "worldSpaceToNodeLocal" property to compute the hit of the mouse ray into the node (in world coordinate system) as well as rendering the cached bitmap in the node itself. The properties cachedRect and cachedTexture of Group2D will give you what you need to do that.
          */
          */
-        static CreateWorldSpace(scene: Scene, size: Size, options: { id?: string, position?: Vector3, rotation?: Quaternion, renderScaleFactor?: number, sideOrientation?: number, cachingStrategy?: number, enableInteraction?: boolean, isVisible?: boolean, noWorldSpaceNode?: boolean}): Canvas2D {
+        static CreateWorldSpace(scene: Scene, size: Size, options: { id?: string, position?: Vector3, rotation?: Quaternion, renderScaleFactor?: number, sideOrientation?: number, cachingStrategy?: number, enableInteraction?: boolean, isVisible?: boolean, customWorldSpaceNode?: Node}): Canvas2D {
 
 
             let cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
             let cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
 
 
@@ -110,16 +111,20 @@
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
             //}
 
 
-            let id = options && options.id || null;
-            let rsf = options && options.renderScaleFactor || 1;
+            let enableInteraction = options ? options.enableInteraction : true;
+            let createWorldSpaceNode = !options || (options.customWorldSpaceNode==null);
+            let isVisible = options ? options.isVisible || true : true;
+            let id = options ? options.id || null : null;
+            let rsf = options ? options.renderScaleFactor || 1 : 1;
+
             let c = new Canvas2D();
             let c = new Canvas2D();
-            c.setupCanvas(scene, id, new Size(size.width * rsf, size.height * rsf), false, cs, options && options.enableInteraction || true, Vector2.Zero(), options && options.isVisible || true, null, null, null, null, null, null);
+            c.setupCanvas(scene, id, new Size(size.width, size.height), rsf, false, cs, enableInteraction, new Vector2(0.5, 0.5), isVisible, null, null, null, null, null, null);
 
 
-            if (!options || !options.noWorldSpaceNode) {
+            if (createWorldSpaceNode) {
                 let plane = new WorldSpaceCanvas2D(id, scene, c);
                 let plane = new WorldSpaceCanvas2D(id, scene, c);
                 let vertexData = VertexData.CreatePlane({
                 let vertexData = VertexData.CreatePlane({
-                    width: size.width / 2,
-                    height: size.height / 2,
+                    width: size.width,
+                    height: size.height,
                     sideOrientation: options && options.sideOrientation || Mesh.DEFAULTSIDE
                     sideOrientation: options && options.sideOrientation || Mesh.DEFAULTSIDE
                 });
                 });
                 let mtl = new StandardMaterial(id + "_Material", scene);
                 let mtl = new StandardMaterial(id + "_Material", scene);
@@ -134,18 +139,23 @@
                 plane.rotationQuaternion = options && options.rotation || Quaternion.Identity();
                 plane.rotationQuaternion = options && options.rotation || Quaternion.Identity();
                 plane.material = mtl;
                 plane.material = mtl;
                 c._worldSpaceNode = plane;
                 c._worldSpaceNode = plane;
+            } else {
+                c._worldSpaceNode = options.customWorldSpaceNode;
             }
             }
-
             return c;
             return c;
         }
         }
 
 
-        protected setupCanvas(scene: Scene, name: string, size: Size, isScreenSpace: boolean, cachingstrategy: number, enableInteraction: boolean, origin: Vector2, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, hAlign: number, vAlign: number) {
+        protected setupCanvas(scene: Scene, name: string, size: Size, renderScaleFactor: number, isScreenSpace: boolean, cachingstrategy: number, enableInteraction: boolean, origin: Vector2, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, hAlign: number, vAlign: number) {
             let engine = scene.getEngine();
             let engine = scene.getEngine();
             this._fitRenderingDevice = !size;
             this._fitRenderingDevice = !size;
             if (!size) {
             if (!size) {
                 size = new Size(engine.getRenderWidth(), engine.getRenderHeight());
                 size = new Size(engine.getRenderWidth(), engine.getRenderHeight());
+            } else {
+                size.height *= renderScaleFactor;
+                size.width *= renderScaleFactor;
             }
             }
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", k => new Canvas2DEngineBoundData());
             this.__engineData = engine.getOrAddExternalDataWithFactory("__BJSCANVAS2D__", k => new Canvas2DEngineBoundData());
+            this._renderScaleFactor = renderScaleFactor;
             this._cachingStrategy = cachingstrategy;
             this._cachingStrategy = cachingstrategy;
             this._primPointerInfo = new PrimitivePointerInfo();
             this._primPointerInfo = new PrimitivePointerInfo();
             this._capturedPointers = new StringDictionary<Prim2DBase>();
             this._capturedPointers = new StringDictionary<Prim2DBase>();
@@ -203,20 +213,72 @@
             // Set the new state
             // Set the new state
             this._interactionEnabled = enable;
             this._interactionEnabled = enable;
 
 
-            // Disable interaction
-            if (!enable) {
-                if (this._scenePrePointerObserver) {
-                    this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
-                    this._scenePrePointerObserver = null;
+            // ScreenSpace mode
+            if (this._isScreeSpace) {
+                // Disable interaction
+                if (!enable) {
+                    if (this._scenePrePointerObserver) {
+                        this.scene.onPrePointerObservable.remove(this._scenePrePointerObserver);
+                        this._scenePrePointerObserver = null;
+                    }
+
+                    return;
                 }
                 }
 
 
-                return;
+                // Enable Interaction
+
+                // Register the observable
+                this._scenePrePointerObserver = this.scene.onPrePointerObservable.add((e, s) => {
+                    let hs = 1/this.engine.getHardwareScalingLevel();
+                    let localPos = e.localPosition.multiplyByFloats(hs, hs);
+                    this._handlePointerEventForInteraction(e, localPos, s);
+                });
+            }
+
+            // World Space Mode
+            else {
+                let scene = this.scene;
+                if (enable) {
+                    scene.constantlyUpdateMeshUnderPointer = true;
+                    this._scenePointerObserver = scene.onPointerObservable.add((e, s) => {
+
+                        if (e.pickInfo.hit && e.pickInfo.pickedMesh === this._worldSpaceNode && this.worldSpaceToNodeLocal) {
+                            let localPos = this.worldSpaceToNodeLocal(e.pickInfo.pickedPoint);
+                            this._handlePointerEventForInteraction(e, localPos, s);
+                        }
+                    });
+                }
+
+                // Disable
+                else {
+                    if (this._scenePointerObserver) {
+                        this.scene.onPointerObservable.remove(this._scenePointerObserver);
+                        this._scenePointerObserver = null;
+                    }
+                }
             }
             }
+        }
 
 
-            // Enable Interaction
+        /**
+         * If you set your own WorldSpaceNode to display the Canvas2D you have to provide your own implementation of this method which computes the local position in the Canvas based on the given 3D World one.
+         * Beware that you have to take under consideration the origin and the renderScaleFactor in your calculations! Good luck!
+         */
+        public worldSpaceToNodeLocal = (worldPos: Vector3): Vector2 => {
+            let node = this._worldSpaceNode;
+            if (!node) {
+                return;
+            }
 
 
-            // Register the observable
-            this.scene.onPrePointerObservable.add((e, s) => this._handlePointerEventForInteraction(e, s));
+            let mtx = node.getWorldMatrix().clone();
+            mtx.invert();
+            let v = Vector3.TransformCoordinates(worldPos, mtx);
+            let rsf = this._renderScaleFactor;
+            let res = new Vector2(v.x*rsf, v.y*rsf);
+            let size = this.actualSize;
+            let o = this.origin;
+            res.x += size.width * o.x;
+            res.y += size.width * o.y;
+            return res;
         }
         }
 
 
         /**
         /**
@@ -280,14 +342,14 @@
         }
         }
            
            
         private static _interInfo = new IntersectInfo2D();
         private static _interInfo = new IntersectInfo2D();
-        private _handlePointerEventForInteraction(eventData: PointerInfoPre, eventState: EventState) {
+        private _handlePointerEventForInteraction(eventData: PointerInfoBase, localPosition: Vector2, eventState: EventState) {
             // Dispose check
             // Dispose check
             if (this.isDisposed) {
             if (this.isDisposed) {
                 return;
                 return;
             }
             }
 
 
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
-            this._updatePointerInfo(eventData);
+            this._updatePointerInfo(eventData, localPosition);
 
 
             let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
             let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
 
 
@@ -323,7 +385,7 @@
             }
             }
         }
         }
 
 
-        private _updatePointerInfo(eventData: PointerInfoPre) {
+        private _updatePointerInfo(eventData: PointerInfoBase, localPosition: Vector2) {
             let pii = this._primPointerInfo;
             let pii = this._primPointerInfo;
             if (!pii.canvasPointerPos) {
             if (!pii.canvasPointerPos) {
                 pii.canvasPointerPos = Vector2.Zero();
                 pii.canvasPointerPos = Vector2.Zero();
@@ -331,15 +393,20 @@
             var camera = this._scene.activeCamera;
             var camera = this._scene.activeCamera;
             var engine = this._scene.getEngine();
             var engine = this._scene.getEngine();
 
 
-            var cameraViewport = camera.viewport;
-            var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+            if (this._isScreeSpace) {
+                var cameraViewport = camera.viewport;
+                var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
 
 
-            // Moving coordinates to local viewport world
-            var x = eventData.localPosition.x / engine.getHardwareScalingLevel() - viewport.x;
-            var y = eventData.localPosition.y / engine.getHardwareScalingLevel() - viewport.y;
+                // Moving coordinates to local viewport world
+                var x = localPosition.x - viewport.x;
+                var y = localPosition.y - viewport.y;
 
 
-            pii.canvasPointerPos.x = x - this.position.x;
-            pii.canvasPointerPos.y = engine.getRenderHeight() -y - this.position.y;
+                pii.canvasPointerPos.x = x - this.position.x;
+                pii.canvasPointerPos.y = engine.getRenderHeight() -y - this.position.y;
+            } else {
+                pii.canvasPointerPos.x = localPosition.x;
+                pii.canvasPointerPos.y = localPosition.y;
+            }
             pii.mouseWheelDelta = 0;
             pii.mouseWheelDelta = 0;
 
 
             if (eventData.type === PointerEventTypes.POINTERWHEEL) {
             if (eventData.type === PointerEventTypes.POINTERWHEEL) {
@@ -386,8 +453,6 @@
                 return;
                 return;
             }
             }
 
 
-            this._updateCanvasState();
-
             this.intersect(ii);
             this.intersect(ii);
 
 
             this._previousIntersectionList = this._actualIntersectionList;
             this._previousIntersectionList = this._actualIntersectionList;
@@ -677,9 +742,9 @@
         }
         }
 
 
         /**
         /**
-         * Only valid for World Space Canvas, returns the scene node that display the canvas
+         * Only valid for World Space Canvas, returns the scene node that displays the canvas
          */
          */
-        public get worldSpaceCanvasNode(): WorldSpaceCanvas2D {
+        public get worldSpaceCanvasNode(): Node {
             return this._worldSpaceNode;
             return this._worldSpaceNode;
         }
         }
 
 
@@ -788,6 +853,7 @@
         private _intersectionRenderId: number;
         private _intersectionRenderId: number;
         private _hoverStatusRenderId: number;
         private _hoverStatusRenderId: number;
         private _pickStartingPosition: Vector2;
         private _pickStartingPosition: Vector2;
+        private _renderScaleFactor: number;
         private _pickedDownPrim: Prim2DBase;
         private _pickedDownPrim: Prim2DBase;
         private _pickStartingTime: number;
         private _pickStartingTime: number;
         private _previousIntersectionList: Array<PrimitiveIntersectedInfo>;
         private _previousIntersectionList: Array<PrimitiveIntersectedInfo>;
@@ -796,7 +862,8 @@
         private _actualOverPrimitive: PrimitiveIntersectedInfo;
         private _actualOverPrimitive: PrimitiveIntersectedInfo;
         private _capturedPointers: StringDictionary<Prim2DBase>;
         private _capturedPointers: StringDictionary<Prim2DBase>;
         private _scenePrePointerObserver: Observer<PointerInfoPre>;
         private _scenePrePointerObserver: Observer<PointerInfoPre>;
-        private _worldSpaceNode: WorldSpaceCanvas2D;
+        private _scenePointerObserver: Observer<PointerInfo>;
+        private _worldSpaceNode: Node;
         private _mapCounter = 0;
         private _mapCounter = 0;
         private _background: Rectangle2D;
         private _background: Rectangle2D;
         private _scene: Scene;
         private _scene: Scene;
@@ -833,7 +900,7 @@
                 }
                 }
             }
             }
 
 
-            var context = new PreapreRender2DContext();
+            var context = new PrepareRender2DContext();
 
 
             ++this._globalTransformProcessStep;
             ++this._globalTransformProcessStep;
             this.updateGlobalTransVis(false);
             this.updateGlobalTransVis(false);
@@ -855,6 +922,7 @@
                 this._updateOverStatus();   // TODO this._primPointerInfo may not be up to date!
                 this._updateOverStatus();   // TODO this._primPointerInfo may not be up to date!
             }
             }
 
 
+            this.engine.setState(false);
             this._groupRender();
             this._groupRender();
 
 
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content
             // If the canvas is cached at canvas level, we must manually render the sprite that will display its content

+ 101 - 56
src/Canvas2d/babylon.ellipse2d.js

@@ -13,69 +13,87 @@ var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var Ellipse2DRenderCache = (function (_super) {
     var Ellipse2DRenderCache = (function (_super) {
         __extends(Ellipse2DRenderCache, _super);
         __extends(Ellipse2DRenderCache, _super);
-        function Ellipse2DRenderCache(engine, modelKey, isTransparent) {
-            _super.call(this, engine, modelKey, isTransparent);
+        function Ellipse2DRenderCache(engine, modelKey) {
+            _super.call(this, engine, modelKey);
+            this.effectsReady = false;
+            this.fillVB = null;
+            this.fillIB = null;
+            this.fillIndicesCount = 0;
+            this.instancingFillAttributes = null;
+            this.effectFillInstanced = null;
+            this.effectFill = null;
+            this.borderVB = null;
+            this.borderIB = null;
+            this.borderIndicesCount = 0;
+            this.instancingBorderAttributes = null;
+            this.effectBorderInstanced = null;
+            this.effectBorder = null;
         }
         }
         Ellipse2DRenderCache.prototype.render = function (instanceInfo, context) {
         Ellipse2DRenderCache.prototype.render = function (instanceInfo, context) {
             // Do nothing if the shader is still loading/preparing 
             // Do nothing if the shader is still loading/preparing 
-            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
-                return false;
+            if (!this.effectsReady) {
+                if ((this.effectFill && (!this.effectFill.isReady() || (this.effectFillInstanced && !this.effectFillInstanced.isReady()))) ||
+                    (this.effectBorder && (!this.effectBorder.isReady() || (this.effectBorderInstanced && !this.effectBorderInstanced.isReady())))) {
+                    return false;
+                }
+                this.effectsReady = true;
             }
             }
-            var engine = instanceInfo._owner.owner.engine;
+            var engine = instanceInfo.owner.owner.engine;
             var depthFunction = 0;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
                 depthFunction = engine.getDepthFunction();
                 engine.setDepthFunctionToLessOrEqual();
                 engine.setDepthFunctionToLessOrEqual();
             }
             }
-            var cur;
-            if (this.isTransparent) {
-                cur = engine.getAlphaMode();
-                engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
-            }
+            var curAlphaMode = engine.getAlphaMode();
             if (this.effectFill) {
             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) {
+                var partIndex = instanceInfo.partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
+                var pid = context.groupInfoPartData[partIndex];
+                if (context.renderMode !== BABYLON.Render2DContext.RenderModeOpaque) {
+                    engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+                }
+                var effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
+                engine.enableEffect(effect);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, effect);
+                if (context.useInstancing) {
                     if (!this.instancingFillAttributes) {
                     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);
+                        this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
                     }
-                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
-                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 }
                 }
                 else {
                 else {
-                    for (var i = 0; i < count; i++) {
-                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                    for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
+                        this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
                         engine.draw(true, 0, this.fillIndicesCount);
                     }
                     }
                 }
                 }
             }
             }
             if (this.effectBorder) {
             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) {
+                var partIndex = instanceInfo.partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
+                var pid = context.groupInfoPartData[partIndex];
+                if (context.renderMode !== BABYLON.Render2DContext.RenderModeOpaque) {
+                    engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+                }
+                var effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
+                engine.enableEffect(effect);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, effect);
+                if (context.useInstancing) {
                     if (!this.instancingBorderAttributes) {
                     if (!this.instancingBorderAttributes) {
-                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
                     }
-                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
-                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 }
                 }
                 else {
                 else {
-                    for (var i = 0; i < count; i++) {
-                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                    for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
+                        this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
                         engine.draw(true, 0, this.borderIndicesCount);
                     }
                     }
                 }
                 }
             }
             }
-            if (this.isTransparent) {
-                engine.setAlphaMode(cur);
-            }
+            engine.setAlphaMode(curAlphaMode);
             if (this.effectFill && this.effectBorder) {
             if (this.effectFill && this.effectBorder) {
                 engine.setDepthFunction(depthFunction);
                 engine.setDepthFunction(depthFunction);
             }
             }
@@ -97,6 +115,10 @@ var BABYLON;
                 this._engine._releaseEffect(this.effectFill);
                 this._engine._releaseEffect(this.effectFill);
                 this.effectFill = null;
                 this.effectFill = null;
             }
             }
+            if (this.effectFillInstanced) {
+                this._engine._releaseEffect(this.effectFillInstanced);
+                this.effectFillInstanced = null;
+            }
             if (this.borderVB) {
             if (this.borderVB) {
                 this._engine._releaseBuffer(this.borderVB);
                 this._engine._releaseBuffer(this.borderVB);
                 this.borderVB = null;
                 this.borderVB = null;
@@ -109,10 +131,14 @@ var BABYLON;
                 this._engine._releaseEffect(this.effectBorder);
                 this._engine._releaseEffect(this.effectBorder);
                 this.effectBorder = null;
                 this.effectBorder = null;
             }
             }
+            if (this.effectBorderInstanced) {
+                this._engine._releaseEffect(this.effectBorderInstanced);
+                this.effectBorderInstanced = null;
+            }
             return true;
             return true;
         };
         };
         return Ellipse2DRenderCache;
         return Ellipse2DRenderCache;
-    }(BABYLON.ModelRenderCache));
+    })(BABYLON.ModelRenderCache);
     BABYLON.Ellipse2DRenderCache = Ellipse2DRenderCache;
     BABYLON.Ellipse2DRenderCache = Ellipse2DRenderCache;
     var Ellipse2DInstanceData = (function (_super) {
     var Ellipse2DInstanceData = (function (_super) {
         __extends(Ellipse2DInstanceData, _super);
         __extends(Ellipse2DInstanceData, _super);
@@ -130,7 +156,7 @@ var BABYLON;
             BABYLON.instanceData()
             BABYLON.instanceData()
         ], Ellipse2DInstanceData.prototype, "properties", null);
         ], Ellipse2DInstanceData.prototype, "properties", null);
         return Ellipse2DInstanceData;
         return Ellipse2DInstanceData;
-    }(BABYLON.Shape2DInstanceData));
+    })(BABYLON.Shape2DInstanceData);
     BABYLON.Ellipse2DInstanceData = Ellipse2DInstanceData;
     BABYLON.Ellipse2DInstanceData = Ellipse2DInstanceData;
     var Ellipse2D = (function (_super) {
     var Ellipse2D = (function (_super) {
         __extends(Ellipse2D, _super);
         __extends(Ellipse2D, _super);
@@ -174,10 +200,8 @@ var BABYLON;
         Ellipse2D.prototype.updateLevelBoundingInfo = function () {
         Ellipse2D.prototype.updateLevelBoundingInfo = function () {
             BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
             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);
+        Ellipse2D.prototype.setupEllipse2D = function (owner, parent, id, position, origin, size, subdivisions, fill, border, borderThickness, isVisible, marginTop, marginLeft, marginRight, marginBottom, vAlignment, hAlignment) {
+            this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.size = size;
             this.size = size;
             this.subdivisions = subdivisions;
             this.subdivisions = subdivisions;
         };
         };
@@ -186,31 +210,40 @@ var BABYLON;
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * options:
          * options:
          *  - id: a text identifier, for information purpose
          *  - 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
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
          *  - 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
+         *  - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
          *  - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
          *  - 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.
          *  - 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.
          *  - 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.
          *  - borderThickness: the thickness of the drawn border, default is 1.
+         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
          */
         Ellipse2D.Create = function (parent, options) {
         Ellipse2D.Create = function (parent, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             BABYLON.Prim2DBase.CheckParent(parent);
-            var fill;
-            if (options && options.fill !== undefined) {
-                fill = options.fill;
+            var ellipse = new Ellipse2D();
+            if (!options) {
+                ellipse.setupEllipse2D(parent.owner, parent, null, BABYLON.Vector2.Zero(), null, new BABYLON.Size(10, 10), 64, BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, true, null, null, null, null, null, null);
             }
             }
             else {
             else {
-                fill = BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                var fill;
+                if (options.fill === undefined) {
+                    fill = BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                }
+                else {
+                    fill = options.fill;
+                }
+                var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
+                var size = options.size || (new BABYLON.Size(options.width || 10, options.height || 10));
+                ellipse.setupEllipse2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, (options.subdivisions == null) ? 64 : options.subdivisions, fill, options.border || null, (options.borderThickness == null) ? 1 : options.borderThickness, (options.isVisible == null) ? true : options.isVisible, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
             }
             }
-            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;
             return ellipse;
         };
         };
-        Ellipse2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
-            var renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey, isTransparent);
+        Ellipse2D.prototype.createModelRenderCache = function (modelKey) {
+            var renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey);
             return renderCache;
             return renderCache;
         };
         };
         Ellipse2D.prototype.setupModelRenderCache = function (modelRenderCache) {
         Ellipse2D.prototype.setupModelRenderCache = function (modelRenderCache) {
@@ -234,7 +267,13 @@ var BABYLON;
                 ib[triCount * 3 - 2] = 1;
                 ib[triCount * 3 - 2] = 1;
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIndicesCount = triCount * 3;
                 renderCache.fillIndicesCount = triCount * 3;
-                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"]);
+                // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], true);
+                if (ei) {
+                    renderCache.effectFillInstanced = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+                }
+                // Get the non instanced version
+                ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], false);
                 renderCache.effectFill = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
                 renderCache.effectFill = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
             }
             }
             // Need to create WebGL resource for border part?
             // Need to create WebGL resource for border part?
@@ -260,8 +299,14 @@ var BABYLON;
                 }
                 }
                 renderCache.borderIB = engine.createIndexBuffer(ib);
                 renderCache.borderIB = engine.createIndexBuffer(ib);
                 renderCache.borderIndicesCount = (triCount * 3);
                 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);
+                // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], true);
+                if (ei) {
+                    renderCache.effectBorderInstanced = engine.createEffect("ellipse2d", ei.attributes, ei.uniforms, [], ei.defines, null);
+                }
+                // Get the non instanced version
+                ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], false);
+                renderCache.effectBorder = engine.createEffect("ellipse2d", ei.attributes, ei.uniforms, [], ei.defines, null);
             }
             }
             return renderCache;
             return renderCache;
         };
         };
@@ -301,6 +346,6 @@ var BABYLON;
             BABYLON.className("Ellipse2D")
             BABYLON.className("Ellipse2D")
         ], Ellipse2D);
         ], Ellipse2D);
         return Ellipse2D;
         return Ellipse2D;
-    }(BABYLON.Shape2D));
+    })(BABYLON.Shape2D);
     BABYLON.Ellipse2D = Ellipse2D;
     BABYLON.Ellipse2D = Ellipse2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 20 - 2
src/Canvas2d/babylon.ellipse2d.ts

@@ -89,7 +89,7 @@
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 } else {
                 } else {
                     for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                     for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
-                        this.setupUniforms(this.effectBorder, partIndex, pid._partData, i);
+                        this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
                         engine.draw(true, 0, this.borderIndicesCount);
                     }
                     }
                 }
                 }
@@ -243,7 +243,25 @@
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let size = options.size || (new Size(options.width || 10, options.height || 10));
                 let size = options.size || (new Size(options.width || 10, options.height || 10));
 
 
-                ellipse.setupEllipse2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.subdivisions || 64, fill, options.border || null, options.borderThickness || 1, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
+                ellipse.setupEllipse2D
+                (
+                    parent.owner,
+                    parent,
+                    options.id || null,
+                    pos,
+                    options.origin || null,
+                    size,
+                    (options.subdivisions == null) ? 64 : options.subdivisions,
+                    fill,
+                    options.border || null,
+                    (options.borderThickness == null) ? 1 : options.borderThickness,
+                    (options.isVisible == null) ? true : options.isVisible,
+                    options.marginTop || null,
+                    options.marginLeft || null,
+                    options.marginRight || null,
+                    options.marginBottom || null,
+                    options.vAlignment || null,
+                    options.hAlignment || null);
             }
             }
 
 
             return ellipse;
             return ellipse;

+ 363 - 98
src/Canvas2d/babylon.group2d.js

@@ -18,45 +18,81 @@ var BABYLON;
          */
          */
         function Group2D() {
         function Group2D() {
             _super.call(this);
             _super.call(this);
-            this._primDirtyList = new Array();
-            this._childrenRenderableGroups = new Array();
-            this._renderGroupInstancesInfo = new BABYLON.StringDictionary();
         }
         }
         /**
         /**
          * Create an Logical or Renderable Group.
          * Create an Logical or Renderable Group.
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * options:
          * options:
          *  - id a text identifier, for information purpose
          *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent, default is [0;0]
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
          *  - 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.
+         *  - size: the size of the group. Alternatively the width and height properties can be set. 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
          *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
+         *  - isVisible: true if the group must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
          */
         Group2D.CreateGroup2D = function (parent, options) {
         Group2D.CreateGroup2D = function (parent, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             BABYLON.Prim2DBase.CheckParent(parent);
             var g = new Group2D();
             var g = new Group2D();
-            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);
+            if (!options) {
+                g.setupGroup2D(parent.owner, parent, null, BABYLON.Vector2.Zero(), null, null, true, Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, null, null, null, null, null, null);
+            }
+            else {
+                var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
+                var size = (!options.size && !options.width && !options.height) ? null : (options.size || (new BABYLON.Size(options.width || 0, options.height || 0)));
+                g.setupGroup2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, (options.isVisible == null) ? true : options.isVisible, (options.cacheBehavior == null) ? Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY : options.cacheBehavior, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || BABYLON.Prim2DBase.HAlignLeft, options.vAlignment || BABYLON.Prim2DBase.VAlignTop);
+            }
             return g;
             return g;
         };
         };
         Group2D._createCachedCanvasGroup = function (owner) {
         Group2D._createCachedCanvasGroup = function (owner) {
             var g = new Group2D();
             var g = new Group2D();
-            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero(), null);
+            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero(), BABYLON.Vector2.Zero(), null, true, Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, null, null, null, null, null, null);
             g.origin = BABYLON.Vector2.Zero();
             g.origin = BABYLON.Vector2.Zero();
             return g;
             return g;
         };
         };
         Group2D.prototype.applyCachedTexture = function (vertexData, material) {
         Group2D.prototype.applyCachedTexture = function (vertexData, material) {
             this._bindCacheTarget();
             this._bindCacheTarget();
             var uv = vertexData.uvs;
             var uv = vertexData.uvs;
-            var nodeuv = this._cacheNode.UVs;
+            var nodeuv = this._renderableData._cacheNode.UVs;
             for (var i = 0; i < 4; i++) {
             for (var i = 0; i < 4; i++) {
                 uv[i * 2 + 0] = nodeuv[i].x;
                 uv[i * 2 + 0] = nodeuv[i].x;
                 uv[i * 2 + 1] = nodeuv[i].y;
                 uv[i * 2 + 1] = nodeuv[i].y;
             }
             }
-            material.diffuseTexture = this._cacheTexture;
+            material.diffuseTexture = this._renderableData._cacheTexture;
             material.emissiveColor = new BABYLON.Color3(1, 1, 1);
             material.emissiveColor = new BABYLON.Color3(1, 1, 1);
-            this._cacheTexture.hasAlpha = true;
+            this._renderableData._cacheTexture.hasAlpha = true;
             this._unbindCacheTarget();
             this._unbindCacheTarget();
         };
         };
+        Object.defineProperty(Group2D.prototype, "cachedRect", {
+            /**
+             * Allow you to access the information regarding the cached rectangle of the Group2D into the MapTexture.
+             * If the `noWorldSpaceNode` options was used at the creation of a WorldSpaceCanvas, the rendering of the canvas must be made by the caller, so typically you want to bind the cacheTexture property to some material/mesh and you must use the cachedRect.UVs property to get the UV coordinates to use for your quad that will display the Canvas.
+             */
+            get: function () {
+                if (!this._renderableData) {
+                    return null;
+                }
+                return this._renderableData._cacheNode;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Group2D.prototype, "cacheTexture", {
+            /**
+             * Access the texture that maintains a cached version of the Group2D.
+             * This is useful only if you're not using a WorldSpaceNode for your WorldSpace Canvas and therefore need to perform the rendering yourself.
+             */
+            get: function () {
+                if (!this._renderableData) {
+                    return null;
+                }
+                return this._renderableData._cacheTexture;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
         /**
          * Call this method to remove this Group and its children from the Canvas
          * Call this method to remove this Group and its children from the Canvas
          */
          */
@@ -64,31 +100,15 @@ var BABYLON;
             if (!_super.prototype.dispose.call(this)) {
             if (!_super.prototype.dispose.call(this)) {
                 return false;
                 return false;
             }
             }
-            if (this._cacheRenderSprite) {
-                this._cacheRenderSprite.dispose();
-                this._cacheRenderSprite = null;
-            }
-            if (this._cacheTexture && this._cacheNode) {
-                this._cacheTexture.freeRect(this._cacheNode);
-                this._cacheTexture = null;
-                this._cacheNode = null;
-            }
-            if (this._primDirtyList) {
-                this._primDirtyList.splice(0);
-                this._primDirtyList = null;
-            }
-            if (this._renderGroupInstancesInfo) {
-                this._renderGroupInstancesInfo.forEach(function (k, v) {
-                    v.dispose();
-                });
-                this._renderGroupInstancesInfo = null;
+            if (this._renderableData) {
+                this._renderableData.dispose();
+                this._renderableData = null;
             }
             }
             return true;
             return true;
         };
         };
-        Group2D.prototype.setupGroup2D = function (owner, parent, id, position, origin, size, cacheBehavior) {
-            if (cacheBehavior === void 0) { cacheBehavior = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY; }
+        Group2D.prototype.setupGroup2D = function (owner, parent, id, position, origin, size, isVisible, cacheBehavior, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign) {
             this._cacheBehavior = cacheBehavior;
             this._cacheBehavior = cacheBehavior;
-            this.setupPrim2DBase(owner, parent, id, position, origin);
+            this.setupPrim2DBase(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
             this.size = size;
             this.size = size;
             this._viewportPosition = BABYLON.Vector2.Zero();
             this._viewportPosition = BABYLON.Vector2.Zero();
         };
         };
@@ -168,12 +188,13 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         Group2D.prototype._addPrimToDirtyList = function (prim) {
         Group2D.prototype._addPrimToDirtyList = function (prim) {
-            this._primDirtyList.push(prim);
+            this._renderableData._primDirtyList.push(prim);
         };
         };
-        Group2D.prototype._renderCachedCanvas = function (context) {
+        Group2D.prototype._renderCachedCanvas = function () {
             this.updateGlobalTransVis(true);
             this.updateGlobalTransVis(true);
+            var context = new BABYLON.PrepareRender2DContext();
             this._prepareGroupRender(context);
             this._prepareGroupRender(context);
-            this._groupRender(context);
+            this._groupRender();
         };
         };
         Group2D.prototype.levelIntersect = function (intersectInfo) {
         Group2D.prototype.levelIntersect = function (intersectInfo) {
             // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
             // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
@@ -194,8 +215,8 @@ var BABYLON;
         Group2D.prototype._prepareGroupRender = function (context) {
         Group2D.prototype._prepareGroupRender = function (context) {
             var sortedDirtyList = null;
             var sortedDirtyList = null;
             // Update the Global Transformation and visibility status of the changed primitives
             // Update the Global Transformation and visibility status of the changed primitives
-            if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
-                sortedDirtyList = this._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
+            if ((this._renderableData._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
+                sortedDirtyList = this._renderableData._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
                 this.updateGlobalTransVisOf(sortedDirtyList, true);
                 this.updateGlobalTransVisOf(sortedDirtyList, true);
             }
             }
             // Setup the size of the rendering viewport
             // Setup the size of the rendering viewport
@@ -232,7 +253,7 @@ var BABYLON;
                 }
                 }
                 this._viewportSize = newSize;
                 this._viewportSize = newSize;
             }
             }
-            if ((this._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
+            if ((this._renderableData._primDirtyList.length > 0) || context.forceRefreshPrimitive) {
                 // If the group is cached, set the dirty flag to true because of the incoming changes
                 // If the group is cached, set the dirty flag to true because of the incoming changes
                 this._cacheGroupDirty = this._isCachedGroup;
                 this._cacheGroupDirty = this._isCachedGroup;
                 // If it's a force refresh, prepare all the children
                 // If it's a force refresh, prepare all the children
@@ -246,7 +267,7 @@ var BABYLON;
                     // Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
                     // Each primitive that changed at least once was added into the primDirtyList, we have to sort this level using
                     //  the hierarchyDepth in order to prepare primitives from top to bottom
                     //  the hierarchyDepth in order to prepare primitives from top to bottom
                     if (!sortedDirtyList) {
                     if (!sortedDirtyList) {
-                        sortedDirtyList = this._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
+                        sortedDirtyList = this._renderableData._primDirtyList.sort(function (a, b) { return a.hierarchyDepth - b.hierarchyDepth; });
                     }
                     }
                     sortedDirtyList.forEach(function (p) {
                     sortedDirtyList.forEach(function (p) {
                         // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
                         // We need to check if prepare is needed because even if the primitive is in the dirtyList, its parent primitive may also have been modified, then prepared, then recurse on its children primitives (this one for instance) if the changes where impacting them.
@@ -256,23 +277,23 @@ var BABYLON;
                         }
                         }
                     });
                     });
                     // Everything is updated, clear the dirty list
                     // Everything is updated, clear the dirty list
-                    this._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
-                    this._primDirtyList.splice(0);
+                    this._renderableData._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
+                    this._renderableData._primDirtyList.splice(0);
                 }
                 }
             }
             }
             // A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
             // A renderable group has a list of direct children that are also renderable groups, we recurse on them to also prepare them
-            this._childrenRenderableGroups.forEach(function (g) {
+            this._renderableData._childrenRenderableGroups.forEach(function (g) {
                 g._prepareGroupRender(context);
                 g._prepareGroupRender(context);
             });
             });
         };
         };
-        Group2D.prototype._groupRender = function (context) {
+        Group2D.prototype._groupRender = function () {
             var _this = this;
             var _this = this;
             var engine = this.owner.engine;
             var engine = this.owner.engine;
             var failedCount = 0;
             var failedCount = 0;
             // First recurse to children render group to render them (in their cache or on screen)
             // First recurse to children render group to render them (in their cache or on screen)
-            for (var _i = 0, _a = this._childrenRenderableGroups; _i < _a.length; _i++) {
+            for (var _i = 0, _a = this._renderableData._childrenRenderableGroups; _i < _a.length; _i++) {
                 var childGroup = _a[_i];
                 var childGroup = _a[_i];
-                childGroup._groupRender(context);
+                childGroup._groupRender();
             }
             }
             // Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
             // Render the primitives if needed: either if we don't cache the content or if the content is cached but has changed
             if (!this.isCachedGroup || this._cacheGroupDirty) {
             if (!this.isCachedGroup || this._cacheGroupDirty) {
@@ -282,47 +303,65 @@ var BABYLON;
                 else {
                 else {
                     var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                     var curVP = engine.setDirectViewport(this._viewportPosition.x, this._viewportPosition.y, this._viewportSize.width, this._viewportSize.height);
                 }
                 }
+                // ===================================================================
+                // First pass, update the InstancedArray and render Opaque primitives
+                // Disable Alpha Testing, Enable Depth Write
+                engine.setAlphaTesting(false);
+                engine.setDepthWrite(true);
                 // For each different model of primitive to render
                 // For each different model of primitive to render
-                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
-                    if (_this.owner.supportInstancedArray) {
-                        for (var i = 0; i < v._instancesPartsData.length; i++) {
-                            // If the instances of the model was changed, pack the data
-                            var array = v._instancesPartsData[i];
-                            var instanceData_1 = array.pack();
-                            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
-                            if (!v._instancesPartsBuffer[i] || (v._instancesPartsBufferSize[i] < neededSize)) {
-                                if (v._instancesPartsBuffer[i]) {
-                                    engine.deleteInstancesBuffer(v._instancesPartsBuffer[i]);
-                                }
-                                v._instancesPartsBuffer[i] = engine.createInstancesBuffer(neededSize);
-                                v._instancesPartsBufferSize[i] = neededSize;
-                                v._dirtyInstancesData = false;
-                                // Update the WebGL buffer to match the new content of the instances data
-                                engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData_1);
-                            }
-                            else if (v._dirtyInstancesData) {
-                                // 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;
+                var context = new BABYLON.Render2DContext(BABYLON.Render2DContext.RenderModeOpaque);
+                this._renderableData._renderGroupInstancesInfo.forEach(function (k, v) {
+                    // Prepare the context object, update the WebGL Instanced Array buffer if needed
+                    var renderCount = _this._prepareContext(engine, context, v);
+                    // If null is returned, there's no opaque data to render
+                    if (renderCount === null) {
+                        return;
                     }
                     }
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
-                    if (!_this.owner.supportInstancedArray || totalRenderCount_1 > 0) {
+                    if (!_this.owner.supportInstancedArray || renderCount > 0) {
                         // render all the instances of this model, if the render method returns true then our instances are no longer dirty
                         // render all the instances of this model, if the render method returns true then our instances are no longer dirty
-                        var renderFailed = !v._modelCache.render(v, context);
+                        var renderFailed = !v.modelRenderCache.render(v, context);
                         // Update dirty flag/related
                         // Update dirty flag/related
-                        v._dirtyInstancesData = renderFailed;
+                        v.opaqueDirty = renderFailed;
                         failedCount += renderFailed ? 1 : 0;
                         failedCount += renderFailed ? 1 : 0;
                     }
                     }
                 });
                 });
+                // =======================================================================
+                // Second pass, update the InstancedArray and render AlphaTest primitives
+                // Enable Alpha Testing, Enable Depth Write
+                engine.setAlphaTesting(true);
+                engine.setDepthWrite(true);
+                // For each different model of primitive to render
+                context = new BABYLON.Render2DContext(BABYLON.Render2DContext.RenderModeAlphaTest);
+                this._renderableData._renderGroupInstancesInfo.forEach(function (k, v) {
+                    // Prepare the context object, update the WebGL Instanced Array buffer if needed
+                    var renderCount = _this._prepareContext(engine, context, v);
+                    // If null is returned, there's no opaque data to render
+                    if (renderCount === null) {
+                        return;
+                    }
+                    // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
+                    if (!_this.owner.supportInstancedArray || renderCount > 0) {
+                        // render all the instances of this model, if the render method returns true then our instances are no longer dirty
+                        var renderFailed = !v.modelRenderCache.render(v, context);
+                        // Update dirty flag/related
+                        v.opaqueDirty = renderFailed;
+                        failedCount += renderFailed ? 1 : 0;
+                    }
+                });
+                // =======================================================================
+                // Third pass, transparent primitive rendering
+                // Enable Alpha Testing, Disable Depth Write
+                engine.setAlphaTesting(true);
+                engine.setDepthWrite(false);
+                // First Check if the transparent List change so we can update the TransparentSegment and PartData (sort if needed)
+                if (this._renderableData._transparentListChanged) {
+                    this._updateTransparentData();
+                }
+                // From this point on we have up to date data to render, so let's go
+                failedCount += this._renderTransparentData();
+                // =======================================================================
+                //  Unbind target/restore viewport setting, clear dirty flag, and quit
                 // The group's content is no longer dirty
                 // The group's content is no longer dirty
                 this._cacheGroupDirty = failedCount !== 0;
                 this._cacheGroupDirty = failedCount !== 0;
                 if (this.isCachedGroup) {
                 if (this.isCachedGroup) {
@@ -335,58 +374,210 @@ var BABYLON;
                 }
                 }
             }
             }
         };
         };
+        Group2D.prototype._updateTransparentData = function () {
+            var rd = this._renderableData;
+            // If null, there was no change of ZOrder, we have nothing to do
+            if (rd._firstChangedPrim === null) {
+                return;
+            }
+            // Sort all the primitive from their depth, max (bottom) to min (top)
+            rd._transparentPrimitives.sort(function (a, b) { return b._primitive.getActualZOffset() - a._primitive.getActualZOffset(); });
+            var checkAndAddPrimInSegment = function (seg, tpiI) {
+                var tpi = rd._transparentPrimitives[tpiI];
+                // Fast rejection: if gii are different
+                if (seg.groupInsanceInfo !== tpi._groupInstanceInfo) {
+                    return false;
+                }
+                var tpiZ = tpi._primitive.getActualZOffset();
+                // We've made it so far, the tpi can be part of the segment, add it
+                tpi._transparentSegment = seg;
+                // Check if we have to update endZ, a smaller value means one above the current one
+                if (tpiZ < seg.endZ) {
+                    seg.endZ = tpiZ;
+                    seg.endDataIndex = tpi._primitive._getLastIndexInDataBuffer() + 1; // Still exclusive
+                }
+                return true;
+            };
+            rd._transparentSegments.splice(0);
+            var prevSeg = null;
+            for (var tpiI = 0; tpiI < rd._transparentPrimitives.length; tpiI++) {
+                var tpi = rd._transparentPrimitives[tpiI];
+                // Check if the Data in which the primitive is stored is not sorted properly
+                if (tpi._groupInstanceInfo.transparentOrderDirty) {
+                    tpi._groupInstanceInfo.sortTransparentData();
+                }
+                // Reset the segment, we have to create/rebuild it
+                tpi._transparentSegment = null;
+                // If there's a previous valid segment, check if this prim can be part of it
+                if (prevSeg) {
+                    checkAndAddPrimInSegment(prevSeg, tpiI);
+                }
+                // If we couldn't insert in the adjacent segments, he have to create one
+                if (!tpi._transparentSegment) {
+                    var ts = new BABYLON.TransparentSegment();
+                    ts.groupInsanceInfo = tpi._groupInstanceInfo;
+                    var prim = tpi._primitive;
+                    ts.startZ = prim.getActualZOffset();
+                    ts.startDataIndex = prim._getFirstIndexInDataBuffer();
+                    ts.endDataIndex = prim._getLastIndexInDataBuffer() + 1; // Make it exclusive, more natural to use in a for loop
+                    ts.endZ = ts.startZ;
+                    tpi._transparentSegment = ts;
+                    rd._transparentSegments.push(ts);
+                }
+                // Update prevSeg
+                prevSeg = tpi._transparentSegment;
+            }
+            rd._firstChangedPrim = null;
+            rd._transparentListChanged = false;
+        };
+        Group2D.prototype._renderTransparentData = function () {
+            var failedCount = 0;
+            var context = new BABYLON.Render2DContext(BABYLON.Render2DContext.RenderModeTransparent);
+            var rd = this._renderableData;
+            var length = rd._transparentSegments.length;
+            for (var i = 0; i < length; i++) {
+                var ts = rd._transparentSegments[i];
+                var gii = ts.groupInsanceInfo;
+                var mrc = gii.modelRenderCache;
+                context.useInstancing = false;
+                context.partDataStartIndex = ts.startDataIndex;
+                context.partDataEndIndex = ts.endDataIndex;
+                context.groupInfoPartData = gii.transparentData;
+                var renderFailed = !mrc.render(gii, context);
+                failedCount += renderFailed ? 1 : 0;
+            }
+            return failedCount;
+        };
+        Group2D.prototype._prepareContext = function (engine, context, gii) {
+            var gipd = null;
+            var setDirty;
+            var getDirty;
+            // Render Mode specifics
+            switch (context.renderMode) {
+                case BABYLON.Render2DContext.RenderModeOpaque:
+                    {
+                        if (!gii.hasOpaqueData) {
+                            return null;
+                        }
+                        setDirty = function (dirty) { gii.opaqueDirty = dirty; };
+                        getDirty = function () { return gii.opaqueDirty; };
+                        context.groupInfoPartData = gii.opaqueData;
+                        gipd = gii.opaqueData;
+                        break;
+                    }
+                case BABYLON.Render2DContext.RenderModeAlphaTest:
+                    {
+                        if (!gii.hasAlphaTestData) {
+                            return null;
+                        }
+                        setDirty = function (dirty) { gii.alphaTestDirty = dirty; };
+                        getDirty = function () { return gii.alphaTestDirty; };
+                        context.groupInfoPartData = gii.alphaTestData;
+                        gipd = gii.alphaTestData;
+                        break;
+                    }
+                default:
+                    throw new Error("_prepareContext is only for opaque or alphaTest");
+            }
+            var renderCount = 0;
+            // This part will pack the dynamicfloatarray and update the instanced array WebGLBufffer
+            // Skip it if instanced arrays are not supported
+            if (this.owner.supportInstancedArray) {
+                // Flag for instancing
+                context.useInstancing = true;
+                // Make sure all the WebGLBuffers of the Instanced Array are created/up to date for the parts to render.
+                for (var i = 0; i < gipd.length; i++) {
+                    var pid = gipd[i];
+                    // If the instances of the model was changed, pack the data
+                    var array = pid._partData;
+                    var instanceData_1 = array.pack();
+                    renderCount += 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
+                    if (!pid._partBuffer || (pid._partBufferSize < neededSize)) {
+                        if (pid._partBuffer) {
+                            engine.deleteInstancesBuffer(pid._partBuffer);
+                        }
+                        pid._partBuffer = engine.createInstancesBuffer(neededSize); // Create + bind
+                        pid._partBufferSize = neededSize;
+                        setDirty(false);
+                        // Update the WebGL buffer to match the new content of the instances data
+                        engine.updateArrayBuffer(instanceData_1);
+                    }
+                    else if (getDirty()) {
+                        // Update the WebGL buffer to match the new content of the instances data
+                        engine.bindArrayBuffer(pid._partBuffer);
+                        engine.updateArrayBuffer(instanceData_1);
+                    }
+                }
+                setDirty(false);
+            }
+            else {
+                context.partDataStartIndex = 0;
+                // Find the first valid object to get the count
+                var i = 0;
+                while (!context.groupInfoPartData[i]) {
+                    i++;
+                }
+                context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
+            }
+            return renderCount;
+        };
         Group2D.prototype._bindCacheTarget = function () {
         Group2D.prototype._bindCacheTarget = function () {
             var curWidth;
             var curWidth;
             var curHeight;
             var curHeight;
-            if (this._cacheNode) {
-                var size = this._cacheNode.contentSize;
+            var rd = this._renderableData;
+            if (rd._cacheNode) {
+                var size = rd._cacheNode.contentSize;
                 var groupWidth = Math.ceil(this.actualSize.width);
                 var groupWidth = Math.ceil(this.actualSize.width);
                 var groupHeight = Math.ceil(this.actualSize.height);
                 var groupHeight = Math.ceil(this.actualSize.height);
                 if ((size.width < groupWidth) || (size.height < groupHeight)) {
                 if ((size.width < groupWidth) || (size.height < groupHeight)) {
                     curWidth = Math.floor(size.width * 1.07); // Grow 5% more to avoid frequent resizing for few pixels...
                     curWidth = Math.floor(size.width * 1.07); // Grow 5% more to avoid frequent resizing for few pixels...
                     curHeight = Math.floor(size.height * 1.07);
                     curHeight = Math.floor(size.height * 1.07);
                     //console.log(`[${this._globalTransformProcessStep}] Resize group ${this.id}, width: ${curWidth}, height: ${curHeight}`);
                     //console.log(`[${this._globalTransformProcessStep}] Resize group ${this.id}, width: ${curWidth}, height: ${curHeight}`);
-                    this._cacheTexture.freeRect(this._cacheNode);
-                    this._cacheNode = null;
+                    rd._cacheTexture.freeRect(rd._cacheNode);
+                    rd._cacheNode = null;
                 }
                 }
             }
             }
-            if (!this._cacheNode) {
+            if (!rd._cacheNode) {
                 // Check if we have to allocate a rendering zone in the global cache texture
                 // Check if we have to allocate a rendering zone in the global cache texture
                 var res = this.owner._allocateGroupCache(this, this.renderGroup, curWidth ? new BABYLON.Size(curWidth, curHeight) : null);
                 var res = this.owner._allocateGroupCache(this, this.renderGroup, curWidth ? new BABYLON.Size(curWidth, curHeight) : null);
-                this._cacheNode = res.node;
-                this._cacheTexture = res.texture;
-                this._cacheRenderSprite = res.sprite;
-                var size = this._cacheNode.contentSize;
+                rd._cacheNode = res.node;
+                rd._cacheTexture = res.texture;
+                rd._cacheRenderSprite = res.sprite;
+                var size = rd._cacheNode.contentSize;
             }
             }
-            var n = this._cacheNode;
-            this._cacheTexture.bindTextureForPosSize(n.pos, this.actualSize, true);
+            var n = rd._cacheNode;
+            rd._cacheTexture.bindTextureForPosSize(n.pos, this.actualSize, true);
         };
         };
         Group2D.prototype._unbindCacheTarget = function () {
         Group2D.prototype._unbindCacheTarget = function () {
-            if (this._cacheTexture) {
-                this._cacheTexture.unbindTexture();
+            if (this._renderableData._cacheTexture) {
+                this._renderableData._cacheTexture.unbindTexture();
             }
             }
         };
         };
         Group2D.prototype.handleGroupChanged = function (prop) {
         Group2D.prototype.handleGroupChanged = function (prop) {
             // This method is only for cachedGroup
             // This method is only for cachedGroup
-            if (!this.isCachedGroup || !this._cacheRenderSprite) {
+            var rd = this._renderableData;
+            if (!this.isCachedGroup || !rd._cacheRenderSprite) {
                 return;
                 return;
             }
             }
             // For now we only support these property changes
             // For now we only support these property changes
             // TODO: add more! :)
             // TODO: add more! :)
             if (prop.id === BABYLON.Prim2DBase.positionProperty.id) {
             if (prop.id === BABYLON.Prim2DBase.positionProperty.id) {
-                this._cacheRenderSprite.position = this.position.clone();
+                rd._cacheRenderSprite.position = this.position.clone();
             }
             }
             else if (prop.id === BABYLON.Prim2DBase.rotationProperty.id) {
             else if (prop.id === BABYLON.Prim2DBase.rotationProperty.id) {
-                this._cacheRenderSprite.rotation = this.rotation;
+                rd._cacheRenderSprite.rotation = this.rotation;
             }
             }
             else if (prop.id === BABYLON.Prim2DBase.scaleProperty.id) {
             else if (prop.id === BABYLON.Prim2DBase.scaleProperty.id) {
-                this._cacheRenderSprite.scale = this.scale;
+                rd._cacheRenderSprite.scale = this.scale;
             }
             }
             else if (prop.id === BABYLON.Prim2DBase.originProperty.id) {
             else if (prop.id === BABYLON.Prim2DBase.originProperty.id) {
-                this._cacheRenderSprite.origin = this.origin.clone();
+                rd._cacheRenderSprite.origin = this.origin.clone();
             }
             }
             else if (prop.id === Group2D.actualSizeProperty.id) {
             else if (prop.id === Group2D.actualSizeProperty.id) {
-                this._cacheRenderSprite.spriteSize = this.actualSize.clone();
+                rd._cacheRenderSprite.spriteSize = this.actualSize.clone();
             }
             }
         };
         };
         Group2D.prototype.detectGroupStates = function () {
         Group2D.prototype.detectGroupStates = function () {
@@ -434,12 +625,15 @@ var BABYLON;
                     this._isCachedGroup = true;
                     this._isCachedGroup = true;
                 }
                 }
             }
             }
+            if (this._isRenderableGroup) {
+                this._renderableData = new RenderableGroupData();
+            }
             // If the group is tagged as renderable we add it to the renderable tree
             // If the group is tagged as renderable we add it to the renderable tree
             if (this._isCachedGroup) {
             if (this._isCachedGroup) {
                 var cur = this.parent;
                 var cur = this.parent;
                 while (cur) {
                 while (cur) {
                     if (cur instanceof Group2D && cur._isRenderableGroup) {
                     if (cur instanceof Group2D && cur._isRenderableGroup) {
-                        cur._childrenRenderableGroups.push(this);
+                        cur._renderableData._childrenRenderableGroups.push(this);
                         break;
                         break;
                     }
                     }
                     cur = cur.parent;
                     cur = cur.parent;
@@ -470,6 +664,77 @@ var BABYLON;
             BABYLON.className("Group2D")
             BABYLON.className("Group2D")
         ], Group2D);
         ], Group2D);
         return Group2D;
         return Group2D;
-    }(BABYLON.Prim2DBase));
+    })(BABYLON.Prim2DBase);
     BABYLON.Group2D = Group2D;
     BABYLON.Group2D = Group2D;
+    var RenderableGroupData = (function () {
+        function RenderableGroupData() {
+            this._primDirtyList = new Array();
+            this._childrenRenderableGroups = new Array();
+            this._renderGroupInstancesInfo = new BABYLON.StringDictionary();
+            this._transparentPrimitives = new Array();
+            this._transparentSegments = new Array();
+            this._firstChangedPrim = null;
+            this._transparentListChanged = false;
+        }
+        RenderableGroupData.prototype.dispose = function () {
+            if (this._cacheRenderSprite) {
+                this._cacheRenderSprite.dispose();
+                this._cacheRenderSprite = null;
+            }
+            if (this._cacheTexture && this._cacheNode) {
+                this._cacheTexture.freeRect(this._cacheNode);
+                this._cacheTexture = null;
+                this._cacheNode = null;
+            }
+            if (this._primDirtyList) {
+                this._primDirtyList.splice(0);
+                this._primDirtyList = null;
+            }
+            if (this._renderGroupInstancesInfo) {
+                this._renderGroupInstancesInfo.forEach(function (k, v) {
+                    v.dispose();
+                });
+                this._renderGroupInstancesInfo = null;
+            }
+        };
+        RenderableGroupData.prototype.addNewTransparentPrimitiveInfo = function (prim, gii) {
+            var tpi = new TransparentPrimitiveInfo();
+            tpi._primitive = prim;
+            tpi._groupInstanceInfo = gii;
+            tpi._transparentSegment = null;
+            this._transparentPrimitives.push(tpi);
+            this._transparentListChanged = true;
+            this.updateSmallestZChangedPrim(tpi);
+            return tpi;
+        };
+        RenderableGroupData.prototype.removeTransparentPrimitiveInfo = function (tpi) {
+            var index = this._transparentPrimitives.indexOf(tpi);
+            if (index !== -1) {
+                this._transparentPrimitives.splice(index, 1);
+                this._transparentListChanged = true;
+                this.updateSmallestZChangedPrim(tpi);
+            }
+        };
+        RenderableGroupData.prototype.transparentPrimitiveZChanged = function (tpi) {
+            this._transparentListChanged = true;
+            this.updateSmallestZChangedPrim(tpi);
+        };
+        RenderableGroupData.prototype.updateSmallestZChangedPrim = function (tpi) {
+            if (tpi._primitive) {
+                var newZ = tpi._primitive.getActualZOffset();
+                var curZ = this._firstChangedPrim ? this._firstChangedPrim._primitive.getActualZOffset() : Number.MIN_VALUE;
+                if (newZ > curZ) {
+                    this._firstChangedPrim = tpi;
+                }
+            }
+        };
+        return RenderableGroupData;
+    })();
+    BABYLON.RenderableGroupData = RenderableGroupData;
+    var TransparentPrimitiveInfo = (function () {
+        function TransparentPrimitiveInfo() {
+        }
+        return TransparentPrimitiveInfo;
+    })();
+    BABYLON.TransparentPrimitiveInfo = TransparentPrimitiveInfo;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 18 - 3
src/Canvas2d/babylon.group2d.ts

@@ -55,7 +55,22 @@
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let size = (!options.size && !options.width && !options.height) ? null : (options.size || (new Size(options.width || 0, options.height || 0)));
                 let size = (!options.size && !options.width && !options.height) ? null : (options.size || (new Size(options.width || 0, options.height || 0)));
                 
                 
-                g.setupGroup2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.isVisible || true, options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || Prim2DBase.HAlignLeft, options.vAlignment || Prim2DBase.VAlignTop);
+                g.setupGroup2D
+                (
+                    parent.owner,
+                    parent,
+                    options.id || null,
+                    pos,
+                    options.origin || null,
+                    size,
+                    (options.isVisible == null) ? true : options.isVisible,
+                    (options.cacheBehavior == null) ? Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY : options.cacheBehavior,
+                    options.marginTop,
+                    options.marginLeft,
+                    options.marginRight,
+                    options.marginBottom,
+                    options.hAlignment || Prim2DBase.HAlignLeft,
+                    options.vAlignment || Prim2DBase.VAlignTop);
             }
             }
        
        
             return g;
             return g;
@@ -205,7 +220,7 @@
 
 
         public _renderCachedCanvas() {
         public _renderCachedCanvas() {
             this.updateGlobalTransVis(true);
             this.updateGlobalTransVis(true);
-            let context = new PreapreRender2DContext();
+            let context = new PrepareRender2DContext();
             this._prepareGroupRender(context);
             this._prepareGroupRender(context);
             this._groupRender();
             this._groupRender();
         }
         }
@@ -231,7 +246,7 @@
         }
         }
 
 
         // Method called only on renderable groups to prepare the rendering
         // Method called only on renderable groups to prepare the rendering
-        protected _prepareGroupRender(context: PreapreRender2DContext) {
+        protected _prepareGroupRender(context: PrepareRender2DContext) {
             let sortedDirtyList: Prim2DBase[] = null;
             let sortedDirtyList: Prim2DBase[] = null;
 
 
             // Update the Global Transformation and visibility status of the changed primitives
             // Update the Global Transformation and visibility status of the changed primitives

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 218 - 121
src/Canvas2d/babylon.lines2d.js


+ 330 - 247
src/Canvas2d/babylon.lines2d.ts

@@ -1,19 +1,19 @@
 module BABYLON {
 module BABYLON {
     export class Lines2DRenderCache extends ModelRenderCache {
     export class Lines2DRenderCache extends ModelRenderCache {
-        effectsReady: boolean                               = false;
-        fillVB: WebGLBuffer                                 = null;
-        fillIB: WebGLBuffer                                 = null;
-        fillIndicesCount: number                            = 0;
+        effectsReady: boolean = false;
+        fillVB: WebGLBuffer = null;
+        fillIB: WebGLBuffer = null;
+        fillIndicesCount: number = 0;
         instancingFillAttributes: InstancingAttributeInfo[] = null;
         instancingFillAttributes: InstancingAttributeInfo[] = null;
-        effectFill: Effect                                  = null;
-        effectFillInstanced: Effect                         = null;
+        effectFill: Effect = null;
+        effectFillInstanced: Effect = null;
 
 
-        borderVB: WebGLBuffer                                 = null;
-        borderIB: WebGLBuffer                                 = null;
-        borderIndicesCount: number                            = 0;
+        borderVB: WebGLBuffer = null;
+        borderIB: WebGLBuffer = null;
+        borderIndicesCount: number = 0;
         instancingBorderAttributes: InstancingAttributeInfo[] = null;
         instancingBorderAttributes: InstancingAttributeInfo[] = null;
-        effectBorder: Effect                                  = null;
-        effectBorderInstanced: Effect                         = null;
+        effectBorder: Effect = null;
+        effectBorderInstanced: Effect = null;
 
 
         constructor(engine: Engine, modelKey: string) {
         constructor(engine: Engine, modelKey: string) {
             super(engine, modelKey);
             super(engine, modelKey);
@@ -42,7 +42,7 @@
             if (this.effectFill) {
             if (this.effectFill) {
                 let partIndex = instanceInfo.partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
                 let partIndex = instanceInfo.partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
                 let pid = context.groupInfoPartData[partIndex];
                 let pid = context.groupInfoPartData[partIndex];
-             
+
                 if (context.renderMode !== Render2DContext.RenderModeOpaque) {
                 if (context.renderMode !== Render2DContext.RenderModeOpaque) {
                     engine.setAlphaMode(Engine.ALPHA_COMBINE);
                     engine.setAlphaMode(Engine.ALPHA_COMBINE);
                 }
                 }
@@ -50,7 +50,7 @@
                 let effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
                 let effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
 
 
                 engine.enableEffect(effect);
                 engine.enableEffect(effect);
-                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [2], 2*4, effect);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [2], 2 * 4, effect);
                 if (context.useInstancing) {
                 if (context.useInstancing) {
                     if (!this.instancingFillAttributes) {
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
@@ -62,7 +62,7 @@
                 } else {
                 } else {
                     for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                     for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
                         this.setupUniforms(effect, partIndex, pid._partData, i);
                         this.setupUniforms(effect, partIndex, pid._partData, i);
-                        engine.draw(true, 0, this.fillIndicesCount);                        
+                        engine.draw(true, 0, this.fillIndicesCount);
                     }
                     }
                 }
                 }
             }
             }
@@ -170,13 +170,13 @@
 
 
     @className("Lines2D")
     @className("Lines2D")
     export class Lines2D extends Shape2D {
     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;         }
+        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 pointsProperty: Prim2DPropInfo;
         public static fillThicknessProperty: Prim2DPropInfo;
         public static fillThicknessProperty: Prim2DPropInfo;
@@ -234,29 +234,67 @@
             this._endCap = value;
             this._endCap = value;
         }
         }
 
 
+        private static _prevA: Vector2 = Vector2.Zero();
+        private static _prevB: Vector2 = Vector2.Zero();
+        private static _curA: Vector2 = Vector2.Zero();
+        private static _curB: Vector2 = Vector2.Zero();
+
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
             let pl = this.points.length;
             let pl = this.points.length;
             let l = this.closed ? pl + 1 : pl;
             let l = this.closed ? pl + 1 : pl;
 
 
-            let originOffset = new Vector2(-0.5, -0.5);
             let p = intersectInfo._localPickPosition;
             let p = intersectInfo._localPickPosition;
 
 
-            let prevA = this.transformPointWithOrigin(this._contour[0], originOffset);
-            let prevB = this.transformPointWithOrigin(this._contour[1], originOffset);
+            this.transformPointWithOriginToRef(this._contour[0], null, Lines2D._prevA);
+            this.transformPointWithOriginToRef(this._contour[1], null, Lines2D._prevB);
             for (let i = 1; i < l; i++) {
             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);
+                this.transformPointWithOriginToRef(this._contour[(i % pl) * 2 + 0], null, Lines2D._curA);
+                this.transformPointWithOriginToRef(this._contour[(i % pl) * 2 + 1], null, Lines2D._curB);
 
 
-                if (Vector2.PointInTriangle(p, prevA, prevB, curA)) {
+                if (Vector2.PointInTriangle(p, Lines2D._prevA, Lines2D._prevB, Lines2D._curA)) {
                     return true;
                     return true;
                 }
                 }
-                if (Vector2.PointInTriangle(p, curA, prevB, curB)) {
+                if (Vector2.PointInTriangle(p, Lines2D._curA, Lines2D._prevB, Lines2D._curB)) {
                     return true;
                     return true;
                 }
                 }
 
 
-                prevA = curA;
-                prevB = curB;
+                Lines2D._prevA.x = Lines2D._curA.x;
+                Lines2D._prevA.y = Lines2D._curA.y;
+                Lines2D._prevB.x = Lines2D._curB.x;
+                Lines2D._prevB.y = Lines2D._curB.y;
+            }
+
+            let capIntersect = (tri: number[], points: number[]): boolean => {
+                let l = tri.length;
+                for (let i = 0; i < l; i += 3) {
+                    Lines2D._curA.x = points[tri[i + 0] * 2 + 0];
+                    Lines2D._curA.y = points[tri[i + 0] * 2 + 1];
+                    this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._curB);
+
+                    Lines2D._curA.x = points[tri[i + 1] * 2 + 0];
+                    Lines2D._curA.y = points[tri[i + 1] * 2 + 1];
+                    this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._prevA);
+
+                    Lines2D._curA.x = points[tri[i + 2] * 2 + 0];
+                    Lines2D._curA.y = points[tri[i + 2] * 2 + 1];
+                    this.transformPointWithOriginToRef(Lines2D._curA, null, Lines2D._prevB);
+
+                    if (Vector2.PointInTriangle(p, Lines2D._prevA, Lines2D._prevB, Lines2D._curB)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            if (this._startCapTriIndices) {
+                if (capIntersect(this._startCapTriIndices, this._startCapContour)) {
+                    return true;
+                }
+                if (capIntersect(this._endCapTriIndices, this._endCapContour)) {
+                    return true;
+                }
             }
             }
+
             return false;
             return false;
         }
         }
 
 
@@ -284,7 +322,7 @@
         }
         }
 
 
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
-            BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+            BoundingInfo2D.CreateFromMinMaxToRef(this._boundingMin.x, this._boundingMax.x, this._boundingMin.y, this._boundingMax.y, 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, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
         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, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
@@ -335,7 +373,28 @@
                 }
                 }
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
 
 
-                lines.setupLines2D(parent.owner, parent, options.id || null, pos, options.origin || null, points, options.fillThickness || 1, options.startCap || 0, options.endCap || 0, fill, options.border || null, options.borderThickness || 1, options.closed || false, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);                
+                lines.setupLines2D
+                    (
+                    parent.owner,
+                    parent,
+                    options.id || null,
+                    pos,
+                    options.origin || null,
+                    points,
+                    (options.fillThickness == null) ? 1 : options.fillThickness,
+                    (options.startCap == null) ? 0 : options.startCap,
+                    (options.endCap == null) ? 0 : options.endCap,
+                    fill,
+                    options.border || null,
+                    (options.borderThickness == null) ? 1 : options.borderThickness,
+                    (options.closed == null) ? false : options.closed,
+                    (options.isVisible == null) ? true : options.isVisible,
+                    options.marginTop || null,
+                    options.marginLeft || null,
+                    options.marginRight || null,
+                    options.marginBottom || null,
+                    options.vAlignment || null,
+                    options.hAlignment || null);
             }
             }
 
 
             return lines;
             return lines;
@@ -351,8 +410,8 @@
             let engine = this.owner.engine;
             let engine = this.owner.engine;
 
 
             // Init min/max because their being computed here
             // 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);
+            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) => {
             let perp = (v: Vector2, res: Vector2) => {
                 res.x = v.y;
                 res.x = v.y;
@@ -399,8 +458,8 @@
                 }
                 }
                 this._boundingMin.x = Math.min(this._boundingMin.x, array[offset]);
                 this._boundingMin.x = Math.min(this._boundingMin.x, array[offset]);
                 this._boundingMax.x = Math.max(this._boundingMax.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]);
+                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 store = (array: Float32Array, contour: Vector2[], index: number, max: number, p: Vector2, n: Vector2, halfThickness: number, borderThickness: number, detectFlip?: number) => {
@@ -483,7 +542,7 @@
                     case Lines2D.RoundCap:
                     case Lines2D.RoundCap:
                         if (border) {
                         if (border) {
                             vbsize = sd;
                             vbsize = sd;
-                            ibsize = (sd-2) * 3;
+                            ibsize = (sd - 2) * 3;
                         } else {
                         } else {
                             vbsize = (sd / 2) + 1;
                             vbsize = (sd / 2) + 1;
                             ibsize = (sd / 2) * 3;
                             ibsize = (sd / 2) * 3;
@@ -527,7 +586,7 @@
                         break;
                         break;
                     case Lines2D.RoundAnchorCap:
                     case Lines2D.RoundAnchorCap:
                         if (border) {
                         if (border) {
-                            vbsize = sd*2;
+                            vbsize = sd * 2;
                             ibsize = (sd - 1) * 6;
                             ibsize = (sd - 1) * 6;
                         } else {
                         } else {
                             vbsize = sd + 1;
                             vbsize = sd + 1;
@@ -536,29 +595,34 @@
                         break;
                         break;
                 }
                 }
 
 
-                return { vbsize: vbsize*2, ibsize: ibsize };
+                return { vbsize: vbsize * 2, ibsize: ibsize };
             }
             }
 
 
             let v = Vector2.Zero();
             let v = Vector2.Zero();
-            let storeVertex = (vb: Float32Array, baseOffset: number, index: number, basePos: Vector2, rotation: number, vertex: Vector2): number => {
+            let storeVertex = (vb: Float32Array, baseOffset: number, index: number, basePos: Vector2, rotation: number, vertex: Vector2, contour: number[]): number => {
                 let c = Math.cos(rotation);
                 let c = Math.cos(rotation);
                 let s = Math.sin(rotation);
                 let s = Math.sin(rotation);
 
 
                 v.x = (c * vertex.x) + (-s * vertex.y) + basePos.x;
                 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);
+                v.y = (s * vertex.x) + (c * vertex.y) + basePos.y;
+                let offset = baseOffset + (index * 2);
                 vb[offset + 0] = v.x;
                 vb[offset + 0] = v.x;
                 vb[offset + 1] = v.y;
                 vb[offset + 1] = v.y;
 
 
+                if (contour) {
+                    contour.push(v.x);
+                    contour.push(v.y);
+                }
+
                 updateMinMax(vb, offset);
                 updateMinMax(vb, offset);
-                return (baseOffset + index*2) / 2;
+                return (baseOffset + index * 2) / 2;
             }
             }
 
 
             let storeIndex = (ib: Float32Array, baseOffset: number, index: number, vertexIndex: number) => {
             let storeIndex = (ib: Float32Array, baseOffset: number, index: number, vertexIndex: number) => {
                 ib[baseOffset + index] = vertexIndex;
                 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 } => {
+            let buildCap = (vb: Float32Array, vbi: number, ib: Float32Array, ibi: number, pos: Vector2, thickness: number, borderThickness: number, type: number, capDir: Vector2, contour: number[]): { 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
                 // 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 dir = new Vector2(1, 0);
@@ -573,10 +637,10 @@
                         if (borderMode && !this.closed) {
                         if (borderMode && !this.closed) {
                             let vi = 0;
                             let vi = 0;
                             let ii = 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)));
+                            let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht + bt), contour);
+                            let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, ht + bt), contour);
+                            let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(bt, -(ht + bt)), contour);
+                            let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht + bt)), contour);
 
 
                             storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v2); storeIndex(ib, ibi, ii++, v3);
                             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);
                             storeIndex(ib, ibi, ii++, v1); storeIndex(ib, ibi, ii++, v3); storeIndex(ib, ibi, ii++, v4);
@@ -585,210 +649,213 @@
                     case Lines2D.ArrowCap:
                     case Lines2D.ArrowCap:
                         ht *= 2;
                         ht *= 2;
                     case Lines2D.TriangleCap:
                     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);
+                        {
+                            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), null);
+                                let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0), null);
+                                let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht), null);
+                                let v4 = storeVertex(vb, vbi, 3, pos, angle, new Vector2(0, ht + f), contour);
+                                let v5 = storeVertex(vb, vbi, 4, pos, angle, new Vector2(ht + f, 0), contour);
+                                let v6 = storeVertex(vb, vbi, 5, pos, angle, new Vector2(0, -(ht + f)), contour);
+
+                                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 v10 = storeVertex(vb, vbi, 9, pos, angle, new Vector2(0, -(rht + bt)), null);
+                                    let v12 = storeVertex(vb, vbi, 11, pos, angle, new Vector2(-bt, -(ht + f)), contour);
+                                    let v11 = storeVertex(vb, vbi, 10, pos, angle, new Vector2(-bt, -(rht + bt)), contour);
+
+                                    let v7 = storeVertex(vb, vbi, 6, pos, angle, new Vector2(0, rht + bt), null);
+                                    let v8 = storeVertex(vb, vbi, 7, pos, angle, new Vector2(-bt, rht + bt), contour);
+                                    let v9 = storeVertex(vb, vbi, 8, pos, angle, new Vector2(-bt, ht + f), contour);
+
+
+                                    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), contour);
+                                let v2 = storeVertex(vb, vbi, 1, pos, angle, new Vector2(ht, 0), contour);
+                                let v3 = storeVertex(vb, vbi, 2, pos, angle, new Vector2(0, -ht), contour);
+
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
                             }
                             }
-                        } 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;
                         }
                         }
-                        break;
-                    }
                     case Lines2D.RoundCap:
                     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 (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), null);
+                                    let v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new Vector2(Math.cos(curA) * (ht + bt), Math.sin(curA) * (ht + bt)), contour);
+
+                                    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), null);
+                                let curA = -Math.PI / 2;
+                                let incA = Math.PI / (sd / 2 - 1);
 
 
-                                if (i > 0) {
-                                    storeIndex(ib, ibi, ii++, v1 - 2);
-                                    storeIndex(ib, ibi, ii++, v2 - 2);
-                                    storeIndex(ib, ibi, ii++, v2);
+                                storeVertex(vb, vbi, 1, pos, angle, new Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht), null);
+                                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), contour);
 
 
-                                    storeIndex(ib, ibi, ii++, v1 - 2);
-                                    storeIndex(ib, ibi, ii++, v2);
-                                    storeIndex(ib, ibi, ii++, v1);
+                                    storeIndex(ib, ibi, i * 3 + 0, c);
+                                    storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                    storeIndex(ib, ibi, i * 3 + 2, v2);
+                                    curA += incA;
                                 }
                                 }
-                                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;
                         }
                         }
-                        break;
-                    }
                     case Lines2D.SquareAnchorCap:
                     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);
+                        {
+                            let vi = 0;
+                            let c = borderMode ? null : contour;
+                            let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, t), c);
+                            let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, t), c);
+                            let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2, -t), c);
+                            let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -t), c);
+
+                            if (borderMode) {
+                                let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht + bt), null);
+                                let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, ht + bt), contour);
+                                let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, t + bt), contour);
+                                let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, t + bt), contour);
+
+                                let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(t * 2 + bt, -(t + bt)), contour);
+                                let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(t + bt)), contour);
+                                let v11 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-bt, -(ht + bt)), contour);
+                                let v12 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -(ht + bt)), null);
+
+                                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;
                         }
                         }
-                        break;
-                    }
                     case Lines2D.RoundAnchorCap:
                     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);
+                        {
+                            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), null);
+                                    let v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new Vector2(cpos + Math.cos(curA) * (t + bt), Math.sin(curA) * (t + bt)), contour);
+
+                                    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, null);
+                                storeVertex(vb, vbi, 1, pos, angle, new Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t), null);  // contour maybe TODO
                                 curA += incA;
                                 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), contour);
+
+                                    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);
                             }
                             }
-                        } 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;
                         }
                         }
-                        break;
-                    }
                     case Lines2D.DiamondAnchorCap:
                     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);
+                        {
+                            let vi = 0;
+                            let c = borderMode ? null : contour;
+                            let v1 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, ht), c);
+                            let v2 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, t), c);
+                            let v3 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht * 3, 0), c);
+                            let v4 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, -t), c);
+                            let v5 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(0, -ht), c);
+
+                            if (borderMode) {
+                                let f = Math.sqrt(bt * bt * 2);
+                                let v6 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f, ht), contour);
+                                let v7 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, t + f), contour);
+                                let v8 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht * 3 + f, 0), contour);
+                                let v9 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(ht, -(t + f)), contour);
+                                let v10 = storeVertex(vb, vbi, vi++, pos, angle, new Vector2(-f, -ht), contour);
+
+                                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;
                         }
                         }
-                        break;
-                    }
                 }
                 }
 
 
                 return null;
                 return null;
@@ -828,7 +895,7 @@
                         direction(next, cur, lineB);
                         direction(next, cur, lineB);
 
 
                         var miterLen = computeMiter(tangent, miter, lineA, lineB);
                         var miterLen = computeMiter(tangent, miter, lineA, lineB);
-                        store(vb, contour, i, total, this.points[i], miter, miterLen*ht, miterLen*bt, i - 1);
+                        store(vb, contour, i, total, this.points[i], miter, miterLen * ht, miterLen * bt, i - 1);
                     }
                     }
                 }
                 }
 
 
@@ -861,6 +928,8 @@
             }
             }
 
 
             let contour = new Array<Vector2>();
             let contour = new Array<Vector2>();
+            let startCapContour = new Array<number>();
+            let endCapContour = new Array<number>();
 
 
             // Need to create WebGL resources for fill part?
             // Need to create WebGL resources for fill part?
             if (this.fill) {
             if (this.fill) {
@@ -877,7 +946,7 @@
                 let max = total * 2;
                 let max = total * 2;
                 let triCount = (count - (this.closed ? 0 : 1)) * 2;
                 let triCount = (count - (this.closed ? 0 : 1)) * 2;
                 let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
                 let ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
-                for (let i = 0; i < triCount; i+=2) {
+                for (let i = 0; i < triCount; i += 2) {
                     ib[i * 3 + 0] = i + 0;
                     ib[i * 3 + 0] = i + 0;
                     ib[i * 3 + 1] = i + 1;
                     ib[i * 3 + 1] = i + 1;
                     ib[i * 3 + 2] = (i + 2) % max;
                     ib[i * 3 + 2] = (i + 2) % max;
@@ -887,8 +956,8 @@
                     ib[i * 3 + 5] = (i + 2) % 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);
+                buildCap(vb, count * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, null, this.startCap, startDir, this.border ? null : startCapContour);
+                buildCap(vb, (count * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, null, this.endCap, endDir, this.border ? null : startCapContour);
 
 
                 renderCache.fillVB = engine.createVertexBuffer(vb);
                 renderCache.fillVB = engine.createVertexBuffer(vb);
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIB = engine.createIndexBuffer(ib);
@@ -939,8 +1008,8 @@
                     ib[i * 3 + 11] = (i + 7) % 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);
+                buildCap(vb, count * 2 * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, this.borderThickness, this.startCap, startDir, startCapContour);
+                buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, endDir, endCapContour);
 
 
                 renderCache.borderVB = engine.createVertexBuffer(vb);
                 renderCache.borderVB = engine.createVertexBuffer(vb);
                 renderCache.borderIB = engine.createIndexBuffer(ib);
                 renderCache.borderIB = engine.createIndexBuffer(ib);
@@ -958,6 +1027,16 @@
             }
             }
 
 
             this._contour = contour;
             this._contour = contour;
+            if (startCapContour.length > 0) {
+                let startCapTri = Earcut.earcut(startCapContour, null, 2);
+                this._startCapTriIndices = startCapTri;
+                this._startCapContour = startCapContour;
+            }
+            if (endCapContour.length > 0) {
+                let endCapTri = Earcut.earcut(endCapContour, null, 2);
+                this._endCapContour = endCapContour;
+                this._endCapTriIndices = endCapTri;
+            }
             let bs = this._boundingMax.subtract(this._boundingMin);
             let bs = this._boundingMax.subtract(this._boundingMin);
             this._size.width = bs.x;
             this._size.width = bs.x;
             this._size.height = bs.y;
             this._size.height = bs.y;
@@ -993,13 +1072,13 @@
             return true;
             return true;
         }
         }
 
 
-        private static _noCap            = 0;
-        private static _roundCap         = 1;
-        private static _triangleCap      = 2;
-        private static _squareAnchorCap  = 3;
-        private static _roundAnchorCap   = 4;
+        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 _diamondAnchorCap = 5;
-        private static _arrowCap         = 6;
+        private static _arrowCap = 6;
 
 
         private static _roundCapSubDiv = 36;
         private static _roundCapSubDiv = 36;
 
 
@@ -1007,6 +1086,10 @@
         private _boundingMax: Vector2;
         private _boundingMax: Vector2;
         private _size: Size;
         private _size: Size;
         private _contour: Vector2[];
         private _contour: Vector2[];
+        private _startCapContour: number[];
+        private _startCapTriIndices: number[];
+        private _endCapContour: number[];
+        private _endCapTriIndices: number[];
 
 
         private _closed: boolean;
         private _closed: boolean;
         private _startCap: number;
         private _startCap: number;

+ 168 - 40
src/Canvas2d/babylon.modelRenderCache.js

@@ -1,46 +1,174 @@
+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 BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var GroupInstanceInfo = (function () {
     var GroupInstanceInfo = (function () {
-        function GroupInstanceInfo(owner, cache) {
-            this._owner = owner;
-            this._modelCache = cache;
-            this._modelCache.addRef();
-            this._instancesPartsData = new Array();
-            this._instancesPartsBuffer = new Array();
-            this._instancesPartsBufferSize = new Array();
-            this._partIndexFromId = new BABYLON.StringDictionary();
-            this._instancesPartsUsedShaderCategories = new Array();
+        function GroupInstanceInfo(owner, mrc, partCount) {
+            this._partCount = partCount;
+            this.owner = owner;
+            this.modelRenderCache = mrc;
+            this.modelRenderCache.addRef();
+            this.partIndexFromId = new BABYLON.StringDictionary();
+            this._usedShaderCategories = new Array(partCount);
+            this._strides = new Array(partCount);
+            this._opaqueData = null;
+            this._alphaTestData = null;
+            this._transparentData = null;
+            this.opaqueDirty = this.alphaTestDirty = this.transparentDirty = this.transparentOrderDirty = false;
         }
         }
         GroupInstanceInfo.prototype.dispose = function () {
         GroupInstanceInfo.prototype.dispose = function () {
             if (this._isDisposed) {
             if (this._isDisposed) {
                 return false;
                 return false;
             }
             }
-            if (this._modelCache) {
-                this._modelCache.dispose();
+            if (this.modelRenderCache) {
+                this.modelRenderCache.dispose();
             }
             }
-            var engine = this._owner.owner.engine;
-            if (this._instancesPartsBuffer) {
-                this._instancesPartsBuffer.forEach(function (b) {
-                    engine._releaseBuffer(b);
-                });
+            var engine = this.owner.owner.engine;
+            if (this.opaqueData) {
+                this.opaqueData.forEach(function (d) { return d.dispose(engine); });
+                this.opaqueData = null;
             }
             }
-            this._partIndexFromId = null;
-            this._instancesPartsData = null;
-            this._instancesPartsBufferSize = null;
-            this._instancesPartsUsedShaderCategories = null;
+            this.partIndexFromId = null;
+            this._isDisposed = true;
             return true;
             return true;
         };
         };
+        Object.defineProperty(GroupInstanceInfo.prototype, "hasOpaqueData", {
+            get: function () {
+                return this._opaqueData != null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GroupInstanceInfo.prototype, "hasAlphaTestData", {
+            get: function () {
+                return this._alphaTestData != null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GroupInstanceInfo.prototype, "hasTransparentData", {
+            get: function () {
+                return this._transparentData != null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GroupInstanceInfo.prototype, "opaqueData", {
+            get: function () {
+                if (!this._opaqueData) {
+                    this._opaqueData = new Array(this._partCount);
+                    for (var i = 0; i < this._partCount; i++) {
+                        this._opaqueData[i] = new GroupInfoPartData(this._strides[i]);
+                    }
+                }
+                return this._opaqueData;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GroupInstanceInfo.prototype, "alphaTestData", {
+            get: function () {
+                if (!this._alphaTestData) {
+                    this._alphaTestData = new Array(this._partCount);
+                    for (var i = 0; i < this._partCount; i++) {
+                        this._alphaTestData[i] = new GroupInfoPartData(this._strides[i]);
+                    }
+                }
+                return this._alphaTestData;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GroupInstanceInfo.prototype, "transparentData", {
+            get: function () {
+                if (!this._transparentData) {
+                    this._transparentData = new Array(this._partCount);
+                    for (var i = 0; i < this._partCount; i++) {
+                        var zoff = this.modelRenderCache._partData[i]._zBiasOffset;
+                        this._transparentData[i] = new TransparentGroupInfoPartData(this._strides[i], zoff);
+                    }
+                }
+                return this._transparentData;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        GroupInstanceInfo.prototype.sortTransparentData = function () {
+            if (!this.transparentOrderDirty) {
+                return;
+            }
+            for (var i = 0; i < this._transparentData.length; i++) {
+                var td = this._transparentData[i];
+                td._partData.sort();
+            }
+            this.transparentOrderDirty = false;
+        };
+        Object.defineProperty(GroupInstanceInfo.prototype, "usedShaderCategories", {
+            get: function () {
+                return this._usedShaderCategories;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GroupInstanceInfo.prototype, "strides", {
+            get: function () {
+                return this._strides;
+            },
+            enumerable: true,
+            configurable: true
+        });
         return GroupInstanceInfo;
         return GroupInstanceInfo;
-    }());
+    })();
     BABYLON.GroupInstanceInfo = GroupInstanceInfo;
     BABYLON.GroupInstanceInfo = GroupInstanceInfo;
+    var TransparentSegment = (function () {
+        function TransparentSegment() {
+        }
+        return TransparentSegment;
+    })();
+    BABYLON.TransparentSegment = TransparentSegment;
+    var GroupInfoPartData = (function () {
+        function GroupInfoPartData(stride) {
+            this._partData = null;
+            this._partBuffer = null;
+            this._partBufferSize = 0;
+            this._partData = new BABYLON.DynamicFloatArray(stride / 4, 50);
+            this._isDisposed = false;
+        }
+        GroupInfoPartData.prototype.dispose = function (engine) {
+            if (this._isDisposed) {
+                return false;
+            }
+            if (this._partBuffer) {
+                engine._releaseBuffer(this._partBuffer);
+                this._partBuffer = null;
+            }
+            this._partData = null;
+            this._isDisposed = true;
+        };
+        return GroupInfoPartData;
+    })();
+    BABYLON.GroupInfoPartData = GroupInfoPartData;
+    var TransparentGroupInfoPartData = (function (_super) {
+        __extends(TransparentGroupInfoPartData, _super);
+        function TransparentGroupInfoPartData(stride, zoff) {
+            _super.call(this, stride);
+            this._partData.compareValueOffset = zoff;
+            this._partData.sortingAscending = false;
+        }
+        return TransparentGroupInfoPartData;
+    })(GroupInfoPartData);
+    BABYLON.TransparentGroupInfoPartData = TransparentGroupInfoPartData;
     var ModelRenderCache = (function () {
     var ModelRenderCache = (function () {
-        function ModelRenderCache(engine, modelKey, isTransparent) {
+        function ModelRenderCache(engine, modelKey) {
             this._engine = engine;
             this._engine = engine;
             this._modelKey = modelKey;
             this._modelKey = modelKey;
-            this._isTransparent = isTransparent;
             this._nextKey = 1;
             this._nextKey = 1;
             this._refCounter = 1;
             this._refCounter = 1;
             this._instancesData = new BABYLON.StringDictionary();
             this._instancesData = new BABYLON.StringDictionary();
+            this._partData = null;
         }
         }
         ModelRenderCache.prototype.dispose = function () {
         ModelRenderCache.prototype.dispose = function () {
             if (--this._refCounter !== 0) {
             if (--this._refCounter !== 0) {
@@ -91,8 +219,8 @@ var BABYLON;
             this._instancesData.remove(key);
             this._instancesData.remove(key);
         };
         };
         ModelRenderCache.prototype.getPartIndexFromId = function (partId) {
         ModelRenderCache.prototype.getPartIndexFromId = function (partId) {
-            for (var i = 0; i < this._partIdList.length; i++) {
-                if (this._partIdList[i] === partId) {
+            for (var i = 0; i < this._partData.length; i++) {
+                if (this._partData[i]._partId === partId) {
                     return i;
                     return i;
                 }
                 }
             }
             }
@@ -104,26 +232,27 @@ var BABYLON;
                 return null;
                 return null;
             }
             }
             var ci = this._partsClassInfo[i];
             var ci = this._partsClassInfo[i];
-            var categories = this._partsUsedCategories[i];
+            var categories = this._partData[i]._partUsedCategories;
             var res = ci.classContent.getInstancingAttributeInfos(effect, categories);
             var res = ci.classContent.getInstancingAttributeInfos(effect, categories);
             return res;
             return res;
         };
         };
         ModelRenderCache.prototype.setupUniforms = function (effect, partIndex, data, elementCount) {
         ModelRenderCache.prototype.setupUniforms = function (effect, partIndex, data, elementCount) {
-            var offset = (this._partsDataStride[partIndex] / 4) * elementCount;
+            var pd = this._partData[partIndex];
+            var offset = (pd._partDataStride / 4) * elementCount;
             var pci = this._partsClassInfo[partIndex];
             var pci = this._partsClassInfo[partIndex];
             var self = this;
             var self = this;
             pci.fullContent.forEach(function (k, v) {
             pci.fullContent.forEach(function (k, v) {
-                if (!v.category || self._partsUsedCategories[partIndex].indexOf(v.category) !== 1) {
+                if (!v.category || pd._partUsedCategories.indexOf(v.category) !== -1) {
                     switch (v.dataType) {
                     switch (v.dataType) {
                         case 4 /* float */:
                         case 4 /* float */:
                             {
                             {
-                                var attribOffset = v.instanceOffset.get(self._partsJoinedUsedCategories[partIndex]);
+                                var attribOffset = v.instanceOffset.get(pd._partJoinedUsedCategories);
                                 effect.setFloat(v.attributeName, data.buffer[offset + attribOffset]);
                                 effect.setFloat(v.attributeName, data.buffer[offset + attribOffset]);
                                 break;
                                 break;
                             }
                             }
                         case 0 /* Vector2 */:
                         case 0 /* Vector2 */:
                             {
                             {
-                                var attribOffset = v.instanceOffset.get(self._partsJoinedUsedCategories[partIndex]);
+                                var attribOffset = v.instanceOffset.get(pd._partJoinedUsedCategories);
                                 ModelRenderCache.v2.x = data.buffer[offset + attribOffset + 0];
                                 ModelRenderCache.v2.x = data.buffer[offset + attribOffset + 0];
                                 ModelRenderCache.v2.y = data.buffer[offset + attribOffset + 1];
                                 ModelRenderCache.v2.y = data.buffer[offset + attribOffset + 1];
                                 effect.setVector2(v.attributeName, ModelRenderCache.v2);
                                 effect.setVector2(v.attributeName, ModelRenderCache.v2);
@@ -132,7 +261,7 @@ var BABYLON;
                         case 5 /* Color3 */:
                         case 5 /* Color3 */:
                         case 1 /* Vector3 */:
                         case 1 /* Vector3 */:
                             {
                             {
-                                var attribOffset = v.instanceOffset.get(self._partsJoinedUsedCategories[partIndex]);
+                                var attribOffset = v.instanceOffset.get(pd._partJoinedUsedCategories);
                                 ModelRenderCache.v3.x = data.buffer[offset + attribOffset + 0];
                                 ModelRenderCache.v3.x = data.buffer[offset + attribOffset + 0];
                                 ModelRenderCache.v3.y = data.buffer[offset + attribOffset + 1];
                                 ModelRenderCache.v3.y = data.buffer[offset + attribOffset + 1];
                                 ModelRenderCache.v3.z = data.buffer[offset + attribOffset + 2];
                                 ModelRenderCache.v3.z = data.buffer[offset + attribOffset + 2];
@@ -142,7 +271,7 @@ var BABYLON;
                         case 6 /* Color4 */:
                         case 6 /* Color4 */:
                         case 2 /* Vector4 */:
                         case 2 /* Vector4 */:
                             {
                             {
-                                var attribOffset = v.instanceOffset.get(self._partsJoinedUsedCategories[partIndex]);
+                                var attribOffset = v.instanceOffset.get(pd._partJoinedUsedCategories);
                                 ModelRenderCache.v4.x = data.buffer[offset + attribOffset + 0];
                                 ModelRenderCache.v4.x = data.buffer[offset + attribOffset + 0];
                                 ModelRenderCache.v4.y = data.buffer[offset + attribOffset + 1];
                                 ModelRenderCache.v4.y = data.buffer[offset + attribOffset + 1];
                                 ModelRenderCache.v4.z = data.buffer[offset + attribOffset + 2];
                                 ModelRenderCache.v4.z = data.buffer[offset + attribOffset + 2];
@@ -155,13 +284,6 @@ var BABYLON;
                 }
                 }
             });
             });
         };
         };
-        Object.defineProperty(ModelRenderCache.prototype, "isTransparent", {
-            get: function () {
-                return this._isTransparent;
-            },
-            enumerable: true,
-            configurable: true
-        });
         //setupUniformsLocation(effect: Effect, uniforms: string[], partId: number) {
         //setupUniformsLocation(effect: Effect, uniforms: string[], partId: number) {
         //    let i = this.getPartIndexFromId(partId);
         //    let i = this.getPartIndexFromId(partId);
         //    if (i === null) {
         //    if (i === null) {
@@ -178,6 +300,12 @@ var BABYLON;
         ModelRenderCache.v3 = BABYLON.Vector3.Zero();
         ModelRenderCache.v3 = BABYLON.Vector3.Zero();
         ModelRenderCache.v4 = BABYLON.Vector4.Zero();
         ModelRenderCache.v4 = BABYLON.Vector4.Zero();
         return ModelRenderCache;
         return ModelRenderCache;
-    }());
+    })();
     BABYLON.ModelRenderCache = ModelRenderCache;
     BABYLON.ModelRenderCache = ModelRenderCache;
+    var ModelRenderCachePartData = (function () {
+        function ModelRenderCachePartData() {
+        }
+        return ModelRenderCachePartData;
+    })();
+    BABYLON.ModelRenderCachePartData = ModelRenderCachePartData;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 258 - 25
src/Canvas2d/babylon.prim2dBase.js

@@ -11,11 +11,68 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
 };
 };
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
+    var PrepareRender2DContext = (function () {
+        function PrepareRender2DContext() {
+            this.forceRefreshPrimitive = false;
+        }
+        return PrepareRender2DContext;
+    })();
+    BABYLON.PrepareRender2DContext = PrepareRender2DContext;
     var Render2DContext = (function () {
     var Render2DContext = (function () {
-        function Render2DContext() {
+        function Render2DContext(renderMode) {
+            this._renderMode = renderMode;
+            this.useInstancing = false;
+            this.groupInfoPartData = null;
+            this.partDataStartIndex = this.partDataEndIndex = null;
         }
         }
+        Object.defineProperty(Render2DContext.prototype, "renderMode", {
+            /**
+             * Define which render Mode should be used to render the primitive: one of Render2DContext.RenderModeXxxx property
+             */
+            get: function () {
+                return this._renderMode;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Render2DContext, "RenderModeOpaque", {
+            /**
+             * The set of primitives to render is opaque.
+             * This is the first rendering pass. All Opaque primitives are rendered. Depth Compare and Write are both enabled.
+             */
+            get: function () {
+                return Render2DContext._renderModeOpaque;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Render2DContext, "RenderModeAlphaTest", {
+            /**
+             * The set of primitives to render is using Alpha Test (aka masking).
+             * Alpha Blend is enabled, the AlphaMode must be manually set, the render occurs after the RenderModeOpaque and is depth independent (i.e. primitives are not sorted by depth). Depth Compare and Write are both enabled.
+             */
+            get: function () {
+                return Render2DContext._renderModeAlphaTest;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Render2DContext, "RenderModeTransparent", {
+            /**
+             * The set of primitives to render is transparent.
+             * Alpha Blend is enabled, the AlphaMode must be manually set, the render occurs after the RenderModeAlphaTest and is depth dependent (i.e. primitives are stored by depth and rendered back to front). Depth Compare is on, but Depth write is Off.
+             */
+            get: function () {
+                return Render2DContext._renderModeTransparent;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Render2DContext._renderModeOpaque = 1;
+        Render2DContext._renderModeAlphaTest = 2;
+        Render2DContext._renderModeTransparent = 3;
         return Render2DContext;
         return Render2DContext;
-    }());
+    })();
     BABYLON.Render2DContext = Render2DContext;
     BABYLON.Render2DContext = Render2DContext;
     /**
     /**
      * This class store information for the pointerEventObservable Observable.
      * This class store information for the pointerEventObservable Observable.
@@ -175,7 +232,7 @@ var BABYLON;
         PrimitivePointerInfo._pointerLostCapture = 0x0200;
         PrimitivePointerInfo._pointerLostCapture = 0x0200;
         PrimitivePointerInfo._mouseWheelPrecision = 3.0;
         PrimitivePointerInfo._mouseWheelPrecision = 3.0;
         return PrimitivePointerInfo;
         return PrimitivePointerInfo;
-    }());
+    })();
     BABYLON.PrimitivePointerInfo = PrimitivePointerInfo;
     BABYLON.PrimitivePointerInfo = PrimitivePointerInfo;
     /**
     /**
      * Stores information about a Primitive that was intersected
      * Stores information about a Primitive that was intersected
@@ -186,8 +243,75 @@ var BABYLON;
             this.intersectionLocation = intersectionLocation;
             this.intersectionLocation = intersectionLocation;
         }
         }
         return PrimitiveIntersectedInfo;
         return PrimitiveIntersectedInfo;
-    }());
+    })();
     BABYLON.PrimitiveIntersectedInfo = PrimitiveIntersectedInfo;
     BABYLON.PrimitiveIntersectedInfo = PrimitiveIntersectedInfo;
+    var PrimitiveMargin = (function () {
+        function PrimitiveMargin(owner) {
+            this._owner = owner;
+            this._left = this._top = this._bottom = this.right = 0;
+        }
+        Object.defineProperty(PrimitiveMargin.prototype, "top", {
+            get: function () {
+                return this._top;
+            },
+            set: function (value) {
+                if (value === this._top) {
+                    return;
+                }
+                this._top = value;
+                this._owner._marginChanged();
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitiveMargin.prototype, "left", {
+            get: function () {
+                return this._left;
+            },
+            set: function (value) {
+                if (value === this._left) {
+                    return;
+                }
+                this._left = value;
+                this._owner._marginChanged();
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitiveMargin.prototype, "right", {
+            get: function () {
+                return this._right;
+            },
+            set: function (value) {
+                if (value === this._right) {
+                    return;
+                }
+                this._right = value;
+                this._owner._marginChanged();
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PrimitiveMargin.prototype, "bottom", {
+            get: function () {
+                return this._bottom;
+            },
+            set: function (value) {
+                if (value === this._bottom) {
+                    return;
+                }
+                this._bottom = value;
+                this._owner._marginChanged();
+            },
+            enumerable: true,
+            configurable: true
+        });
+        PrimitiveMargin.Zero = function (owner) {
+            return new PrimitiveMargin(owner);
+        };
+        return PrimitiveMargin;
+    })();
+    BABYLON.PrimitiveMargin = PrimitiveMargin;
     /**
     /**
      * Main class used for the Primitive Intersection API
      * Main class used for the Primitive Intersection API
      */
      */
@@ -223,18 +347,65 @@ var BABYLON;
             }
             }
         };
         };
         return IntersectInfo2D;
         return IntersectInfo2D;
-    }());
+    })();
     BABYLON.IntersectInfo2D = IntersectInfo2D;
     BABYLON.IntersectInfo2D = IntersectInfo2D;
     var Prim2DBase = (function (_super) {
     var Prim2DBase = (function (_super) {
         __extends(Prim2DBase, _super);
         __extends(Prim2DBase, _super);
         function Prim2DBase() {
         function Prim2DBase() {
             _super.apply(this, arguments);
             _super.apply(this, arguments);
         }
         }
-        Prim2DBase.prototype.setupPrim2DBase = function (owner, parent, id, position, origin, isVisible) {
-            if (isVisible === void 0) { isVisible = true; }
+        Object.defineProperty(Prim2DBase, "HAlignLeft", {
+            get: function () { return Prim2DBase._hAlignLeft; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "HAlignCenter", {
+            get: function () { return Prim2DBase._hAlignCenter; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "HAlignRight", {
+            get: function () { return Prim2DBase._hAlignRight; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "HAlignStretch", {
+            get: function () { return Prim2DBase._hAlignStretch; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "VAlignTop", {
+            get: function () { return Prim2DBase._vAlignTop; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "VAlignCenter", {
+            get: function () { return Prim2DBase._vAlignCenter; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "VAlignBottom", {
+            get: function () { return Prim2DBase._vAlignBottom; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase, "VAlignStretch", {
+            get: function () { return Prim2DBase._vAlignStretch; },
+            enumerable: true,
+            configurable: true
+        });
+        Prim2DBase.prototype.setupPrim2DBase = function (owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, vAlignment, hAlignment) {
             if (!(this instanceof BABYLON.Group2D) && !(this instanceof BABYLON.Sprite2D && id !== null && id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
             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");
                 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");
             }
             }
+            var m = null;
+            if (marginTop || marginLeft || marginRight || marginBottom) {
+                m = new PrimitiveMargin(this);
+                m.top = marginTop || 0;
+                m.left = marginLeft || 0;
+                m.right = marginRight || 0;
+                m.bottom = marginBottom || 0;
+            }
             this.setupSmartPropertyPrim();
             this.setupSmartPropertyPrim();
             this._pointerEventObservable = new BABYLON.Observable();
             this._pointerEventObservable = new BABYLON.Observable();
             this._isPickable = true;
             this._isPickable = true;
@@ -243,6 +414,7 @@ var BABYLON;
             this._boundingInfo = new BABYLON.BoundingInfo2D();
             this._boundingInfo = new BABYLON.BoundingInfo2D();
             this._owner = owner;
             this._owner = owner;
             this._parent = parent;
             this._parent = parent;
+            this._id = id;
             if (parent != null) {
             if (parent != null) {
                 this._hierarchyDepth = parent._hierarchyDepth + 1;
                 this._hierarchyDepth = parent._hierarchyDepth + 1;
                 this._renderGroup = this.parent.traverseUp(function (p) { return p instanceof BABYLON.Group2D && p.isRenderableGroup; });
                 this._renderGroup = this.parent.traverseUp(function (p) { return p instanceof BABYLON.Group2D && p.isRenderableGroup; });
@@ -252,7 +424,6 @@ var BABYLON;
                 this._hierarchyDepth = 0;
                 this._hierarchyDepth = 0;
                 this._renderGroup = null;
                 this._renderGroup = null;
             }
             }
-            this._id = id;
             this.propertyChanged = new BABYLON.Observable();
             this.propertyChanged = new BABYLON.Observable();
             this._children = new Array();
             this._children = new Array();
             this._globalTransformProcessStep = 0;
             this._globalTransformProcessStep = 0;
@@ -266,6 +437,9 @@ var BABYLON;
             this.scale = 1;
             this.scale = 1;
             this.levelVisible = isVisible;
             this.levelVisible = isVisible;
             this.origin = origin || new BABYLON.Vector2(0.5, 0.5);
             this.origin = origin || new BABYLON.Vector2(0.5, 0.5);
+            this.margin = m;
+            this.hAlignment = hAlignment;
+            this.vAlignment = vAlignment;
         };
         };
         Object.defineProperty(Prim2DBase.prototype, "actionManager", {
         Object.defineProperty(Prim2DBase.prototype, "actionManager", {
             get: function () {
             get: function () {
@@ -418,6 +592,40 @@ var BABYLON;
             },
             },
             set: function (value) {
             set: function (value) {
                 this._zOrder = value;
                 this._zOrder = value;
+                this.onZOrderChanged();
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase.prototype, "margin", {
+            get: function () {
+                if (!this._margin) {
+                    this._margin = new PrimitiveMargin(this);
+                }
+                return this._margin;
+            },
+            set: function (value) {
+                this._margin = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase.prototype, "hAlignment", {
+            get: function () {
+                return this._hAlignment;
+            },
+            set: function (value) {
+                this._hAlignment = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase.prototype, "vAlignment", {
+            get: function () {
+                return this._vAlignment;
+            },
+            set: function (value) {
+                this._vAlignment = value;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
@@ -491,7 +699,7 @@ var BABYLON;
         });
         });
         Object.defineProperty(Prim2DBase.prototype, "boundingInfo", {
         Object.defineProperty(Prim2DBase.prototype, "boundingInfo", {
             /**
             /**
-             * Get the boundingInfo associated to the primitive.
+             * Get the boundingInfo associated to the primitive and its children.
              * The value is supposed to be always up to date
              * The value is supposed to be always up to date
              */
              */
             get: function () {
             get: function () {
@@ -521,6 +729,8 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
+        Prim2DBase.prototype.onZOrderChanged = function () {
+        };
         Prim2DBase.prototype.levelIntersect = function (intersectInfo) {
         Prim2DBase.prototype.levelIntersect = function (intersectInfo) {
             return false;
             return false;
         };
         };
@@ -561,23 +771,26 @@ var BABYLON;
                 return false;
                 return false;
             }
             }
             // Fast rejection test with boundingInfo
             // Fast rejection test with boundingInfo
-            if (!this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
+            if (this.isPickable && !this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
                 // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
                 // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
                 intersectInfo._exit(firstLevel);
                 intersectInfo._exit(firstLevel);
                 return false;
                 return false;
             }
             }
             // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
             // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
-            var levelIntersectRes = this.levelIntersect(intersectInfo);
-            if (levelIntersectRes) {
-                var pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
-                intersectInfo.intersectedPrimitives.push(pii);
-                if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.getActualZOffset() > pii.prim.getActualZOffset())) {
-                    intersectInfo.topMostIntersectedPrimitive = pii;
-                }
-                // If we must stop at the first intersection, we're done, quit!
-                if (intersectInfo.findFirstOnly) {
-                    intersectInfo._exit(firstLevel);
-                    return true;
+            var levelIntersectRes = false;
+            if (this.isPickable) {
+                levelIntersectRes = this.levelIntersect(intersectInfo);
+                if (levelIntersectRes) {
+                    var pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
+                    intersectInfo.intersectedPrimitives.push(pii);
+                    if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.getActualZOffset() > pii.prim.getActualZOffset())) {
+                        intersectInfo.topMostIntersectedPrimitive = pii;
+                    }
+                    // If we must stop at the first intersection, we're done, quit!
+                    if (intersectInfo.findFirstOnly) {
+                        intersectInfo._exit(firstLevel);
+                        return true;
+                    }
                 }
                 }
             }
             }
             // Recurse to children if needed
             // Recurse to children if needed
@@ -621,6 +834,7 @@ var BABYLON;
         };
         };
         Prim2DBase.prototype.addChild = function (child) {
         Prim2DBase.prototype.addChild = function (child) {
             child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
             child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
+            //            console.log(`Node: ${child.id} has depth: ${child._hierarchyDepthOffset}`);
             child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             this._children.push(child);
             this._children.push(child);
         };
         };
@@ -656,6 +870,8 @@ var BABYLON;
                 this._renderGroup._addPrimToDirtyList(this);
                 this._renderGroup._addPrimToDirtyList(this);
             }
             }
         };
         };
+        Prim2DBase.prototype._marginChanged = function () {
+        };
         Prim2DBase.prototype._needPrepare = function () {
         Prim2DBase.prototype._needPrepare = function () {
             return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
             return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         };
         };
@@ -696,8 +912,8 @@ var BABYLON;
             }
             }
         };
         };
         Prim2DBase.prototype.updateGlobalTransVisOf = function (list, recurse) {
         Prim2DBase.prototype.updateGlobalTransVisOf = function (list, recurse) {
-            for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
-                var cur = list_1[_i];
+            for (var _i = 0; _i < list.length; _i++) {
+                var cur = list[_i];
                 cur.updateGlobalTransVis(recurse);
                 cur.updateGlobalTransVis(recurse);
             }
             }
         };
         };
@@ -752,7 +968,15 @@ var BABYLON;
                 }
                 }
             }
             }
         };
         };
-        Prim2DBase.PRIM2DBASE_PROPCOUNT = 10;
+        Prim2DBase.PRIM2DBASE_PROPCOUNT = 12;
+        Prim2DBase._hAlignLeft = 1;
+        Prim2DBase._hAlignCenter = 2;
+        Prim2DBase._hAlignRight = 3;
+        Prim2DBase._hAlignStretch = 4;
+        Prim2DBase._vAlignTop = 1;
+        Prim2DBase._vAlignCenter = 2;
+        Prim2DBase._vAlignBottom = 3;
+        Prim2DBase._vAlignStretch = 4;
         __decorate([
         __decorate([
             BABYLON.instanceLevelProperty(1, function (pi) { return Prim2DBase.positionProperty = pi; }, false, true)
             BABYLON.instanceLevelProperty(1, function (pi) { return Prim2DBase.positionProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "position", null);
         ], Prim2DBase.prototype, "position", null);
@@ -774,10 +998,19 @@ var BABYLON;
         __decorate([
         __decorate([
             BABYLON.instanceLevelProperty(7, function (pi) { return Prim2DBase.zOrderProperty = pi; })
             BABYLON.instanceLevelProperty(7, function (pi) { return Prim2DBase.zOrderProperty = pi; })
         ], Prim2DBase.prototype, "zOrder", null);
         ], Prim2DBase.prototype, "zOrder", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(8, function (pi) { return Prim2DBase.marginProperty = pi; })
+        ], Prim2DBase.prototype, "margin", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(9, function (pi) { return Prim2DBase.hAlignmentProperty = pi; })
+        ], Prim2DBase.prototype, "hAlignment", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(10, function (pi) { return Prim2DBase.vAlignmentProperty = pi; })
+        ], Prim2DBase.prototype, "vAlignment", null);
         Prim2DBase = __decorate([
         Prim2DBase = __decorate([
             BABYLON.className("Prim2DBase")
             BABYLON.className("Prim2DBase")
         ], Prim2DBase);
         ], Prim2DBase);
         return Prim2DBase;
         return Prim2DBase;
-    }(BABYLON.SmartPropertyPrim));
+    })(BABYLON.SmartPropertyPrim);
     BABYLON.Prim2DBase = Prim2DBase;
     BABYLON.Prim2DBase = Prim2DBase;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 21 - 19
src/Canvas2d/babylon.prim2dBase.ts

@@ -1,6 +1,6 @@
 module BABYLON {
 module BABYLON {
 
 
-    export class PreapreRender2DContext {
+    export class PrepareRender2DContext {
         constructor() {
         constructor() {
             this.forceRefreshPrimitive = false;
             this.forceRefreshPrimitive = false;
         }
         }
@@ -799,7 +799,7 @@
         }
         }
 
 
         /**
         /**
-         * Get the boundingInfo associated to the primitive.
+         * Get the boundingInfo associated to the primitive and its children.
          * The value is supposed to be always up to date
          * The value is supposed to be always up to date
          */
          */
         public get boundingInfo(): BoundingInfo2D {
         public get boundingInfo(): BoundingInfo2D {
@@ -876,28 +876,30 @@
             }
             }
 
 
             // Fast rejection test with boundingInfo
             // Fast rejection test with boundingInfo
-            if (!this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
+            if (this.isPickable && !this.boundingInfo.doesIntersect(intersectInfo._localPickPosition)) {
                 // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
                 // Important to call this before each return to allow a good recursion next time this intersectInfo is reused
                 intersectInfo._exit(firstLevel);
                 intersectInfo._exit(firstLevel);
                 return false;
                 return false;
             }
             }
 
 
             // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
             // We hit the boundingInfo that bounds this primitive and its children, now we have to test on the primitive of this level
-            let levelIntersectRes = this.levelIntersect(intersectInfo);
-            if (levelIntersectRes) {
-                let pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
-                intersectInfo.intersectedPrimitives.push(pii);
-                if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.getActualZOffset() > pii.prim.getActualZOffset())) {
-                    intersectInfo.topMostIntersectedPrimitive = pii;
-                }
+            let levelIntersectRes = false;
+            if (this.isPickable) {
+                levelIntersectRes = this.levelIntersect(intersectInfo);
+                if (levelIntersectRes) {
+                    let pii = new PrimitiveIntersectedInfo(this, intersectInfo._localPickPosition.clone());
+                    intersectInfo.intersectedPrimitives.push(pii);
+                    if (!intersectInfo.topMostIntersectedPrimitive || (intersectInfo.topMostIntersectedPrimitive.prim.getActualZOffset() > pii.prim.getActualZOffset())) {
+                        intersectInfo.topMostIntersectedPrimitive = pii;
+                    }
 
 
-                // If we must stop at the first intersection, we're done, quit!
-                if (intersectInfo.findFirstOnly) {
-                    intersectInfo._exit(firstLevel);
-                    return true;
+                    // If we must stop at the first intersection, we're done, quit!
+                    if (intersectInfo.findFirstOnly) {
+                        intersectInfo._exit(firstLevel);
+                        return true;
+                    }
                 }
                 }
             }
             }
-
             // Recurse to children if needed
             // Recurse to children if needed
             if (!levelIntersectRes || !intersectInfo.findFirstOnly) {
             if (!levelIntersectRes || !intersectInfo.findFirstOnly) {
                 for (let curChild of this._children) {
                 for (let curChild of this._children) {
@@ -946,7 +948,7 @@
 
 
         private addChild(child: Prim2DBase) {
         private addChild(child: Prim2DBase) {
             child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
             child._hierarchyDepthOffset = this._hierarchyDepthOffset + ((this._children.length + 1) * this._siblingDepthOffset);
-            console.log(`Node: ${child.id} has depth: ${child._hierarchyDepthOffset}`);
+//            console.log(`Node: ${child.id} has depth: ${child._hierarchyDepthOffset}`);
             child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             child._siblingDepthOffset = this._siblingDepthOffset / this.owner.hierarchyLevelMaxSiblingCount;
             this._children.push(child);
             this._children.push(child);
         }
         }
@@ -998,15 +1000,15 @@
             return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
             return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         }
         }
 
 
-        public _prepareRender(context: PreapreRender2DContext) {
+        public _prepareRender(context: PrepareRender2DContext) {
             this._prepareRenderPre(context);
             this._prepareRenderPre(context);
             this._prepareRenderPost(context);
             this._prepareRenderPost(context);
         }
         }
 
 
-        public _prepareRenderPre(context: PreapreRender2DContext) {
+        public _prepareRenderPre(context: PrepareRender2DContext) {
         }
         }
 
 
-        public _prepareRenderPost(context: PreapreRender2DContext) {
+        public _prepareRenderPost(context: PrepareRender2DContext) {
             // Don't recurse if it's a renderable group, the content will be processed by the group itself
             // Don't recurse if it's a renderable group, the content will be processed by the group itself
             if (this instanceof Group2D) {
             if (this instanceof Group2D) {
                 var self: any = this;
                 var self: any = this;

+ 150 - 64
src/Canvas2d/babylon.rectangle2d.js

@@ -13,69 +13,87 @@ var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
     var Rectangle2DRenderCache = (function (_super) {
     var Rectangle2DRenderCache = (function (_super) {
         __extends(Rectangle2DRenderCache, _super);
         __extends(Rectangle2DRenderCache, _super);
-        function Rectangle2DRenderCache(engine, modelKey, isTransparent) {
-            _super.call(this, engine, modelKey, isTransparent);
+        function Rectangle2DRenderCache(engine, modelKey) {
+            _super.call(this, engine, modelKey);
+            this.effectsReady = false;
+            this.fillVB = null;
+            this.fillIB = null;
+            this.fillIndicesCount = 0;
+            this.instancingFillAttributes = null;
+            this.effectFill = null;
+            this.effectFillInstanced = null;
+            this.borderVB = null;
+            this.borderIB = null;
+            this.borderIndicesCount = 0;
+            this.instancingBorderAttributes = null;
+            this.effectBorder = null;
+            this.effectBorderInstanced = null;
         }
         }
         Rectangle2DRenderCache.prototype.render = function (instanceInfo, context) {
         Rectangle2DRenderCache.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;
+            // Do nothing if the shader is still loading/preparing 
+            if (!this.effectsReady) {
+                if ((this.effectFill && (!this.effectFill.isReady() || (this.effectFillInstanced && !this.effectFillInstanced.isReady()))) ||
+                    (this.effectBorder && (!this.effectBorder.isReady() || (this.effectBorderInstanced && !this.effectBorderInstanced.isReady())))) {
+                    return false;
+                }
+                this.effectsReady = true;
             }
             }
-            var engine = instanceInfo._owner.owner.engine;
+            var engine = instanceInfo.owner.owner.engine;
             var depthFunction = 0;
             var depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
             if (this.effectFill && this.effectBorder) {
                 depthFunction = engine.getDepthFunction();
                 depthFunction = engine.getDepthFunction();
                 engine.setDepthFunctionToLessOrEqual();
                 engine.setDepthFunctionToLessOrEqual();
             }
             }
-            var cur;
-            if (this.isTransparent) {
-                cur = engine.getAlphaMode();
-                engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
-            }
+            var curAlphaMode = engine.getAlphaMode();
             if (this.effectFill) {
             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) {
+                var partIndex = instanceInfo.partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
+                var pid = context.groupInfoPartData[partIndex];
+                if (context.renderMode !== BABYLON.Render2DContext.RenderModeOpaque) {
+                    engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+                }
+                var effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
+                engine.enableEffect(effect);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, effect);
+                if (context.useInstancing) {
                     if (!this.instancingFillAttributes) {
                     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);
+                        this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, effect);
                     }
                     }
-                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
-                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 }
                 }
                 else {
                 else {
-                    for (var i = 0; i < count; i++) {
-                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                    for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
+                        this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.fillIndicesCount);
                         engine.draw(true, 0, this.fillIndicesCount);
                     }
                     }
                 }
                 }
             }
             }
             if (this.effectBorder) {
             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) {
+                var partIndex = instanceInfo.partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
+                var pid = context.groupInfoPartData[partIndex];
+                if (context.renderMode !== BABYLON.Render2DContext.RenderModeOpaque) {
+                    engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+                }
+                var effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
+                engine.enableEffect(effect);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, effect);
+                if (context.useInstancing) {
                     if (!this.instancingBorderAttributes) {
                     if (!this.instancingBorderAttributes) {
-                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, effect);
                     }
                     }
-                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
-                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.updateAndBindInstancesBuffer(pid._partBuffer, null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, pid._partData.usedElementCount);
                     engine.unbindInstanceAttributes();
                     engine.unbindInstanceAttributes();
                 }
                 }
                 else {
                 else {
-                    for (var i = 0; i < count; i++) {
-                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                    for (var i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
+                        this.setupUniforms(effect, partIndex, pid._partData, i);
                         engine.draw(true, 0, this.borderIndicesCount);
                         engine.draw(true, 0, this.borderIndicesCount);
                     }
                     }
                 }
                 }
             }
             }
-            if (this.isTransparent) {
-                engine.setAlphaMode(cur);
-            }
+            engine.setAlphaMode(curAlphaMode);
             if (this.effectFill && this.effectBorder) {
             if (this.effectFill && this.effectBorder) {
                 engine.setDepthFunction(depthFunction);
                 engine.setDepthFunction(depthFunction);
             }
             }
@@ -97,6 +115,10 @@ var BABYLON;
                 this._engine._releaseEffect(this.effectFill);
                 this._engine._releaseEffect(this.effectFill);
                 this.effectFill = null;
                 this.effectFill = null;
             }
             }
+            if (this.effectFillInstanced) {
+                this._engine._releaseEffect(this.effectFillInstanced);
+                this.effectFillInstanced = null;
+            }
             if (this.borderVB) {
             if (this.borderVB) {
                 this._engine._releaseBuffer(this.borderVB);
                 this._engine._releaseBuffer(this.borderVB);
                 this.borderVB = null;
                 this.borderVB = null;
@@ -109,10 +131,14 @@ var BABYLON;
                 this._engine._releaseEffect(this.effectBorder);
                 this._engine._releaseEffect(this.effectBorder);
                 this.effectBorder = null;
                 this.effectBorder = null;
             }
             }
+            if (this.effectBorderInstanced) {
+                this._engine._releaseEffect(this.effectBorderInstanced);
+                this.effectBorderInstanced = null;
+            }
             return true;
             return true;
         };
         };
         return Rectangle2DRenderCache;
         return Rectangle2DRenderCache;
-    }(BABYLON.ModelRenderCache));
+    })(BABYLON.ModelRenderCache);
     BABYLON.Rectangle2DRenderCache = Rectangle2DRenderCache;
     BABYLON.Rectangle2DRenderCache = Rectangle2DRenderCache;
     var Rectangle2DInstanceData = (function (_super) {
     var Rectangle2DInstanceData = (function (_super) {
         __extends(Rectangle2DInstanceData, _super);
         __extends(Rectangle2DInstanceData, _super);
@@ -130,7 +156,7 @@ var BABYLON;
             BABYLON.instanceData()
             BABYLON.instanceData()
         ], Rectangle2DInstanceData.prototype, "properties", null);
         ], Rectangle2DInstanceData.prototype, "properties", null);
         return Rectangle2DInstanceData;
         return Rectangle2DInstanceData;
-    }(BABYLON.Shape2DInstanceData));
+    })(BABYLON.Shape2DInstanceData);
     BABYLON.Rectangle2DInstanceData = Rectangle2DInstanceData;
     BABYLON.Rectangle2DInstanceData = Rectangle2DInstanceData;
     var Rectangle2D = (function (_super) {
     var Rectangle2D = (function (_super) {
         __extends(Rectangle2D, _super);
         __extends(Rectangle2D, _super);
@@ -180,17 +206,64 @@ var BABYLON;
             if (this.notRounded) {
             if (this.notRounded) {
                 return true;
                 return true;
             }
             }
-            // Well, for now we neglect the area where the pickPosition could be outside due to the roundRadius...
-            // TODO make REAL intersection test here!
+            // If we got so far it means the bounding box at least passed, so we know it's inside the bounding rectangle, but it can be outside the roundedRectangle.
+            // The easiest way is to check if the point is inside on of the four corners area (a little square of roundRadius size at the four corners)
+            // If it's the case for one, check if the mouse is located in the quarter that we care about (the one who is visible) then finally make a distance check with the roundRadius radius to see if it's inside the circle quarter or outside.
+            // First let remove the origin out the equation, to have the rectangle with an origin at bottom/left
+            var o = this.origin;
+            var size = this.size;
+            Rectangle2D._i0.x = intersectInfo._localPickPosition.x + (size.width * o.x);
+            Rectangle2D._i0.y = intersectInfo._localPickPosition.y + (size.height * o.y);
+            var rr = this.roundRadius;
+            var rrs = rr * rr;
+            // Check if the point is in the bottom/left quarter area
+            Rectangle2D._i1.x = rr;
+            Rectangle2D._i1.y = rr;
+            if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+            // Check if the point is in the top/left quarter area
+            Rectangle2D._i1.x = rr;
+            Rectangle2D._i1.y = size.height - rr;
+            if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+            // Check if the point is in the top/right quarter area
+            Rectangle2D._i1.x = size.width - rr;
+            Rectangle2D._i1.y = size.height - rr;
+            if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+            // Check if the point is in the bottom/right quarter area
+            Rectangle2D._i1.x = size.width - rr;
+            Rectangle2D._i1.y = rr;
+            if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+            // At any other locations the point is guarantied to be inside
             return true;
             return true;
         };
         };
         Rectangle2D.prototype.updateLevelBoundingInfo = function () {
         Rectangle2D.prototype.updateLevelBoundingInfo = function () {
             BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
             BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
         };
         };
-        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, origin, true, fill, border, borderThickness);
+        Rectangle2D.prototype.setupRectangle2D = function (owner, parent, id, position, origin, size, roundRadius, fill, border, borderThickness, isVisible, marginTop, marginLeft, marginRight, marginBottom, vAlignment, hAlignment) {
+            this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.size = size;
             this.size = size;
             this.notRounded = !roundRadius;
             this.notRounded = !roundRadius;
             this.roundRadius = roundRadius;
             this.roundRadius = roundRadius;
@@ -200,32 +273,34 @@ var BABYLON;
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * options:
          * options:
          *  - id a text identifier, for information purpose
          *  - 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
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
          *  - 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
+         *  - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
          *  - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp rectangle).
          *  - 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.
          *  - 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.
          *  - 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.
          *  - borderThickness: the thickness of the drawn border, default is 1.
+         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
          */
         Rectangle2D.Create = function (parent, options) {
         Rectangle2D.Create = function (parent, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             BABYLON.Prim2DBase.CheckParent(parent);
             var rect = new Rectangle2D();
             var rect = new Rectangle2D();
-            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;
+            if (!options) {
+                rect.setupRectangle2D(parent.owner, parent, null, BABYLON.Vector2.Zero(), null, new BABYLON.Size(10, 10), 0, BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, true, null, null, null, null, null, null);
             }
             }
             else {
             else {
-                rect.fill = BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                var pos = options.position || new BABYLON.Vector2(options.x || 0, options.y || 0);
+                var size = options.size || (new BABYLON.Size(options.width || 10, options.height || 10));
+                var fill = options.fill === undefined ? BABYLON.Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF") : options.fill;
+                rect.setupRectangle2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, (options.roundRadius == null) ? 0 : options.roundRadius, fill, options.border || null, (options.borderThickness == null) ? 1 : options.borderThickness, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
             }
             }
-            rect.border = options && options.border || null;
-            rect.borderThickness = options && options.borderThickness || 1;
             return rect;
             return rect;
         };
         };
-        Rectangle2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
-            var renderCache = new Rectangle2DRenderCache(this.owner.engine, modelKey, isTransparent);
+        Rectangle2D.prototype.createModelRenderCache = function (modelKey) {
+            var renderCache = new Rectangle2DRenderCache(this.owner.engine, modelKey);
             return renderCache;
             return renderCache;
         };
         };
         Rectangle2D.prototype.setupModelRenderCache = function (modelRenderCache) {
         Rectangle2D.prototype.setupModelRenderCache = function (modelRenderCache) {
@@ -249,10 +324,14 @@ var BABYLON;
                 ib[triCount * 3 - 2] = 1;
                 ib[triCount * 3 - 2] = 1;
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIndicesCount = triCount * 3;
                 renderCache.fillIndicesCount = triCount * 3;
-                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"]);
-                renderCache.effectFill = engine.createEffect({ vertex: "rect2d", fragment: "rect2d" }, ei.attributes, ei.uniforms, [], ei.defines, null, function (e) {
-                    //                    renderCache.setupUniformsLocation(e, ei.uniforms, Shape2D.SHAPE2D_FILLPARTID);
-                });
+                // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], true);
+                if (ei) {
+                    renderCache.effectFillInstanced = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
+                }
+                // Get the non instanced version
+                ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"], false);
+                renderCache.effectFill = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
             }
             }
             // Need to create WebGL resource for border part?
             // Need to create WebGL resource for border part?
             if (this.border) {
             if (this.border) {
@@ -277,10 +356,14 @@ var BABYLON;
                 }
                 }
                 renderCache.borderIB = engine.createIndexBuffer(ib);
                 renderCache.borderIB = engine.createIndexBuffer(ib);
                 renderCache.borderIndicesCount = triCount * 3;
                 renderCache.borderIndicesCount = triCount * 3;
-                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"]);
-                renderCache.effectBorder = engine.createEffect({ vertex: "rect2d", fragment: "rect2d" }, ei.attributes, ei.uniforms, [], ei.defines, null, function (e) {
-                    //                    renderCache.setupUniformsLocation(e, ei.uniforms, Shape2D.SHAPE2D_BORDERPARTID);
-                });
+                // Get the instanced version of the effect, if the engine does not support it, null is return and we'll only draw on by one
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], true);
+                if (ei) {
+                    renderCache.effectBorderInstanced = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
+                }
+                // Get the non instanced version
+                ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"], false);
+                renderCache.effectBorder = engine.createEffect("rect2d", ei.attributes, ei.uniforms, [], ei.defines, null);
             }
             }
             return renderCache;
             return renderCache;
         };
         };
@@ -310,6 +393,9 @@ var BABYLON;
             }
             }
             return true;
             return true;
         };
         };
+        Rectangle2D._i0 = BABYLON.Vector2.Zero();
+        Rectangle2D._i1 = BABYLON.Vector2.Zero();
+        Rectangle2D._i2 = BABYLON.Vector2.Zero();
         Rectangle2D.roundSubdivisions = 16;
         Rectangle2D.roundSubdivisions = 16;
         __decorate([
         __decorate([
             BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Rectangle2D.sizeProperty = pi; }, false, true)
             BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Rectangle2D.sizeProperty = pi; }, false, true)
@@ -324,6 +410,6 @@ var BABYLON;
             BABYLON.className("Rectangle2D")
             BABYLON.className("Rectangle2D")
         ], Rectangle2D);
         ], Rectangle2D);
         return Rectangle2D;
         return Rectangle2D;
-    }(BABYLON.Shape2D));
+    })(BABYLON.Shape2D);
     BABYLON.Rectangle2D = Rectangle2D;
     BABYLON.Rectangle2D = Rectangle2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 87 - 3
src/Canvas2d/babylon.rectangle2d.ts

@@ -203,14 +203,80 @@
             this.notRounded = value === 0;
             this.notRounded = value === 0;
         }
         }
 
 
+        private static _i0 = Vector2.Zero();
+        private static _i1 = Vector2.Zero();
+        private static _i2 = Vector2.Zero();
+
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
             // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
             // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
             if (this.notRounded) {
             if (this.notRounded) {
                 return true;
                 return true;
             }
             }
 
 
-            // Well, for now we neglect the area where the pickPosition could be outside due to the roundRadius...
-            // TODO make REAL intersection test here!
+            // If we got so far it means the bounding box at least passed, so we know it's inside the bounding rectangle, but it can be outside the roundedRectangle.
+            // The easiest way is to check if the point is inside on of the four corners area (a little square of roundRadius size at the four corners)
+            // If it's the case for one, check if the mouse is located in the quarter that we care about (the one who is visible) then finally make a distance check with the roundRadius radius to see if it's inside the circle quarter or outside.
+
+            // First let remove the origin out the equation, to have the rectangle with an origin at bottom/left
+            let o = this.origin;
+            let size = this.size;
+            Rectangle2D._i0.x = intersectInfo._localPickPosition.x + (size.width * o.x);
+            Rectangle2D._i0.y = intersectInfo._localPickPosition.y + (size.height * o.y);
+
+            let rr = this.roundRadius;
+            let rrs = rr * rr;
+
+            // Check if the point is in the bottom/left quarter area
+            Rectangle2D._i1.x = rr;
+            Rectangle2D._i1.y = rr;
+            if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+            // Check if the point is in the top/left quarter area
+            Rectangle2D._i1.x = rr;
+            Rectangle2D._i1.y = size.height - rr;
+            if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+            // Check if the point is in the top/right quarter area
+            Rectangle2D._i1.x = size.width - rr;
+            Rectangle2D._i1.y = size.height - rr;
+            if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+
+            // Check if the point is in the bottom/right quarter area
+            Rectangle2D._i1.x = size.width - rr;
+            Rectangle2D._i1.y = rr;
+            if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+            // At any other locations the point is guarantied to be inside
+
             return true;
             return true;
         }
         }
 
 
@@ -254,7 +320,25 @@
                 let size = options.size || (new Size(options.width || 10, options.height || 10));
                 let size = options.size || (new Size(options.width || 10, options.height || 10));
                 let fill = options.fill===undefined ? Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF") : options.fill;
                 let fill = options.fill===undefined ? Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF") : options.fill;
 
 
-                rect.setupRectangle2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.roundRadius || 0, fill, options.border || null, options.borderThickness || 1, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
+                rect.setupRectangle2D
+                (
+                    parent.owner,
+                    parent,
+                    options.id || null,
+                    pos,
+                    options.origin || null,
+                    size,
+                    (options.roundRadius == null) ? 0 : options.roundRadius,
+                    fill,
+                    options.border || null,
+                    (options.borderThickness==null) ? 1 : options.borderThickness,
+                    options.isVisible || true,
+                    options.marginTop || null,
+                    options.marginLeft || null,
+                    options.marginRight || null,
+                    options.marginBottom || null,
+                    options.vAlignment || null,
+                    options.hAlignment || null);
             }
             }
             return rect;
             return rect;
         }
         }

+ 261 - 122
src/Canvas2d/babylon.renderablePrim2d.js

@@ -73,7 +73,7 @@ var BABYLON;
             return curOffset;
             return curOffset;
         };
         };
         return InstanceClassInfo;
         return InstanceClassInfo;
-    }());
+    })();
     BABYLON.InstanceClassInfo = InstanceClassInfo;
     BABYLON.InstanceClassInfo = InstanceClassInfo;
     var InstancePropInfo = (function () {
     var InstancePropInfo = (function () {
         function InstancePropInfo() {
         function InstancePropInfo() {
@@ -187,7 +187,7 @@ var BABYLON;
             }
             }
         };
         };
         return InstancePropInfo;
         return InstancePropInfo;
-    }());
+    })();
     BABYLON.InstancePropInfo = InstancePropInfo;
     BABYLON.InstancePropInfo = InstancePropInfo;
     function instanceData(category, shaderAttributeName) {
     function instanceData(category, shaderAttributeName) {
         return function (target, propName, descriptor) {
         return function (target, propName, descriptor) {
@@ -319,13 +319,23 @@ var BABYLON;
             instanceData()
             instanceData()
         ], InstanceDataBase.prototype, "origin", null);
         ], InstanceDataBase.prototype, "origin", null);
         return InstanceDataBase;
         return InstanceDataBase;
-    }());
+    })();
     BABYLON.InstanceDataBase = InstanceDataBase;
     BABYLON.InstanceDataBase = InstanceDataBase;
     var RenderablePrim2D = (function (_super) {
     var RenderablePrim2D = (function (_super) {
         __extends(RenderablePrim2D, _super);
         __extends(RenderablePrim2D, _super);
         function RenderablePrim2D() {
         function RenderablePrim2D() {
             _super.apply(this, arguments);
             _super.apply(this, arguments);
         }
         }
+        Object.defineProperty(RenderablePrim2D.prototype, "isAlphaTest", {
+            get: function () {
+                return this._isAlphaTest;
+            },
+            set: function (value) {
+                this._isAlphaTest = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RenderablePrim2D.prototype, "isTransparent", {
         Object.defineProperty(RenderablePrim2D.prototype, "isTransparent", {
             get: function () {
             get: function () {
                 return this._isTransparent;
                 return this._isTransparent;
@@ -336,9 +346,11 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        RenderablePrim2D.prototype.setupRenderablePrim2D = function (owner, parent, id, position, origin, isVisible) {
-            this.setupPrim2DBase(owner, parent, id, position, origin);
+        RenderablePrim2D.prototype.setupRenderablePrim2D = function (owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign) {
+            this.setupPrim2DBase(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
             this._isTransparent = false;
             this._isTransparent = false;
+            this._isAlphaTest = false;
+            this._transparentPrimitiveInfo = null;
         };
         };
         RenderablePrim2D.prototype.dispose = function () {
         RenderablePrim2D.prototype.dispose = function () {
             if (!_super.prototype.dispose.call(this)) {
             if (!_super.prototype.dispose.call(this)) {
@@ -361,7 +373,6 @@ var BABYLON;
             return true;
             return true;
         };
         };
         RenderablePrim2D.prototype._prepareRenderPre = function (context) {
         RenderablePrim2D.prototype._prepareRenderPre = function (context) {
-            var _this = this;
             _super.prototype._prepareRenderPre.call(this, context);
             _super.prototype._prepareRenderPre.call(this, context);
             // If the model changed and we have already an instance, we must remove this instance from the obsolete model
             // If the model changed and we have already an instance, we must remove this instance from the obsolete model
             if (this._modelDirty && this._modelRenderInstanceID) {
             if (this._modelDirty && this._modelRenderInstanceID) {
@@ -371,98 +382,15 @@ var BABYLON;
             // Need to create the model?
             // Need to create the model?
             var setupModelRenderCache = false;
             var setupModelRenderCache = false;
             if (!this._modelRenderCache || this._modelDirty) {
             if (!this._modelRenderCache || this._modelDirty) {
-                if (this._modelRenderCache) {
-                    this._modelRenderCache.dispose();
-                }
-                this._modelRenderCache = this.owner._engineData.GetOrAddModelCache(this.modelKey, function (key) {
-                    var mrc = _this.createModelRenderCache(key, _this.isTransparent);
-                    setupModelRenderCache = true;
-                    return mrc;
-                });
-                this._modelDirty = false;
-                // if this is still false it means the MRC already exists, so we add a reference to it
-                if (!setupModelRenderCache) {
-                    this._modelRenderCache.addRef();
-                }
+                setupModelRenderCache = this._createModelRenderCache();
             }
             }
-            var gii;
+            var gii = null;
             var newInstance = false;
             var newInstance = false;
             // Need to create the instance data parts?
             // Need to create the instance data parts?
             if (!this._modelRenderInstanceID) {
             if (!this._modelRenderInstanceID) {
                 // Yes, flag it for later, more processing will have to be done
                 // Yes, flag it for later, more processing will have to be done
                 newInstance = true;
                 newInstance = true;
-                // Create the instance data parts of the primitive and store them
-                var parts = this.createInstanceDataParts();
-                this._instanceDataParts = parts;
-                // Check if the ModelRenderCache for this particular instance is also brand new, initialize it if it's the case
-                if (!this._modelRenderCache._partsDataStride) {
-                    var ctiArray = new Array();
-                    var dataStrides = new Array();
-                    var usedCatList = new Array();
-                    var partIdList = new Array();
-                    var joinedUsedCatList = new Array();
-                    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
-                        var curVisible = this.isVisible;
-                        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(";") + ";";
-                        joinedUsedCatList.push(joinCat);
-                        InstanceClassInfo._CurCategories = joinCat;
-                        var obj = this.beforeRefreshForLayoutConstruction(dataPart);
-                        this.refreshInstanceDataPart(dataPart);
-                        this.afterRefreshForLayoutConstruction(dataPart, obj);
-                        this.isVisible = curVisible;
-                        var size = 0;
-                        cti.fullContent.forEach(function (k, v) {
-                            if (!v.category || cat.indexOf(v.category) !== -1) {
-                                if (!v.size) {
-                                    console.log("ERROR: Couldn't detect the size of the Property " + v.attributeName + " from type " + BABYLON.Tools.getClassName(cti.type) + ". Property is ignored.");
-                                }
-                                else {
-                                    size += v.size;
-                                }
-                            }
-                        });
-                        dataStrides.push(size);
-                        usedCatList.push(cat);
-                        ctiArray.push(cti);
-                        partIdList.push(dataPart.id);
-                    }
-                    this._modelRenderCache._partsDataStride = dataStrides;
-                    this._modelRenderCache._partsUsedCategories = usedCatList;
-                    this._modelRenderCache._partsJoinedUsedCategories = joinedUsedCatList;
-                    this._modelRenderCache._partsClassInfo = ctiArray;
-                    this._modelRenderCache._partIdList = partIdList;
-                }
-                // The Rendering resources (Effect, VB, IB, Textures) are stored in the ModelRenderCache
-                // But it's the RenderGroup that will store all the Instanced related data to render all the primitive it owns.
-                // So for a given ModelKey we getOrAdd a GroupInstanceInfo that will store all these data
-                gii = this.renderGroup._renderGroupInstancesInfo.getOrAddWithFactory(this.modelKey, function (k) { return new BABYLON.GroupInstanceInfo(_this.renderGroup, _this._modelRenderCache); });
-                // First time init of the GroupInstanceInfo
-                if (gii._instancesPartsData.length === 0) {
-                    for (var j = 0; j < this._modelRenderCache._partsDataStride.length; j++) {
-                        var stride = this._modelRenderCache._partsDataStride[j];
-                        gii._instancesPartsData.push(new BABYLON.DynamicFloatArray(stride / 4, 50));
-                        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(";") + ";";
-                        }
-                    }
-                }
-                // For each instance data part of the primitive, allocate the instanced element it needs for render
-                for (var i = 0; i < parts.length; i++) {
-                    var part = parts[i];
-                    part.dataBuffer = gii._instancesPartsData[i];
-                    part.allocElements();
-                }
-                // Add the instance data parts in the ModelRenderCache they belong, track them by storing their ID in the primitive in case we need to change the model later on, so we'll have to release the allocated instance data parts because they won't fit anymore
-                this._modelRenderInstanceID = this._modelRenderCache.addInstanceDataParts(this._instanceDataParts);
+                gii = this._createModelDataParts();
             }
             }
             // If the ModelRenderCache is brand new, now is the time to call the implementation's specific setup method to create the rendering resources
             // If the ModelRenderCache is brand new, now is the time to call the implementation's specific setup method to create the rendering resources
             if (setupModelRenderCache) {
             if (setupModelRenderCache) {
@@ -471,30 +399,225 @@ var BABYLON;
             // At this stage we have everything correctly initialized, ModelRenderCache is setup, Model Instance data are good too, they have allocated elements in the Instanced DynamicFloatArray.
             // At this stage we have everything correctly initialized, ModelRenderCache is setup, Model Instance data are good too, they have allocated elements in the Instanced DynamicFloatArray.
             // The last thing to do is check if the instanced related data must be updated because a InstanceLevel property had changed or the primitive visibility changed.
             // The last thing to do is check if the instanced related data must be updated because a InstanceLevel property had changed or the primitive visibility changed.
             if (this._visibilityChanged || context.forceRefreshPrimitive || newInstance || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep)) {
             if (this._visibilityChanged || context.forceRefreshPrimitive || newInstance || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep)) {
-                // Fetch the GroupInstanceInfo if we don't already have it
-                if (!gii) {
-                    gii = this.renderGroup._renderGroupInstancesInfo.get(this.modelKey);
+                this._updateInstanceDataParts(gii);
+            }
+        };
+        RenderablePrim2D.prototype._createModelRenderCache = function () {
+            var _this = this;
+            var setupModelRenderCache = false;
+            if (this._modelRenderCache) {
+                this._modelRenderCache.dispose();
+            }
+            this._modelRenderCache = this.owner._engineData.GetOrAddModelCache(this.modelKey, function (key) {
+                var mrc = _this.createModelRenderCache(key);
+                setupModelRenderCache = true;
+                return mrc;
+            });
+            this._modelDirty = false;
+            // if this is still false it means the MRC already exists, so we add a reference to it
+            if (!setupModelRenderCache) {
+                this._modelRenderCache.addRef();
+            }
+            return setupModelRenderCache;
+        };
+        RenderablePrim2D.prototype._createModelDataParts = function () {
+            var _this = this;
+            // Create the instance data parts of the primitive and store them
+            var parts = this.createInstanceDataParts();
+            this._instanceDataParts = parts;
+            // Check if the ModelRenderCache for this particular instance is also brand new, initialize it if it's the case
+            if (!this._modelRenderCache._partData) {
+                this._setupModelRenderCache(parts);
+            }
+            // The Rendering resources (Effect, VB, IB, Textures) are stored in the ModelRenderCache
+            // But it's the RenderGroup that will store all the Instanced related data to render all the primitive it owns.
+            // So for a given ModelKey we getOrAdd a GroupInstanceInfo that will store all these data
+            var gii = this.renderGroup._renderableData._renderGroupInstancesInfo.getOrAddWithFactory(this.modelKey, function (k) {
+                var res = new BABYLON.GroupInstanceInfo(_this.renderGroup, _this._modelRenderCache, _this._modelRenderCache._partData.length);
+                for (var j = 0; j < _this._modelRenderCache._partData.length; j++) {
+                    var part = _this._instanceDataParts[j];
+                    res.partIndexFromId.add(part.id.toString(), j);
+                    res.usedShaderCategories[j] = ";" + _this.getUsedShaderCategories(part).join(";") + ";";
+                    res.strides[j] = _this._modelRenderCache._partData[j]._partDataStride;
                 }
                 }
-                // For each Instance Data part, refresh it to update the data in the DynamicFloatArray
-                for (var _c = 0, _d = this._instanceDataParts; _c < _d.length; _c++) {
-                    var part = _d[_c];
-                    // Check if we need to allocate data elements (hidden prim which becomes visible again)
-                    if (this._visibilityChanged && !part.dataElements) {
-                        part.allocElements();
+                return res;
+            });
+            // Get the GroupInfoDataPart corresponding to the render category of the part
+            var gipd = null;
+            if (this.isTransparent) {
+                gipd = gii.transparentData;
+            }
+            else if (this.isAlphaTest) {
+                gipd = gii.alphaTestData;
+            }
+            else {
+                gipd = gii.opaqueData;
+            }
+            // For each instance data part of the primitive, allocate the instanced element it needs for render
+            for (var i = 0; i < parts.length; i++) {
+                var part = parts[i];
+                part.dataBuffer = gipd[i]._partData;
+                part.allocElements();
+            }
+            // Add the instance data parts in the ModelRenderCache they belong, track them by storing their ID in the primitive in case we need to change the model later on, so we'll have to release the allocated instance data parts because they won't fit anymore
+            this._modelRenderInstanceID = this._modelRenderCache.addInstanceDataParts(this._instanceDataParts);
+            return gii;
+        };
+        RenderablePrim2D.prototype._setupModelRenderCache = function (parts) {
+            var ctiArray = new Array();
+            this._modelRenderCache._partData = new Array();
+            for (var _i = 0; _i < parts.length; _i++) {
+                var dataPart = parts[_i];
+                var pd = new BABYLON.ModelRenderCachePartData();
+                this._modelRenderCache._partData.push(pd);
+                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
+                var curVisible = this.isVisible;
+                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(";") + ";";
+                pd._partJoinedUsedCategories = joinCat;
+                InstanceClassInfo._CurCategories = joinCat;
+                var obj = this.beforeRefreshForLayoutConstruction(dataPart);
+                this.refreshInstanceDataPart(dataPart);
+                this.afterRefreshForLayoutConstruction(dataPart, obj);
+                this.isVisible = curVisible;
+                var size = 0;
+                cti.fullContent.forEach(function (k, v) {
+                    if (!v.category || cat.indexOf(v.category) !== -1) {
+                        if (v.attributeName === "zBias") {
+                            pd._zBiasOffset = v.instanceOffset.get(joinCat);
+                        }
+                        if (!v.size) {
+                            console.log("ERROR: Couldn't detect the size of the Property " + v.attributeName + " from type " + BABYLON.Tools.getClassName(cti.type) + ". Property is ignored.");
+                        }
+                        else {
+                            size += v.size;
+                        }
                     }
                     }
-                    InstanceClassInfo._CurCategories = gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())];
-                    // Will return false if the instance should not be rendered (not visible or other any reasons)
-                    if (!this.refreshInstanceDataPart(part)) {
-                        // Free the data element
-                        if (part.dataElements) {
-                            part.freeElements();
+                });
+                pd._partDataStride = size;
+                pd._partUsedCategories = cat;
+                pd._partId = dataPart.id;
+                ctiArray.push(cti);
+            }
+            this._modelRenderCache._partsClassInfo = ctiArray;
+        };
+        RenderablePrim2D.prototype.onZOrderChanged = function () {
+            if (this.isTransparent && this._transparentPrimitiveInfo) {
+                this.renderGroup._renderableData.transparentPrimitiveZChanged(this._transparentPrimitiveInfo);
+                var gii = this.renderGroup._renderableData._renderGroupInstancesInfo.get(this.modelKey);
+                // Flag the transparentData dirty has will have to sort it again
+                gii.transparentOrderDirty = true;
+            }
+        };
+        RenderablePrim2D.prototype._updateInstanceDataParts = function (gii) {
+            // Fetch the GroupInstanceInfo if we don't already have it
+            if (!gii) {
+                gii = this.renderGroup._renderableData._renderGroupInstancesInfo.get(this.modelKey);
+            }
+            // Handle changes related to ZOffset
+            if (this.isTransparent) {
+                // Handle visibility change, which is also triggered when the primitive just got created
+                if (this._visibilityChanged) {
+                    if (this.isVisible) {
+                        if (!this._transparentPrimitiveInfo) {
+                            // Add the primitive to the list of transparent ones in the group that render is
+                            this._transparentPrimitiveInfo = this.renderGroup._renderableData.addNewTransparentPrimitiveInfo(this, gii);
+                        }
+                    }
+                    else {
+                        if (this._transparentPrimitiveInfo) {
+                            this.renderGroup._renderableData.removeTransparentPrimitiveInfo(this._transparentPrimitiveInfo);
                         }
                         }
                     }
                     }
+                    gii.transparentOrderDirty = true;
+                }
+            }
+            // For each Instance Data part, refresh it to update the data in the DynamicFloatArray
+            for (var _i = 0, _a = this._instanceDataParts; _i < _a.length; _i++) {
+                var part = _a[_i];
+                // Check if we need to allocate data elements (hidden prim which becomes visible again)
+                if (this._visibilityChanged && !part.dataElements) {
+                    part.allocElements();
+                }
+                InstanceClassInfo._CurCategories = gii.usedShaderCategories[gii.partIndexFromId.get(part.id.toString())];
+                // Will return false if the instance should not be rendered (not visible or other any reasons)
+                if (!this.refreshInstanceDataPart(part)) {
+                    // Free the data element
+                    if (part.dataElements) {
+                        part.freeElements();
+                    }
+                }
+            }
+            this._instanceDirtyFlags = 0;
+            // Make the appropriate data dirty
+            if (this.isTransparent) {
+                gii.transparentDirty = true;
+            }
+            else if (this.isAlphaTest) {
+                gii.alphaTestDirty = true;
+            }
+            else {
+                gii.opaqueDirty = true;
+            }
+            this._visibilityChanged = false; // Reset the flag as we've handled the case            
+        };
+        RenderablePrim2D.prototype._getFirstIndexInDataBuffer = function () {
+            for (var _i = 0, _a = this._instanceDataParts; _i < _a.length; _i++) {
+                var part = _a[_i];
+                if (part) {
+                    return part.dataElements[0].offset / part.dataBuffer.stride;
+                }
+            }
+            return null;
+        };
+        RenderablePrim2D.prototype._getLastIndexInDataBuffer = function () {
+            for (var _i = 0, _a = this._instanceDataParts; _i < _a.length; _i++) {
+                var part = _a[_i];
+                if (part) {
+                    return part.dataElements[part.dataElements.length - 1].offset / part.dataBuffer.stride;
+                }
+            }
+            return null;
+        };
+        // This internal method is mainly used for transparency processing
+        RenderablePrim2D.prototype._getNextPrimZOrder = function () {
+            var length = this._instanceDataParts.length;
+            for (var i = 0; i < length; i++) {
+                var part = this._instanceDataParts[i];
+                if (part) {
+                    var stride = part.dataBuffer.stride;
+                    var lastElementOffset = part.dataElements[part.dataElements.length - 1].offset;
+                    // check if it's the last in the DFA
+                    if (part.dataBuffer.totalElementCount * stride <= lastElementOffset) {
+                        return null;
+                    }
+                    // Return the Z of the next primitive that lies in the DFA
+                    return part.dataBuffer[lastElementOffset + stride + this.modelRenderCache._partData[i]._zBiasOffset];
                 }
                 }
-                this._instanceDirtyFlags = 0;
-                gii._dirtyInstancesData = true;
-                this._visibilityChanged = false; // Reset the flag as we've handled the case
             }
             }
+            return null;
+        };
+        // This internal method is mainly used for transparency processing
+        RenderablePrim2D.prototype._getPrevPrimZOrder = function () {
+            var length = this._instanceDataParts.length;
+            for (var i = 0; i < length; i++) {
+                var part = this._instanceDataParts[i];
+                if (part) {
+                    var stride = part.dataBuffer.stride;
+                    var firstElementOffset = part.dataElements[0].offset;
+                    // check if it's the first in the DFA
+                    if (firstElementOffset === 0) {
+                        return null;
+                    }
+                    // Return the Z of the previous primitive that lies in the DFA
+                    return part.dataBuffer[firstElementOffset - stride + this.modelRenderCache._partData[i]._zBiasOffset];
+                }
+            }
+            return null;
         };
         };
         /**
         /**
          * Transform a given point using the Primitive's origin setting.
          * Transform a given point using the Primitive's origin setting.
@@ -508,17 +631,31 @@ var BABYLON;
             res.x = p.x - ((this.origin.x + (originOffset ? originOffset.x : 0)) * actualSize.width);
             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);
             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);
+        RenderablePrim2D.prototype.transformPointWithOriginToRef = function (p, originOffset, res) {
             this.transformPointWithOriginByRef(p, originOffset, res);
             this.transformPointWithOriginByRef(p, originOffset, res);
             return res;
             return res;
         };
         };
-        RenderablePrim2D.prototype.getDataPartEffectInfo = function (dataPartId, vertexBufferAttributes) {
+        /**
+         * Get the info for a given effect based on the dataPart metadata
+         * @param dataPartId partId in part list to get the info
+         * @param vertexBufferAttributes vertex buffer attributes to manually add
+         * @param useInstanced specified if Instanced Array should be used, if null the engine caps will be used (so true if WebGL supports it, false otherwise), but you have the possibility to override the engine capability. However, if you manually set true but the engine does not support Instanced Array, this method will return null
+         */
+        RenderablePrim2D.prototype.getDataPartEffectInfo = function (dataPartId, vertexBufferAttributes, useInstanced) {
+            if (useInstanced === void 0) { useInstanced = null; }
             var dataPart = BABYLON.Tools.first(this._instanceDataParts, function (i) { return i.id === dataPartId; });
             var dataPart = BABYLON.Tools.first(this._instanceDataParts, function (i) { return i.id === dataPartId; });
             if (!dataPart) {
             if (!dataPart) {
                 return null;
                 return null;
             }
             }
             var instancedArray = this.owner.supportInstancedArray;
             var instancedArray = this.owner.supportInstancedArray;
+            if (useInstanced != null) {
+                // Check if the caller ask for Instanced Array and the engine does not support it, return null if it's the case
+                if (useInstanced && instancedArray === false) {
+                    return null;
+                }
+                // Use the caller's setting
+                instancedArray = useInstanced;
+            }
             var cti = dataPart.getClassTreeInfo();
             var cti = dataPart.getClassTreeInfo();
             var categories = this.getUsedShaderCategories(dataPart);
             var categories = this.getUsedShaderCategories(dataPart);
             var att = cti.classContent.getShaderAttributes(categories);
             var att = cti.classContent.getShaderAttributes(categories);
@@ -536,7 +673,7 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        RenderablePrim2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
+        RenderablePrim2D.prototype.createModelRenderCache = function (modelKey) {
             return null;
             return null;
         };
         };
         RenderablePrim2D.prototype.setupModelRenderCache = function (modelRenderCache) {
         RenderablePrim2D.prototype.setupModelRenderCache = function (modelRenderCache) {
@@ -584,15 +721,14 @@ var BABYLON;
             }
             }
             // Have to convert the coordinates to clip space which is ranged between [-1;1] on X and Y axis, with 0,0 being the left/bottom corner
             // Have to convert the coordinates to clip space which is ranged between [-1;1] on X and Y axis, with 0,0 being the left/bottom corner
             // Current coordinates are expressed in renderGroup coordinates ([0, renderGroup.actualSize.width|height]) with 0,0 being at the left/top corner
             // Current coordinates are expressed in renderGroup coordinates ([0, renderGroup.actualSize.width|height]) with 0,0 being at the left/top corner
-            // RenderGroup Width and Height are multiplied by zBias because the VertexShader will multiply X and Y by W, which is 1/zBias. As we divide our coordinate by these Width/Height, we will also divide by the zBias to compensate the operation made by the VertexShader.
             // So for X: 
             // So for X: 
             //  - tx.x = value * 2 / width: is to switch from [0, renderGroup.width] to [0, 2]
             //  - tx.x = value * 2 / width: is to switch from [0, renderGroup.width] to [0, 2]
-            //  - tx.w = (value * 2 / width) - 1: w stores the translation in renderGroup coordinates so (value * 2 / width) to switch to a clip space translation value. - 1 is to offset the overall [0;2] to [-1;1]. Don't forget it's -(1/zBias) and not -1 because everything need to be scaled by 1/zBias.
-            var w = size.width * zBias;
-            var h = size.height * zBias;
+            //  - tx.w = (value * 2 / width) - 1: w stores the translation in renderGroup coordinates so (value * 2 / width) to switch to a clip space translation value. - 1 is to offset the overall [0;2] to [-1;1].
+            var w = size.width;
+            var h = size.height;
             var invZBias = 1 / zBias;
             var invZBias = 1 / zBias;
-            var tx = new BABYLON.Vector4(t.m[0] * 2 / w, t.m[4] * 2 / w, 0 /*t.m[8]*/, ((t.m[12] + offX) * 2 / w) - (invZBias));
-            var ty = new BABYLON.Vector4(t.m[1] * 2 / h, t.m[5] * 2 / h, 0 /*t.m[9]*/, ((t.m[13] + offY) * 2 / h) - (invZBias));
+            var tx = new BABYLON.Vector4(t.m[0] * 2 / w, t.m[4] * 2 / w, 0 /*t.m[8]*/, ((t.m[12] + offX) * 2 / w) - 1);
+            var ty = new BABYLON.Vector4(t.m[1] * 2 / h, t.m[5] * 2 / h, 0 /*t.m[9]*/, ((t.m[13] + offY) * 2 / h) - 1);
             part.transformX = tx;
             part.transformX = tx;
             part.transformY = ty;
             part.transformY = ty;
             part.origin = this.origin;
             part.origin = this.origin;
@@ -601,12 +737,15 @@ var BABYLON;
         };
         };
         RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT = BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;
         RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT = BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 5;
         __decorate([
         __decorate([
-            BABYLON.modelLevelProperty(BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, function (pi) { return RenderablePrim2D.isTransparentProperty = pi; })
+            BABYLON.dynamicLevelProperty(BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 0, function (pi) { return RenderablePrim2D.isAlphaTestProperty = pi; })
+        ], RenderablePrim2D.prototype, "isAlphaTest", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, function (pi) { return RenderablePrim2D.isTransparentProperty = pi; })
         ], RenderablePrim2D.prototype, "isTransparent", null);
         ], RenderablePrim2D.prototype, "isTransparent", null);
         RenderablePrim2D = __decorate([
         RenderablePrim2D = __decorate([
             BABYLON.className("RenderablePrim2D")
             BABYLON.className("RenderablePrim2D")
         ], RenderablePrim2D);
         ], RenderablePrim2D);
         return RenderablePrim2D;
         return RenderablePrim2D;
-    }(BABYLON.Prim2DBase));
+    })(BABYLON.Prim2DBase);
     BABYLON.RenderablePrim2D = RenderablePrim2D;
     BABYLON.RenderablePrim2D = RenderablePrim2D;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

+ 4 - 5
src/Canvas2d/babylon.renderablePrim2d.ts

@@ -385,7 +385,7 @@
             return true;
             return true;
         }
         }
 
 
-        public _prepareRenderPre(context: PreapreRender2DContext) {
+        public _prepareRenderPre(context: PrepareRender2DContext) {
             super._prepareRenderPre(context);
             super._prepareRenderPre(context);
 
 
             // If the model changed and we have already an instance, we must remove this instance from the obsolete model
             // If the model changed and we have already an instance, we must remove this instance from the obsolete model
@@ -498,7 +498,7 @@
             let ctiArray = new Array<ClassTreeInfo<InstanceClassInfo, InstancePropInfo>>();
             let ctiArray = new Array<ClassTreeInfo<InstanceClassInfo, InstancePropInfo>>();
             this._modelRenderCache._partData = new Array<ModelRenderCachePartData>();
             this._modelRenderCache._partData = new Array<ModelRenderCachePartData>();
             for (let dataPart of parts) {
             for (let dataPart of parts) {
-                let pd = new ModelRenderCachePartData();
+                var pd = new ModelRenderCachePartData();
                 this._modelRenderCache._partData.push(pd)
                 this._modelRenderCache._partData.push(pd)
                 var cat = this.getUsedShaderCategories(dataPart);
                 var cat = this.getUsedShaderCategories(dataPart);
                 var cti = dataPart.getClassTreeInfo();
                 var cti = dataPart.getClassTreeInfo();
@@ -507,7 +507,7 @@
                 this.isVisible = true;
                 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.
                 // 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]));
                 //console.log("Build Prop Layout for " + Tools.getClassName(this._instanceDataParts[0]));
-                let joinCat = ";" + cat.join(";") + ";";
+                var joinCat = ";" + cat.join(";") + ";";
                 pd._partJoinedUsedCategories = joinCat;
                 pd._partJoinedUsedCategories = joinCat;
                 InstanceClassInfo._CurCategories = joinCat;
                 InstanceClassInfo._CurCategories = joinCat;
                 let obj = this.beforeRefreshForLayoutConstruction(dataPart);
                 let obj = this.beforeRefreshForLayoutConstruction(dataPart);
@@ -674,8 +674,7 @@
             res.y = p.y - ((this.origin.y + (originOffset ? originOffset.y : 0)) * actualSize.height);
             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);
+        protected transformPointWithOriginToRef(p: Vector2, originOffset: Vector2, res: Vector2) {
             this.transformPointWithOriginByRef(p, originOffset, res);
             this.transformPointWithOriginByRef(p, originOffset, res);
             return res;
             return res;
         }
         }

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

@@ -48,9 +48,8 @@ var BABYLON;
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        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, origin, isVisible);
+        Shape2D.prototype.setupShape2D = function (owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, vAlignment, hAlignment) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment || BABYLON.Prim2DBase.HAlignLeft, vAlignment || BABYLON.Prim2DBase.VAlignTop);
             this.border = border;
             this.border = border;
             this.fill = fill;
             this.fill = fill;
             this.borderThickness = borderThickness;
             this.borderThickness = borderThickness;
@@ -144,7 +143,7 @@ var BABYLON;
             BABYLON.className("Shape2D")
             BABYLON.className("Shape2D")
         ], Shape2D);
         ], Shape2D);
         return Shape2D;
         return Shape2D;
-    }(BABYLON.RenderablePrim2D));
+    })(BABYLON.RenderablePrim2D);
     BABYLON.Shape2D = Shape2D;
     BABYLON.Shape2D = Shape2D;
     var Shape2DInstanceData = (function (_super) {
     var Shape2DInstanceData = (function (_super) {
         __extends(Shape2DInstanceData, _super);
         __extends(Shape2DInstanceData, _super);
@@ -244,6 +243,6 @@ var BABYLON;
             BABYLON.instanceData(Shape2D.SHAPE2D_CATEGORY_BORDERGRADIENT)
             BABYLON.instanceData(Shape2D.SHAPE2D_CATEGORY_BORDERGRADIENT)
         ], Shape2DInstanceData.prototype, "borderGradientTY", null);
         ], Shape2DInstanceData.prototype, "borderGradientTY", null);
         return Shape2DInstanceData;
         return Shape2DInstanceData;
-    }(BABYLON.InstanceDataBase));
+    })(BABYLON.InstanceDataBase);
     BABYLON.Shape2DInstanceData = Shape2DInstanceData;
     BABYLON.Shape2DInstanceData = Shape2DInstanceData;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

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

@@ -10,7 +10,7 @@ var BABYLON;
         function Prim2DClassInfo() {
         function Prim2DClassInfo() {
         }
         }
         return Prim2DClassInfo;
         return Prim2DClassInfo;
-    }());
+    })();
     BABYLON.Prim2DClassInfo = Prim2DClassInfo;
     BABYLON.Prim2DClassInfo = Prim2DClassInfo;
     var Prim2DPropInfo = (function () {
     var Prim2DPropInfo = (function () {
         function Prim2DPropInfo() {
         function Prim2DPropInfo() {
@@ -19,13 +19,13 @@ var BABYLON;
         Prim2DPropInfo.PROPKIND_INSTANCE = 2;
         Prim2DPropInfo.PROPKIND_INSTANCE = 2;
         Prim2DPropInfo.PROPKIND_DYNAMIC = 3;
         Prim2DPropInfo.PROPKIND_DYNAMIC = 3;
         return Prim2DPropInfo;
         return Prim2DPropInfo;
-    }());
+    })();
     BABYLON.Prim2DPropInfo = Prim2DPropInfo;
     BABYLON.Prim2DPropInfo = Prim2DPropInfo;
     var PropertyChangedInfo = (function () {
     var PropertyChangedInfo = (function () {
         function PropertyChangedInfo() {
         function PropertyChangedInfo() {
         }
         }
         return PropertyChangedInfo;
         return PropertyChangedInfo;
-    }());
+    })();
     BABYLON.PropertyChangedInfo = PropertyChangedInfo;
     BABYLON.PropertyChangedInfo = PropertyChangedInfo;
     var ClassTreeInfo = (function () {
     var ClassTreeInfo = (function () {
         function ClassTreeInfo(baseClass, type, classContentFactory) {
         function ClassTreeInfo(baseClass, type, classContentFactory) {
@@ -62,13 +62,13 @@ var BABYLON;
         Object.defineProperty(ClassTreeInfo.prototype, "fullContent", {
         Object.defineProperty(ClassTreeInfo.prototype, "fullContent", {
             get: function () {
             get: function () {
                 if (!this._fullContent) {
                 if (!this._fullContent) {
-                    var dic_1 = new BABYLON.StringDictionary();
+                    var dic = new BABYLON.StringDictionary();
                     var curLevel = this;
                     var curLevel = this;
                     while (curLevel) {
                     while (curLevel) {
-                        curLevel.levelContent.forEach(function (k, v) { return dic_1.add(k, v); });
+                        curLevel.levelContent.forEach(function (k, v) { return dic.add(k, v); });
                         curLevel = curLevel._baseClass;
                         curLevel = curLevel._baseClass;
                     }
                     }
-                    this._fullContent = dic_1;
+                    this._fullContent = dic;
                 }
                 }
                 return this._fullContent;
                 return this._fullContent;
             },
             },
@@ -128,7 +128,7 @@ var BABYLON;
             return dic;
             return dic;
         };
         };
         return ClassTreeInfo;
         return ClassTreeInfo;
-    }());
+    })();
     BABYLON.ClassTreeInfo = ClassTreeInfo;
     BABYLON.ClassTreeInfo = ClassTreeInfo;
     var SmartPropertyPrim = (function () {
     var SmartPropertyPrim = (function () {
         function SmartPropertyPrim() {
         function SmartPropertyPrim() {
@@ -278,6 +278,9 @@ var BABYLON;
             return false;
             return false;
         };
         };
         SmartPropertyPrim.prototype.markAsDirty = function (propertyName) {
         SmartPropertyPrim.prototype.markAsDirty = function (propertyName) {
+            if (this.isDisposed) {
+                return;
+            }
             var i = propertyName.indexOf(".");
             var i = propertyName.indexOf(".");
             if (i !== -1) {
             if (i !== -1) {
                 propertyName = propertyName.substr(0, i);
                 propertyName = propertyName.substr(0, i);
@@ -419,7 +422,7 @@ var BABYLON;
             BABYLON.className("SmartPropertyPrim")
             BABYLON.className("SmartPropertyPrim")
         ], SmartPropertyPrim);
         ], SmartPropertyPrim);
         return SmartPropertyPrim;
         return SmartPropertyPrim;
-    }());
+    })();
     BABYLON.SmartPropertyPrim = SmartPropertyPrim;
     BABYLON.SmartPropertyPrim = SmartPropertyPrim;
     function modelLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo) {
     function modelLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo) {
         if (typeLevelCompare === void 0) { typeLevelCompare = false; }
         if (typeLevelCompare === void 0) { typeLevelCompare = false; }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 82 - 44
src/Canvas2d/babylon.sprite2d.js


+ 49 - 14
src/Canvas2d/babylon.sprite2d.ts

@@ -11,7 +11,7 @@
         render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
         render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
             // Do nothing if the shader is still loading/preparing 
             // Do nothing if the shader is still loading/preparing 
             if (!this.effectsReady) {
             if (!this.effectsReady) {
-                if ((!this.effect.isReady() || (this.effectInstanced && !this.effectInstanced.isReady()))) {
+                if ((this.effect && (!this.effect.isReady() || (this.effectInstanced && !this.effectInstanced.isReady())))) {
                     return false;
                     return false;
                 }
                 }
                 this.effectsReady = true;
                 this.effectsReady = true;
@@ -106,13 +106,12 @@
             return null;
             return null;
         }
         }
 
 
+        // 3 floats being:
+        // - x: frame number to display
+        // - y: invertY setting
+        // - z: alignToPixel setting
         @instanceData()
         @instanceData()
-        get frame(): number {
-            return null;
-        }
-
-        @instanceData()
-        get invertY(): number {
+        get properties(): Vector3 {
             return null;
             return null;
         }
         }
     }
     }
@@ -126,6 +125,7 @@
         public static spriteLocationProperty: Prim2DPropInfo;
         public static spriteLocationProperty: Prim2DPropInfo;
         public static spriteFrameProperty: Prim2DPropInfo;
         public static spriteFrameProperty: Prim2DPropInfo;
         public static invertYProperty: Prim2DPropInfo;
         public static invertYProperty: Prim2DPropInfo;
+        public static alignToPixelProperty: Prim2DPropInfo;
 
 
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Sprite2D.textureProperty = pi)
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Sprite2D.textureProperty = pi)
         public get texture(): Texture {
         public get texture(): Texture {
@@ -176,6 +176,14 @@
             this._invertY = value;
             this._invertY = value;
         }
         }
 
 
+        public get alignToPixel(): boolean {
+            return this._alignToPixel;
+        }
+
+        public set alignToPixel(value: boolean) {
+            this._alignToPixel = value;
+        }
+
         protected updateLevelBoundingInfo() {
         protected updateLevelBoundingInfo() {
             BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo, this.origin);
             BoundingInfo2D.CreateFromSizeToRef(this.spriteSize, this._levelBoundingInfo, this.origin);
         }
         }
@@ -194,7 +202,7 @@
             return true;
             return true;
         }
         }
 
 
-        protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+        protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean, alignToPixel: boolean, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
             this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.texture = texture;
             this.texture = texture;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
@@ -203,6 +211,7 @@
             this.spriteLocation = spriteLocation || new Vector2(0,0);
             this.spriteLocation = spriteLocation || new Vector2(0,0);
             this.spriteFrame = 0;
             this.spriteFrame = 0;
             this.invertY = invertY;
             this.invertY = invertY;
+            this.alignToPixel = alignToPixel;
             this._isTransparent = true;
             this._isTransparent = true;
 
 
             if (!this.spriteSize) {
             if (!this.spriteSize) {
@@ -222,20 +231,39 @@
          *  - spriteSize: the size of the sprite, if null the size of the given texture will be used, default is null.
          *  - 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)
          *  - 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.
          *  - invertY: if true the texture Y will be inverted, default is false.
+         *  - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
          *  - isVisible: true if the sprite must be visible, false for hidden. Default is true.
          *  - isVisible: true if the sprite must be visible, false for hidden. Default is true.
          *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
          *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
          *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
          */
-        public static Create(parent: Prim2DBase, texture: Texture, options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, spriteSize?: Size, spriteLocation?: Vector2, invertY?: boolean, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number}): Sprite2D {
+        public static Create(parent: Prim2DBase, texture: Texture, options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, spriteSize?: Size, spriteLocation?: Vector2, invertY?: boolean, alignToPixel?: boolean, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number}): Sprite2D {
             Prim2DBase.CheckParent(parent);
             Prim2DBase.CheckParent(parent);
 
 
             let sprite = new Sprite2D();
             let sprite = new Sprite2D();
             if (!options) {
             if (!options) {
-                sprite.setupSprite2D(parent.owner, parent, null, Vector2.Zero(), null, texture, null, null, false, true, null, null, null, null, null, null);
+                sprite.setupSprite2D(parent.owner, parent, null, Vector2.Zero(), null, texture, null, null, false, true, true, null, null, null, null, null, null);
             } else {
             } else {
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
-                sprite.setupSprite2D(parent.owner, parent, options.id || null, pos, options.origin || null, texture, options.spriteSize || null, options.spriteLocation || null, options.invertY || false, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
+                sprite.setupSprite2D
+                (
+                    parent.owner,
+                    parent,
+                    options.id || null,
+                    pos,
+                    options.origin || null,
+                    texture, options.spriteSize || null,
+                    options.spriteLocation || null,
+                    (options.invertY == null) ? false : options.invertY,
+                    (options.alignToPixel == null) ? true : options.alignToPixel,
+                    (options.isVisible == null) ? true : options.isVisible,
+                    options.marginTop || null,
+                    options.marginLeft || null,
+                    options.marginRight || null,
+                    options.marginBottom || null,
+                    options.vAlignment || null,
+                    options.hAlignment || null
+                );
             }
             }
 
 
             return sprite;
             return sprite;
@@ -244,7 +272,7 @@
         static _createCachedCanvasSprite(owner: Canvas2D, texture: MapTexture, size: Size, pos: Vector2): Sprite2D {
         static _createCachedCanvasSprite(owner: Canvas2D, texture: MapTexture, size: Size, pos: Vector2): Sprite2D {
 
 
             let sprite = new Sprite2D();
             let sprite = new Sprite2D();
-            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new Vector2(0, 0), null, texture, size, pos, false, true, null, null, null, null, null, null);
+            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new Vector2(0, 0), null, texture, size, pos, false, true, true, null, null, null, null, null, null);
 
 
             return sprite;
             return sprite;
         }
         }
@@ -292,6 +320,8 @@
             return [new Sprite2DInstanceData(Sprite2D.SPRITE2D_MAINPARTID)];
             return [new Sprite2DInstanceData(Sprite2D.SPRITE2D_MAINPARTID)];
         }
         }
 
 
+        private static _prop: Vector3 = Vector3.Zero();
+
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
             if (!super.refreshInstanceDataPart(part)) {
             if (!super.refreshInstanceDataPart(part)) {
                 return false;
                 return false;
@@ -305,9 +335,13 @@
                 d.topLeftUV = new Vector2(sl.x / ts.width, sl.y / ts.height);
                 d.topLeftUV = new Vector2(sl.x / ts.width, sl.y / ts.height);
                 let suv = new Vector2(ss.width / ts.width, ss.height / ts.height);
                 let suv = new Vector2(ss.width / ts.width, ss.height / ts.height);
                 d.sizeUV = suv;
                 d.sizeUV = suv;
-                d.frame = this.spriteFrame;
+
+                Sprite2D._prop.x = this.spriteFrame;
+                Sprite2D._prop.y = this.invertY ? 1 : 0;
+                Sprite2D._prop.z = this.alignToPixel ? 1 : 0;
+                d.properties = Sprite2D._prop;
+
                 d.textureSize = new Vector2(ts.width, ts.height);
                 d.textureSize = new Vector2(ts.width, ts.height);
-                d.invertY = this.invertY ? 1 : 0;
             }
             }
             return true;
             return true;
         }
         }
@@ -317,6 +351,7 @@
         private _location: Vector2;
         private _location: Vector2;
         private _spriteFrame: number;
         private _spriteFrame: number;
         private _invertY: boolean;
         private _invertY: boolean;
+        private _alignToPixel: boolean;
     }
     }
 
 
 
 

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 60 - 69
src/Canvas2d/babylon.text2d.js


+ 20 - 2
src/Canvas2d/babylon.text2d.ts

@@ -11,7 +11,7 @@
         render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
         render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
             // Do nothing if the shader is still loading/preparing 
             // Do nothing if the shader is still loading/preparing 
             if (!this.effectsReady) {
             if (!this.effectsReady) {
-                if ((!this.effect.isReady() || (this.effectInstanced && !this.effectInstanced.isReady()))) {
+                if ((this.effect && (!this.effect.isReady() || (this.effectInstanced && !this.effectInstanced.isReady())))) {
                     return false;
                     return false;
                 }
                 }
                 this.effectsReady = true;
                 this.effectsReady = true;
@@ -239,7 +239,25 @@
                 text2d.setupText2D(parent.owner, parent, null, Vector2.Zero(), null, "12pt Arial", text, null, new Color4(1,1,1,1), 4, true, null, null, null, null, null, null);
                 text2d.setupText2D(parent.owner, parent, null, Vector2.Zero(), null, "12pt Arial", text, null, new Color4(1,1,1,1), 4, true, null, null, null, null, null, null);
             } else {
             } else {
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
                 let pos = options.position || new Vector2(options.x || 0, options.y || 0);
-                text2d.setupText2D(parent.owner, parent, options.id || null, pos, options.origin || null, options.fontName || "12pt Arial", text, options.areaSize, options.defaultFontColor || new Color4(1, 1, 1, 1), options.tabulationSize || 4, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
+                text2d.setupText2D
+                (
+                    parent.owner,
+                    parent,
+                    options.id || null,
+                    pos,
+                    options.origin || null,
+                    options.fontName || "12pt Arial",
+                    text,
+                    options.areaSize,
+                    options.defaultFontColor || new Color4(1, 1, 1, 1),
+                    (options.tabulationSize==null) ? 4 : options.tabulationSize,
+                    (options.isVisible==null) ? true : options.isVisible,
+                    options.marginTop || null,
+                    options.marginLeft || null,
+                    options.marginRight || null,
+                    options.marginBottom || null,
+                    options.vAlignment || null,
+                    options.hAlignment || null);
             }
             }
             return text2d;
             return text2d;
         }
         }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -46,6 +46,6 @@ var BABYLON;
             BABYLON.serializeAsVector3()
             BABYLON.serializeAsVector3()
         ], HemisphericLight.prototype, "direction", void 0);
         ], HemisphericLight.prototype, "direction", void 0);
         return HemisphericLight;
         return HemisphericLight;
-    }(BABYLON.Light));
+    })(BABYLON.Light);
     BABYLON.HemisphericLight = HemisphericLight;
     BABYLON.HemisphericLight = HemisphericLight;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

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

@@ -189,6 +189,6 @@ var BABYLON;
             BABYLON.serialize()
             BABYLON.serialize()
         ], Light.prototype, "radius", void 0);
         ], Light.prototype, "radius", void 0);
         return Light;
         return Light;
-    }(BABYLON.Node));
+    })(BABYLON.Node);
     BABYLON.Light = Light;
     BABYLON.Light = Light;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

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

@@ -82,6 +82,6 @@ var BABYLON;
             BABYLON.serializeAsVector3()
             BABYLON.serializeAsVector3()
         ], PointLight.prototype, "position", void 0);
         ], PointLight.prototype, "position", void 0);
         return PointLight;
         return PointLight;
-    }(BABYLON.Light));
+    })(BABYLON.Light);
     BABYLON.PointLight = PointLight;
     BABYLON.PointLight = PointLight;
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));

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


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott