Quellcode durchsuchen

Merge remote-tracking branch 'BabylonJS/master'

MackeyK24 vor 8 Jahren
Ursprung
Commit
06251de468
71 geänderte Dateien mit 7896 neuen und 2230 gelöschten Zeilen
  1. 5 4
      Playground/debug.html
  2. 2 1
      Playground/frame.html
  3. 2 1
      Playground/index-local.html
  4. 2 1
      Playground/index.html
  5. 3 0
      Tools/Gulp/config.json
  6. 1 0
      Tools/Npm/package.json
  7. 49 0
      canvas2D/features.md
  8. 65 11
      canvas2D/readme.md
  9. 80 0
      canvas2D/src/Engine/babylon.bounding2d.ts
  10. 154 90
      canvas2D/src/Engine/babylon.canvas2d.ts
  11. 472 4
      canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts
  12. 39 4
      canvas2D/src/Engine/babylon.ellipse2d.ts
  13. 2 0
      canvas2D/src/Engine/babylon.fontTexture.ts
  14. 39 2
      canvas2D/src/Engine/babylon.group2d.ts
  15. 90 80
      canvas2D/src/Engine/babylon.lines2d.ts
  16. 4 0
      canvas2D/src/Engine/babylon.modelRenderCache.ts
  17. 180 29
      canvas2D/src/Engine/babylon.prim2dBase.ts
  18. 730 0
      canvas2D/src/Engine/babylon.primitiveCollisionManager.ts
  19. 85 2
      canvas2D/src/Engine/babylon.rectangle2d.ts
  20. 5 0
      canvas2D/src/Engine/babylon.renderablePrim2d.ts
  21. 26 2
      canvas2D/src/Engine/babylon.smartPropertyPrim.ts
  22. 24 6
      canvas2D/src/Engine/babylon.sprite2d.ts
  23. 18 7
      canvas2D/src/Engine/babylon.text2d.ts
  24. 483 0
      canvas2D/src/Engine/babylon.wireFrame2d.ts
  25. 278 0
      canvas2D/src/Tools/babylon.math2D.ts
  26. 8 6
      canvas2D/src/Tools/babylon.observableStringDictionary.ts
  27. 1 1
      canvas2D/src/shaders/text2d.fragment.fx
  28. 5 0
      canvas2D/src/shaders/wireframe2d.fragment.fx
  29. 40 0
      canvas2D/src/shaders/wireframe2d.vertex.fx
  30. 26 26
      dist/preview release/babylon.core.js
  31. 963 832
      dist/preview release/babylon.d.ts
  32. 36 36
      dist/preview release/babylon.js
  33. 529 353
      dist/preview release/babylon.max.js
  34. 35 35
      dist/preview release/babylon.noworker.js
  35. 449 13
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  36. 2366 238
      dist/preview release/canvas2D/babylon.canvas2d.js
  37. 12 10
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  38. 3 3
      dist/preview release/inspector/babylon.inspector.bundle.js
  39. 6 9
      dist/preview release/inspector/babylon.inspector.js
  40. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  41. 1 1
      dist/preview release/proceduralTexturesLibrary/babylon.brickProceduralTexture.js
  42. 1 1
      dist/preview release/proceduralTexturesLibrary/babylon.brickProceduralTexture.min.js
  43. 1 1
      dist/preview release/proceduralTexturesLibrary/babylon.marbleProceduralTexture.js
  44. 1 1
      dist/preview release/proceduralTexturesLibrary/babylon.marbleProceduralTexture.min.js
  45. 2 1
      inspector/src/adapters/Canvas2DAdapter.ts
  46. 1 1
      inspector/src/helpers/Helpers.ts
  47. 5 8
      inspector/src/tabs/ConsoleTab.ts
  48. 31 35
      inspector/test/index.js
  49. 3 3
      proceduralTexturesLibrary/src/brick/brickProceduralTexture.fragment.fx
  50. 3 3
      proceduralTexturesLibrary/src/marble/marbleProceduralTexture.fragment.fx
  51. 2 4
      src/Animations/babylon.animatable.ts
  52. 2 2
      src/Cameras/babylon.arcRotateCamera.ts
  53. 1 1
      src/Cameras/babylon.camera.ts
  54. 5 4
      src/Cameras/babylon.followCamera.ts
  55. 1 1
      src/Collisions/babylon.collider.ts
  56. 2 2
      src/Collisions/babylon.collisionCoordinator.ts
  57. 6 3
      src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts
  58. 4 3
      src/Math/babylon.math.ts
  59. 449 10
      src/Mesh/babylon.abstractMesh.ts
  60. 4 4
      src/Mesh/babylon.geometry.ts
  61. 1 1
      src/Mesh/babylon.instancedMesh.ts
  62. 4 301
      src/Mesh/babylon.mesh.ts
  63. 9 6
      src/Mesh/babylon.mesh.vertexData.ts
  64. 1 1
      src/Mesh/babylon.meshBuilder.ts
  65. 2 2
      src/Mesh/babylon.subMesh.ts
  66. 1 1
      src/Physics/babylon.physicsImpostor.ts
  67. 1 3
      src/Tools/babylon.tools.ts
  68. 7 7
      src/Tools/babylon.virtualJoystick.ts
  69. 21 10
      src/babylon.engine.ts
  70. 2 0
      tests/Canvas2d/Jasmine/chutzpah.json
  71. 2 0
      tests/Tools/Jasmine/chutzpah.json

+ 5 - 4
Playground/debug.html

@@ -2,14 +2,15 @@
 <html>
 <html>
 <head>
 <head>
     <title>Babylon.js Playground</title>
     <title>Babylon.js Playground</title>
+    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/poly2tri.js"></script>    
     <script src="https://babylonjs.azurewebsites.net/poly2tri.js"></script>    
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.js"></script>
     <!--x-tag-->
     <!--x-tag-->
-    <script src="xtag.js"></script>
+    <script src="xtag.min.js"></script>
     <script src="splitbox.js"></script>
     <script src="splitbox.js"></script>
     <link href="splitbox.css" rel="stylesheet" />
     <link href="splitbox.css" rel="stylesheet" />
     <!-- jszip -->
     <!-- jszip -->
-    <script src="jszip.js"></script>
+    <script src="jszip.min.js"></script>
     <script src="fileSaver.js"></script>
     <script src="fileSaver.js"></script>
     <!-- Bootstrap -->
     <!-- Bootstrap -->
     <link href="bootstrap/css/bootstrap.css" rel="stylesheet">
     <link href="bootstrap/css/bootstrap.css" rel="stylesheet">
@@ -19,7 +20,7 @@
     <script src="https://babylonjs.azurewebsites.net/Oimo.js"></script>
     <script src="https://babylonjs.azurewebsites.net/Oimo.js"></script>
     <!--<script src="../babylon.js"></script>-->
     <!--<script src="../babylon.js"></script>-->
     <script src="https://babylonjs.azurewebsites.net/babylon.max.js"></script>
     <script src="https://babylonjs.azurewebsites.net/babylon.max.js"></script>
-    <script src="https://babylonjs.azurewebsites.net/babylon.canvas2d.max.js"></script>
+    <script src="https://babylonjs.azurewebsites.net/babylon.canvas2d.js"></script>
     
     
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.fireMaterial.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.fireMaterial.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.waterMaterial.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.waterMaterial.js"></script>
@@ -99,7 +100,7 @@
         <div id="jsEditor"></div>
         <div id="jsEditor"></div>
         <div splitter></div>
         <div splitter></div>
         <div id="canvasZone">
         <div id="canvasZone">
-            <canvas id="renderCanvas"></canvas>
+            <canvas touch-action="none" id="renderCanvas"></canvas>
         </div>
         </div>
     </x-splitbox>
     </x-splitbox>
 
 

+ 2 - 1
Playground/frame.html

@@ -3,6 +3,7 @@
 <head>
 <head>
     <title>Babylon.js Playground</title>
     <title>Babylon.js Playground</title>
     <script src="hand.minified-1.2.js"></script>
     <script src="hand.minified-1.2.js"></script>
+    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/poly2tri.js"></script>    
     <script src="https://babylonjs.azurewebsites.net/poly2tri.js"></script>    
     <!-- Babylon.js -->    
     <!-- Babylon.js -->    
     <script src="https://babylonjs.azurewebsites.net/cannon.js"></script>
     <script src="https://babylonjs.azurewebsites.net/cannon.js"></script>
@@ -40,7 +41,7 @@
     <link href="frame.css" rel="stylesheet" />
     <link href="frame.css" rel="stylesheet" />
 </head>
 </head>
 <body>
 <body>
-    <canvas id="renderCanvas"></canvas>
+    <canvas touch-action="none" id="renderCanvas"></canvas>
 
 
     <span class="label" id="fpsLabel">FPS</span>
     <span class="label" id="fpsLabel">FPS</span>
 
 

+ 2 - 1
Playground/index-local.html

@@ -2,6 +2,7 @@
 <html>
 <html>
 <head>
 <head>
     <title>Babylon.js Playground</title>
     <title>Babylon.js Playground</title>
+    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="http://www.babylonjs.com/poly2tri.js"></script>
     <script src="http://www.babylonjs.com/poly2tri.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <!--x-tag-->
     <!--x-tag-->
@@ -67,7 +68,7 @@
         <div id="jsEditor"></div>
         <div id="jsEditor"></div>
         <div splitter></div>
         <div splitter></div>
         <div id="canvasZone">
         <div id="canvasZone">
-            <canvas id="renderCanvas"></canvas>
+            <canvas touch-action="none" id="renderCanvas"></canvas>
         </div>
         </div>
     </x-splitbox>
     </x-splitbox>
 
 

+ 2 - 1
Playground/index.html

@@ -2,6 +2,7 @@
 <html>
 <html>
 <head>
 <head>
     <title>Babylon.js Playground</title>
     <title>Babylon.js Playground</title>
+    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/poly2tri.js"></script>
     <script src="https://babylonjs.azurewebsites.net/poly2tri.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <!--x-tag-->
     <!--x-tag-->
@@ -99,7 +100,7 @@
         <div id="jsEditor"></div>
         <div id="jsEditor"></div>
         <div splitter></div>
         <div splitter></div>
         <div id="canvasZone">
         <div id="canvasZone">
-            <canvas id="renderCanvas"></canvas>
+            <canvas touch-action="none" id="renderCanvas"></canvas>
         </div>
         </div>
     </x-splitbox>
     </x-splitbox>
 
 

+ 3 - 0
Tools/Gulp/config.json

@@ -465,11 +465,13 @@
     "libraries": [  
     "libraries": [  
     {
     {
       "files": [
       "files": [
+        "../../canvas2D/src/Tools/babylon.math2D.ts",
         "../../canvas2D/src/Tools/babylon.IPropertyChanged.ts",
         "../../canvas2D/src/Tools/babylon.IPropertyChanged.ts",
         "../../canvas2D/src/Tools/babylon.observableArray.ts",
         "../../canvas2D/src/Tools/babylon.observableArray.ts",
         "../../canvas2D/src/Tools/babylon.observableStringDictionary.ts",
         "../../canvas2D/src/Tools/babylon.observableStringDictionary.ts",
         "../../canvas2D/src/Engine/babylon.fontTexture.ts",
         "../../canvas2D/src/Engine/babylon.fontTexture.ts",
         "../../canvas2D/src/Engine/babylon.bounding2d.ts",
         "../../canvas2D/src/Engine/babylon.bounding2d.ts",
+        "../../canvas2D/src/Engine/babylon.primitiveCollisionManager.ts",
         "../../canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts",
         "../../canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts",
         "../../canvas2D/src/Engine/babylon.brushes2d.ts",
         "../../canvas2D/src/Engine/babylon.brushes2d.ts",
         "../../canvas2D/src/Engine/babylon.smartPropertyPrim.ts",
         "../../canvas2D/src/Engine/babylon.smartPropertyPrim.ts",
@@ -478,6 +480,7 @@
         "../../canvas2D/src/Engine/babylon.renderablePrim2d.ts",
         "../../canvas2D/src/Engine/babylon.renderablePrim2d.ts",
         "../../canvas2D/src/Engine/babylon.shape2d.ts",
         "../../canvas2D/src/Engine/babylon.shape2d.ts",
         "../../canvas2D/src/Engine/babylon.group2d.ts",
         "../../canvas2D/src/Engine/babylon.group2d.ts",
+        "../../canvas2D/src/Engine/babylon.wireFrame2d.ts",
         "../../canvas2D/src/Engine/babylon.rectangle2d.ts",
         "../../canvas2D/src/Engine/babylon.rectangle2d.ts",
         "../../canvas2D/src/Engine/babylon.ellipse2d.ts",
         "../../canvas2D/src/Engine/babylon.ellipse2d.ts",
         "../../canvas2D/src/Engine/babylon.sprite2d.ts",
         "../../canvas2D/src/Engine/babylon.sprite2d.ts",

+ 1 - 0
Tools/Npm/package.json

@@ -19,6 +19,7 @@
     "babylon.max.js",
     "babylon.max.js",
     "babylon.noworker.js",
     "babylon.noworker.js",
     "babylon.core.js",
     "babylon.core.js",
+    "babylon.canvas2d.js",
     "Oimo.js",
     "Oimo.js",
     "package.json"
     "package.json"
   ],
   ],

+ 49 - 0
canvas2D/features.md

@@ -0,0 +1,49 @@
+Canvas2D: features list
+====================
+
+## Features soup
+
+Here is some features, in no particular order. To get a clearer picture of what is where, keep reading this document.
+
+ - Rendering is **WebGL** based, powered by Babylon.js core architecture.
+ - Draw Calls are **minimized** due to a rendering architecture using (when available) the [ANGLE_instanced_arrays](https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays) extension.
+   - All non transparent instances of the Ellipse2D, Rectangl2D are drawn in one draw call per primitive type whatever the Ellipse/Rectangle width, height, roundRadius, subdivision, fill/border brush are.
+   - All non transparent instances of Sprite2D using the same Texture are rendered into a single draw call.
+   - All instances of [Text2D](http://doc.babylonjs.com/overviews/Canvas2D_Text2D) using the same fontName in Signed Distance Field mode are rendered into a single draw call. Non SDF Text follow the Transparent rendering rules.
+   - All transparent primitives applying to the rules above can be rendered into a single call if there are consecutive in the depth order. Transparent Primitives must be rendered **back to front** no matter what, it's packed into Instanced Arrays when it can, otherwise single draw calls are made.
+ - All the rendering engine's architecture is based on dirty/update caches to compute the data only when needed to **avoid computation when nothing changes**.
+ - Part of the content of a Canvas (or the whole) can be **cached** into a big texture in order to avoid a render every frame, in order to maximize performance by rendering just once static content. [More info](http://doc.babylonjs.com/overviews/Canvas2D_Rendering).
+ - You can create on screen Canvas, called **ScreenSpaceCanvas** (SSC) that are displayed on the top of the 3D Scene (or below, in between, in the depth layer you want...)
+ - You can create a Canvas that is displayed in the 3D Scene on a Rectangle, this a **WorldSpaceCanvas** (WSC), some features:
+   - The rendering quality of a WSC is **adaptive**, the closer your WSC is the bigger is the surface when its content is rendered to, avoiding you to see pixelized content everything is adaptive, **even text**.
+   
+     - Without ![before](http://www.html5gamedevs.com/uploads/monthly_2016_06/57641896ccee5_c6o7BdT1.png.57ad694cf4cb86f884cfebb0ffa29fc1.png)
+     - With ![after](http://www.html5gamedevs.com/uploads/monthly_2016_06/576419dc5794d_Pastedimageat2016_06_1715_35.thumb.png.47a928c707526926198401132a049b50.png)
+   - A WSC can **track** a 3D Scene Node, and optionally always face the screen, [example](http://babylonjs-playground.com/#1KYG17#1).
+   - By default a WSC is rendered on a Plane, but you can render it on pretty much every **surface you want**, [example](http://babylonjs-playground.com/#EPFQG#4).
+ - A Canvas can rely on a **Design Size** for the user to work with a Fixed Size Surface and position its primitives accordingly, the rendered size of the Canvas adapting to the rendering device, scaling the content for proportions to be kept, [more info](http://doc.babylonjs.com/overviews/Canvas2D_DesignSize).
+ - You can create custom frame of reference or containers, called **Group2D**, [more info](http://doc.babylonjs.com/overviews/Canvas2D_Group2D). A Group2D's content can be **cached** into a big texture in order to avoid rendering of its content every frame, as stated above.
+ - An **Interaction Engine** support the HTML PointerEvent, which is replicated at the level of a primitive, you can now when the pointer is entering/leaving a primitive's surface, when there's a touch/click, etc. [more info](http://doc.babylonjs.com/overviews/Canvas2D_Interaction). Interaction works for both WSC and SSC.
+ - A **Collision Engine** at the Canvas level detect all the primitives intersecting each others or against the Canvas' borders, [more info](http://doc.babylonjs.com/overviews/Canvas2D_PCM)
+ - Every primitive can be **animated** using the Babylon.js [animation framework](http://doc.babylonjs.com/tutorials/animations), [example](http://babylonjs-playground.com/#FFTQL#3).
+ - Primitives can be positioned manually using the **Absolute Positioning** ([more info](http://doc.babylonjs.com/overviews/Canvas2D_PosTransHierarchy)) or arranged by a **Layout Engine** and the **Positioning Engine** using Margin, Margin Alignment, Padding properties, [more info](http://doc.babylonjs.com/overviews/Canvas2D_Prim_Positioning).
+ - Primitives can be manually sized or be auto-size based on their content and/or the area taken by their children.
+ - Multiple Layout Engines can be developed through time, for now only the default one (Canvas Positioning) and a StackPanel are implemented. Grid Layout and Radial Layout will come soon.
+ - The Primitive types implemented so far:
+   - [Shape based](http://doc.babylonjs.com/overviews/Canvas2D_Shape2D):
+     - [Rectangle2D](http://doc.babylonjs.com/overviews/Canvas2D_Rectangle2D): a simple rectangle with a width and a height and an optional roundedRadius to have rounded corners.
+     - [Ellipse2D](http://doc.babylonjs.com/overviews/Canvas2D_Ellipse2D): an ellipse (so also a circle) with a width, height and a subdivision count for its rendering.
+     - [Lines2D](http://doc.babylonjs.com/overviews/Canvas2D_Lines2D): to render opened or closed line path from a list of points, with a given thickness and for opened Lines2D optional starting and ending caps (RoundCap, TriangleCap, SquaredAnchorCap, etc.)
+   - [Text2D](http://doc.babylonjs.com/overviews/Canvas2D_Text2D): render a text using either:
+     - A generated font using a given CSS-style fontName, in this mode the text can be rendered using three techniques: standard quality, super sample quality, signed distance field algorithm.
+     - A BitmapFontTexture to render more game/fancy character.
+     - This primitive also support Text Word Wrapping and Text Alignment (both horizontally and vertically).
+   - [Sprite2D](http://doc.babylonjs.com/overviews/Canvas2D_Sprite2D): render a sprite from a texture, sprite frame is supported for sprite animation, the sprite can be stored in an [AtlasPicture](http://doc.babylonjs.com/overviews/Canvas2D_AtlasPicture) and/or be rendered using the [Scale9Sprite](http://doc.babylonjs.com/overviews/Canvas2D_Sprite2D#scale9Sprite-feature) algorithm.
+   - [WireFrame2D](http://doc.babylonjs.com/overviews/Canvas2D_WireFrame2D): to render many wire lines.
+
+## Class diagram
+
+You can get a high level view of the implemented classes with this class diagram:
+
+![ClassDiagram](http://imgur.com/qclw4cI.png)
+

+ 65 - 11
canvas2D/readme.md

@@ -1,48 +1,102 @@
-Build Babylon.canvas2d.js with Gulp
+Canvas2D, a 100% WebGL based 2D Engine
 ====================
 ====================
 
 
-More info about [Canvas2D](http://doc.babylonjs.com/overviews/Canvas2D_Home)
+## Table of Content
 
 
+- [Introduction](#introduction)
+- [Documentation, want to give feedback, report a bug or need help?](#documentation)
+- [Releases](#releases)
+- [Features list](features.md) (separated page)
+- [How to build it](#how-to-build-babyloncanvas2djs-with-gulp)
+
+## Introduction
+
+Canvas2D is a separated distributed set of .js/.d.ts files laying on the top of the [babylon.js library](../readme.md).
+
+Its purpose is to provide a general featured 2D Engine that will serve as the foundations for:
+
+ - Any 2D Graphics related work
+ - A WebGL based GUI Library also present in the library (but under development right now.)
+
+ **Canvas2D provides two types of Canvas**
+
+  - [ScreenSpace](http://babylonjs-playground.com/#272WI1#6) Canvas, lying on the top (or [below](http://babylonjs-playground.com/#RXVJD#2)/between) the 3D content. Typically used for 3D Game/App on screen UI
+  - [WorldSpace](http://babylonjs-playground.com/#1BKDEO#22) Canvas, to display the content of a Canvas right in the 3D Scene. You can even make it [track a scene node and using billboard](http://babylonjs-playground.com/#1KYG17#1) mode to make it always face the screen.
+
+## Documentation
+
+#### Overview
+There's a full overview [documentation](http://doc.babylonjs.com/overviews/Canvas2D_Home) that we **greatly encourage you to read at least a bit before you start !**
+
+This overview page has many links to other documentation pages (organized like a wiki) you can learn a lot about the basic usage, the different features, how rendering works and the overall architecture of the 2D Engine.
+
+#### Reference
+The reference documentation is available [here](http://doc.babylonjs.com/classes/), most of the Canvas2D classes are suffixed by `2D` so you can use it in the filter box like this:![2D classes](http://i.imgur.com/hx4Llmi.png)
+
+#### Using the Forum
+
+If you need help, want to give feedback, report a bug or be aware of the latest development: you have to use the **[Babylon.js forum](http://www.html5gamedevs.com/forum/16-babylonjs/)**.
+
+ - Questions are to be posted [here](http://www.html5gamedevs.com/forum/28-questions-answers/)
+ - Bugs reports must be made [there](http://www.html5gamedevs.com/forum/30-bugs/)
+ - Check [this post](http://www.html5gamedevs.com/topic/22552-canvas2d-main-post/) to be aware of all the improvements/fixes made during the alpha/beta development of the library. You can check the first post as I update it each time there's new stuff or I simply encourage you to follow the thread to get notified. **Please** don't ask question or report bugs in this thread, create a dedicated one, thanks!
+ - [Another post](http://www.html5gamedevs.com/topic/25275-the-gui-lib-of-babylonjs/) was created to track the progress on the GUI Library, same rules and observations as above.
+
+**Important** when you post something you better mentioned me using `@nockawa`, I'm **not** checking the forum everyday but some other users does and ping me if needed, but still: mentioning me is the best way to get my attention.
+
+## Releases
+
+You want to use an existing build, that's simple, you can grab either the latest official release or the latest build of the current developing version.
+
+- The latest official release can be found [here](https://github.com/BabylonJS/Babylon.js/tree/master/dist)
+- The latest preview release (which is the current developing version, stable most of the time) can be found [there](https://github.com/BabylonJS/Babylon.js/tree/master/dist/preview%20release/canvas2D)
+
+
+## How to build babylon.canvas2d.js with Gulp
+
+If you want to locally build the canvas2D library, you can follow the steps below. But sure you've read [this page](http://doc.babylonjs.com/generals/how_to_start) before to learn how to setup your local repository and the general build concepts.
+
+### Gulp
 Build Babylon.canvas2d.js with [gulp](http://gulpjs.com/ "gulp") and npm ([nodejs](http://nodejs.org/ "nodejs")), easy and cross-platform
 Build Babylon.canvas2d.js with [gulp](http://gulpjs.com/ "gulp") and npm ([nodejs](http://nodejs.org/ "nodejs")), easy and cross-platform
 
 
 (Paths in this file are relative to this file location.)
 (Paths in this file are relative to this file location.)
 
 
-# How to use it
+### How to use it
 
 
 From the /Tools/Gulp folder:
 From the /Tools/Gulp folder:
 
 
-### First install gulp :
+#### First install gulp :
 ```
 ```
 npm install -g gulp
 npm install -g gulp
 ```
 ```
 
 
-### Install some dependencies :
+#### Install some dependencies :
 ```
 ```
 npm install
 npm install
 ```
 ```
 
 
-### Update dependencies if necessary :
+#### Update dependencies if necessary :
 ```
 ```
 npm update
 npm update
 ```
 ```
 
 
-## From the javascript source
-### Build Babylon.canvas2d.js:
+### From the javascript source
+#### Build Babylon.canvas2d.js:
 
 
 ```
 ```
-gulp Canvas2D
+gulp canvas2D
 ```
 ```
 Will be generated in dist/preview release/canvas2D:
 Will be generated in dist/preview release/canvas2D:
 - babylon.canvas2d.min.js
 - babylon.canvas2d.min.js
 - babylon.canvas2d.js (unminified)
 - babylon.canvas2d.js (unminified)
 - babylon.canvas2d.d.ts
 - babylon.canvas2d.d.ts
 
 
-### Build the changed files for debug when you save a typescript or shader file:
+#### Build the changed files for debug when you save a typescript or shader file:
 ```
 ```
 gulp watch
 gulp watch
 ```
 ```
 
 
-### Watch and run a web server for debug purpose:
+#### Watch and run a web server for debug purpose:
 ```
 ```
 gulp run
 gulp run
 ```
 ```

+ 80 - 0
canvas2D/src/Engine/babylon.bounding2d.ts

@@ -26,6 +26,9 @@
             this.radius = 0;
             this.radius = 0;
             this.center = Vector2.Zero();
             this.center = Vector2.Zero();
             this.extent = Vector2.Zero();
             this.extent = Vector2.Zero();
+            this._worldAABBDirty = false;
+            this._worldAABBDirtyObservable = null;
+            this._worldAABB = Vector4.Zero();
         }
         }
 
 
         /**
         /**
@@ -74,6 +77,7 @@
             b.extent.x = b.center.x;
             b.extent.x = b.center.x;
             b.extent.y = b.center.y;
             b.extent.y = b.center.y;
             b.radius = b.extent.length();
             b.radius = b.extent.length();
+            b._worldAABBDirty = true;
         }
         }
 
 
         /**
         /**
@@ -87,6 +91,7 @@
             b.extent.x = r;
             b.extent.x = r;
             b.extent.y = r;
             b.extent.y = r;
             b.radius = r;
             b.radius = r;
+            b._worldAABBDirty = true;
         }
         }
 
 
         /**
         /**
@@ -119,6 +124,7 @@
             b.center = new Vector2(xmin + w / 2, ymin + h / 2);
             b.center = new Vector2(xmin + w / 2, ymin + h / 2);
             b.extent = new Vector2(xmax - b.center.x, ymax - b.center.y);
             b.extent = new Vector2(xmax - b.center.x, ymax - b.center.y);
             b.radius = b.extent.length();
             b.radius = b.extent.length();
+            b._worldAABBDirty = true;
         }
         }
 
 
         /**
         /**
@@ -137,12 +143,14 @@
             this.center.copyFromFloats(0, 0);
             this.center.copyFromFloats(0, 0);
             this.radius = 0;
             this.radius = 0;
             this.extent.copyFromFloats(0, 0);
             this.extent.copyFromFloats(0, 0);
+            this._worldAABBDirty = true;
         }
         }
 
 
         public copyFrom(src: BoundingInfo2D) {
         public copyFrom(src: BoundingInfo2D) {
             this.center.copyFrom(src.center);
             this.center.copyFrom(src.center);
             this.radius = src.radius;
             this.radius = src.radius;
             this.extent.copyFrom(src.extent);
             this.extent.copyFrom(src.extent);
+            this._worldAABBDirty = true;
         }
         }
 
 
         /**
         /**
@@ -234,6 +242,13 @@
             return r;
             return r;
         }
         }
 
 
+        public worldAABBIntersectionTest(other: BoundingInfo2D): boolean {
+            let a = this.worldAABB;
+            let b = other.worldAABB;
+
+            return b.z >= a.x && b.x <= a.z && b.w >= a.y && b.y <= a.w;
+        }
+
         private static _transform: Array<Vector2> = new Array<Vector2>(Vector2.Zero(), Vector2.Zero(), Vector2.Zero(), Vector2.Zero());
         private static _transform: Array<Vector2> = new Array<Vector2>(Vector2.Zero(), Vector2.Zero(), Vector2.Zero(), Vector2.Zero());
 
 
         /**
         /**
@@ -261,6 +276,67 @@
             BoundingInfo2D.CreateFromPointsToRef(p, result);
             BoundingInfo2D.CreateFromPointsToRef(p, result);
         }
         }
 
 
+        private _updateWorldAABB(worldMatrix: Matrix) {
+            // Construct a bounding box based on the extent values
+            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
+            for (let i = 0; i < 4; i++) {
+                Vector2.TransformToRef(p[i], worldMatrix, p[i]);
+            }
+
+            this._worldAABB.x = Math.min(Math.min(p[0].x, p[1].x), Math.min(p[2].x, p[3].x));
+            this._worldAABB.y = Math.min(Math.min(p[0].y, p[1].y), Math.min(p[2].y, p[3].y));
+            this._worldAABB.z = Math.max(Math.max(p[0].x, p[1].x), Math.max(p[2].x, p[3].x));
+            this._worldAABB.w = Math.max(Math.max(p[0].y, p[1].y), Math.max(p[2].y, p[3].y));
+        }
+
+        public worldMatrixAccess: () => Matrix;
+
+        public get worldAABBDirtyObservable(): Observable<BoundingInfo2D> {
+            if (!this._worldAABBDirtyObservable) {
+                this._worldAABBDirtyObservable = new Observable<BoundingInfo2D>();
+            }
+            return this._worldAABBDirtyObservable;
+        }
+
+        public get isWorldAABBDirty() {
+            return this._worldAABBDirty;
+        }
+
+        public dirtyWorldAABB() {
+            if (this._worldAABBDirty) {
+                return;
+            }
+
+            this._worldAABBDirty = true;
+            if (this._worldAABBDirtyObservable && this._worldAABBDirtyObservable.hasObservers()) {
+                this._worldAABBDirtyObservable.notifyObservers(this);
+            }
+        }
+
+        /**
+         * Retrieve the world AABB, the Vector4's data is x=xmin, y=ymin, z=xmax, w=ymax
+         */
+        public get worldAABB(): Vector4 {
+            if (this._worldAABBDirty) {
+                if (!this.worldMatrixAccess) {
+                    throw new Error("you must set the worldMatrixAccess function first");
+                }
+                this._updateWorldAABB(this.worldMatrixAccess());
+                this._worldAABBDirty = false;
+            }
+            return this._worldAABB;
+        }
+
         /**
         /**
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
          * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it 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.
@@ -290,5 +366,9 @@
             }
             }
             return false;
             return false;
         }
         }
+
+        private _worldAABBDirtyObservable: Observable<BoundingInfo2D>;
+        private _worldAABBDirty: boolean;
+        private _worldAABB: Vector4;
     }
     }
 }
 }

+ 154 - 90
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -66,21 +66,24 @@
         private static _INSTANCES : Array<Canvas2D> = [];
         private static _INSTANCES : Array<Canvas2D> = [];
 
 
         constructor(scene: Scene, settings?: {
         constructor(scene: Scene, settings?: {
-            id?: string,
-            children?: Array<Prim2DBase>,
-            size?: Size,
-            renderingPhase?: { camera: Camera, renderingGroupID: number },
-            designSize?: Size,
-            designUseHorizAxis?: boolean,
-            isScreenSpace?: boolean,
-            cachingStrategy?: number,
-            enableInteraction?: boolean,
-            origin?: Vector2,
-            isVisible?: boolean,
-            backgroundRoundRadius?: number,
-            backgroundFill?: IBrush2D | string,
-            backgroundBorder?: IBrush2D | string,
-            backgroundBorderThickNess?: number,
+            id                            ?: string,
+            children                      ?: Array<Prim2DBase>,
+            size                          ?: Size,
+            renderingPhase                ?: { camera: Camera, renderingGroupID: number },
+            designSize                    ?: Size,
+            designUseHorizAxis            ?: boolean,
+            isScreenSpace                 ?: boolean,
+            cachingStrategy               ?: number,
+            enableInteraction             ?: boolean,
+            enableCollisionManager        ?: boolean,
+            customCollisionManager        ?: (owner: Canvas2D, enableBorders: boolean) => PrimitiveCollisionManagerBase,
+            collisionManagerUseBorders    ?: boolean,
+            origin                        ?: Vector2,
+            isVisible                     ?: boolean,
+            backgroundRoundRadius         ?: number,
+            backgroundFill                ?: IBrush2D | string,
+            backgroundBorder              ?: IBrush2D | string,
+            backgroundBorderThickNess     ?: number,
         }) {
         }) {
             super(settings);
             super(settings);
 
 
@@ -97,7 +100,6 @@
             this._updateGlobalTransformCounter = new PerfCounter();
             this._updateGlobalTransformCounter = new PerfCounter();
             this._boundingInfoRecomputeCounter = new PerfCounter();
             this._boundingInfoRecomputeCounter = new PerfCounter();
 
 
-            this._uid = null;
             this._cachedCanvasGroup = null;
             this._cachedCanvasGroup = null;
 
 
             this._renderingGroupObserver = null;
             this._renderingGroupObserver = null;
@@ -173,7 +175,6 @@
             }
             }
             this._maxAdaptiveWorldSpaceCanvasSize = null;
             this._maxAdaptiveWorldSpaceCanvasSize = null;
             this._groupCacheMaps = new StringDictionary<MapTexture[]>();
             this._groupCacheMaps = new StringDictionary<MapTexture[]>();
-
             this._patchHierarchy(this);
             this._patchHierarchy(this);
 
 
             let enableInteraction = (settings.enableInteraction == null) ? true : settings.enableInteraction;
             let enableInteraction = (settings.enableInteraction == null) ? true : settings.enableInteraction;
@@ -214,8 +215,15 @@
             this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
             this._supprtInstancedArray = this._engine.getCaps().instancedArrays !== null;
                         //this._supprtInstancedArray = false; // TODO REMOVE!!!
                         //this._supprtInstancedArray = false; // TODO REMOVE!!!
 
 
+            // Setup the canvas for interaction (or not)
             this._setupInteraction(enableInteraction);
             this._setupInteraction(enableInteraction);
 
 
+            // Initialize the Primitive Collision Manager
+            if (settings.enableCollisionManager) {
+                let enableBorders = settings.collisionManagerUseBorders;
+                this._primitiveCollisionManager = (settings.customCollisionManager==null) ? PrimitiveCollisionManagerBase.allocBasicPCM(this, enableBorders) : settings.customCollisionManager(this, enableBorders);
+            }
+
             // Register this instance
             // Register this instance
             Canvas2D._INSTANCES.push(this);
             Canvas2D._INSTANCES.push(this);
         }
         }
@@ -272,6 +280,10 @@
             return Canvas2D._INSTANCES;
             return Canvas2D._INSTANCES;
         }
         }
 
 
+        public get primitiveCollisionManager(): PrimitiveCollisionManagerBase {
+            return this._primitiveCollisionManager;
+        }
+
         protected _canvasPreInit(settings: any) {
         protected _canvasPreInit(settings: any) {
             let cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_DONTCACHE : settings.cachingStrategy;
             let cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_DONTCACHE : settings.cachingStrategy;
             this._cachingStrategy = cachingStrategy;
             this._cachingStrategy = cachingStrategy;
@@ -328,6 +340,8 @@
                         if (e.pickInfo.hit && e.pickInfo.pickedMesh === this._worldSpaceNode && this.worldSpaceToNodeLocal) {
                         if (e.pickInfo.hit && e.pickInfo.pickedMesh === this._worldSpaceNode && this.worldSpaceToNodeLocal) {
                             let localPos = this.worldSpaceToNodeLocal(e.pickInfo.pickedPoint);
                             let localPos = this.worldSpaceToNodeLocal(e.pickInfo.pickedPoint);
                             this._handlePointerEventForInteraction(e, localPos, s);
                             this._handlePointerEventForInteraction(e, localPos, s);
+                        } else if (this._actualIntersectionList && this._actualIntersectionList.length > 0) {
+                            this._handlePointerEventForInteraction(e, null, s);
                         }
                         }
                     });
                     });
                 }
                 }
@@ -456,15 +470,19 @@
             }
             }
 
 
             // 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
-            if (!this._updatePointerInfo(eventData, localPosition)) {
-                return;
+            if (localPosition) {
+                if (!this._updatePointerInfo(eventData, localPosition)) {
+                    return;
+                }
+            } else {
+                this._primPointerInfo.canvasPointerPos = null;
             }
             }
 
 
             let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
             let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
 
 
             // Make sure the intersection list is up to date, we maintain this list either in response of a mouse event (here) or before rendering the canvas.
             // 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.
-            this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, capturedPrim !== null, true);
+            this._updateIntersectionList(localPosition ? this._primPointerInfo.canvasPointerPos : null, capturedPrim !== null, true);
 
 
             // Update the over status, same as above, it's could be done here or during rendering, but will be performed only once per render frame
             // Update the over status, same as above, it's could be done here or during rendering, but will be performed only once per render frame
             this._updateOverStatus(true);
             this._updateOverStatus(true);
@@ -560,35 +578,38 @@
                 return;
                 return;
             }
             }
 
 
-            // A little safe guard, it might happens than the event is triggered before the first render and nothing is computed, this simple check will make sure everything will be fine
-            if (!this._globalTransform) {
-                this.updateCachedStates(true);
-            }
-
             let ii = Canvas2D._interInfo;
             let ii = Canvas2D._interInfo;
-            ii.pickPosition.x = mouseLocalPos.x;
-            ii.pickPosition.y = mouseLocalPos.y;
-            ii.findFirstOnly = false;
-
-            // Fast rejection: test if the mouse pointer is outside the canvas's bounding Info
-            if (!isCapture && !this.levelBoundingInfo.doesIntersect(ii.pickPosition)) {
-                // Reset intersection info as we don't hit anything
-                ii.intersectedPrimitives = new Array<PrimitiveIntersectedInfo>();
-                ii.topMostIntersectedPrimitive = null;
-            } else {
-                // The pointer is inside the Canvas, do an intersection test
-                this.intersect(ii);
+            let outCase = mouseLocalPos == null;
+            if (!outCase) {
+                // A little safe guard, it might happens than the event is triggered before the first render and nothing is computed, this simple check will make sure everything will be fine
+                if (!this._globalTransform) {
+                    this.updateCachedStates(true);
+                }
+
+                ii.pickPosition.x = mouseLocalPos.x;
+                ii.pickPosition.y = mouseLocalPos.y;
+                ii.findFirstOnly = false;
 
 
-                // Sort primitives to get them from top to bottom
-                ii.intersectedPrimitives = ii.intersectedPrimitives.sort((a, b) => a.prim.actualZOffset - b.prim.actualZOffset);
+                // Fast rejection: test if the mouse pointer is outside the canvas's bounding Info
+                if (!isCapture && !this.levelBoundingInfo.doesIntersect(ii.pickPosition)) {
+                    // Reset intersection info as we don't hit anything
+                    ii.intersectedPrimitives = new Array<PrimitiveIntersectedInfo>();
+                    ii.topMostIntersectedPrimitive = null;
+                } else {
+                    // The pointer is inside the Canvas, do an intersection test
+                    this.intersect(ii);
+
+                    // Sort primitives to get them from top to bottom
+                    ii.intersectedPrimitives = ii.intersectedPrimitives.sort((a, b) => a.prim.actualZOffset - b.prim.actualZOffset);
+                }
             }
             }
 
 
             {
             {
                 // Update prev/actual intersection info, fire "overPrim" property change if needed
                 // Update prev/actual intersection info, fire "overPrim" property change if needed
                 this._previousIntersectionList = this._actualIntersectionList;
                 this._previousIntersectionList = this._actualIntersectionList;
-                this._actualIntersectionList = ii.intersectedPrimitives;
-                this._previousOverPrimitive = this._actualOverPrimitive;
-                this._actualOverPrimitive = ii.topMostIntersectedPrimitive;
+                this._actualIntersectionList   = outCase ? new Array<PrimitiveIntersectedInfo>() : ii.intersectedPrimitives;
+                this._previousOverPrimitive    = this._actualOverPrimitive;
+                this._actualOverPrimitive      = outCase ? null : ii.topMostIntersectedPrimitive;
 
 
                 let prev = (this._previousOverPrimitive != null) ? this._previousOverPrimitive.prim : null;
                 let prev = (this._previousOverPrimitive != null) ? this._previousOverPrimitive.prim : null;
                 let actual = (this._actualOverPrimitive != null) ? this._actualOverPrimitive.prim : null;
                 let actual = (this._actualOverPrimitive != null) ? this._actualOverPrimitive.prim : null;
@@ -881,16 +902,6 @@
         }
         }
 
 
         /**
         /**
-         * return a unique identifier for the Canvas2D
-         */
-        public get uid(): string {
-            if (!this._uid) {
-                this._uid = Tools.RandomId();
-            }
-            return this._uid;
-        }
-
-        /**
          * And observable called during the Canvas rendering process.
          * And observable called during the Canvas rendering process.
          * This observable is called twice per render, each time with a different mask:
          * This observable is called twice per render, each time with a different mask:
          *  - 1: before render is executed
          *  - 1: before render is executed
@@ -1050,6 +1061,10 @@
             return this._designUseHorizAxis;
             return this._designUseHorizAxis;
         }
         }
 
 
+        public set designSizeUseHorizeAxis(value: boolean) {
+            this._designUseHorizAxis = value;
+        }
+
         /**
         /**
          * Return 
          * Return 
          */
          */
@@ -1205,7 +1220,6 @@
             this._updateGlobalTransformCounter.addCount(count, false);
             this._updateGlobalTransformCounter.addCount(count, false);
         }
         }
 
 
-        private _uid: string;
         private _renderObservable: Observable<Canvas2D>;
         private _renderObservable: Observable<Canvas2D>;
         private __engineData: Canvas2DEngineBoundData;
         private __engineData: Canvas2DEngineBoundData;
         private _interactionEnabled: boolean;
         private _interactionEnabled: boolean;
@@ -1246,6 +1260,7 @@
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         private _designSize: Size;
         private _designSize: Size;
         private _designUseHorizAxis: boolean;
         private _designUseHorizAxis: boolean;
+        public  _primitiveCollisionManager: PrimitiveCollisionManagerBase;
 
 
         public _renderingSize: Size;
         public _renderingSize: Size;
 
 
@@ -1271,6 +1286,8 @@
         private static tS = Vector3.Zero();
         private static tS = Vector3.Zero();
         private static tT = Vector3.Zero();
         private static tT = Vector3.Zero();
         private static tR = Quaternion.Identity();
         private static tR = Quaternion.Identity();
+        private static _tmpMtx = Matrix.Identity();
+        private static _tmpVec3 = Vector3.Zero();
 
 
         private _updateTrackedNodes() {
         private _updateTrackedNodes() {
             // Get the used camera
             // Get the used camera
@@ -1280,6 +1297,8 @@
             cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D._m);
             cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D._m);
             let rh = this.engine.getRenderHeight();
             let rh = this.engine.getRenderHeight();
             let v = cam.viewport.toGlobal(this.engine.getRenderWidth(), rh);
             let v = cam.viewport.toGlobal(this.engine.getRenderWidth(), rh);
+            let tmpVec3 = Canvas2D._tmpVec3;
+            let tmpMtx = Canvas2D._tmpMtx;
 
 
             // Compute the screen position of each group that track a given scene node
             // Compute the screen position of each group that track a given scene node
             for (let group of this._trackedGroups) {
             for (let group of this._trackedGroups) {
@@ -1290,6 +1309,13 @@
                 let node = group.trackedNode;
                 let node = group.trackedNode;
                 let worldMtx = node.getWorldMatrix();
                 let worldMtx = node.getWorldMatrix();
 
 
+                if(group.trackedNodeOffset){
+                    Vector3.TransformCoordinatesToRef(group.trackedNodeOffset, worldMtx, tmpVec3);
+                    tmpMtx.copyFrom(worldMtx);
+                    worldMtx = tmpMtx;
+                    worldMtx.setTranslation(tmpVec3);
+                }
+
                 let proj = Vector3.Project(Canvas2D._v, worldMtx, Canvas2D._m, v);
                 let proj = Vector3.Project(Canvas2D._v, worldMtx, Canvas2D._m, v);
 
 
                 // Set the visibility state accordingly, if the position is outside the frustum (well on the Z planes only...) set the group to hidden
                 // Set the visibility state accordingly, if the position is outside the frustum (well on the Z planes only...) set the group to hidden
@@ -1334,6 +1360,8 @@
                     if (scale) {
                     if (scale) {
                         wsn.scaling = scale;
                         wsn.scaling = scale;
                     }
                     }
+                } else {
+                    throw new Error("Can't Track another Scene Node Type than AbstractMesh right now, call me lazy!");
                 }
                 }
             }
             }
         }
         }
@@ -1415,8 +1443,8 @@
 
 
             // If the canvas fit the rendering size and it changed, update
             // If the canvas fit the rendering size and it changed, update
             if (renderingSizeChanged && this._fitRenderingDevice) {
             if (renderingSizeChanged && this._fitRenderingDevice) {
-                this._actualSize = this._renderingSize.clone();
-                this._size = this._renderingSize.clone();
+                this.actualSize = this._renderingSize.clone();
+                this.size = this._renderingSize.clone();
                 if (this._background) {
                 if (this._background) {
                     this._background.size = this.size;
                     this._background.size = this.size;
                 }
                 }
@@ -1474,12 +1502,16 @@
 
 
             this._updateCanvasState(false);
             this._updateCanvasState(false);
 
 
+            if (this._primitiveCollisionManager) {
+                this._primitiveCollisionManager._update();
+            }
+
             if (this._primPointerInfo.canvasPointerPos) {
             if (this._primPointerInfo.canvasPointerPos) {
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false, false);
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false, false);
                 this._updateOverStatus(false);
                 this._updateOverStatus(false);
             }
             }
 
 
-            this.engine.setState(false);
+            this.engine.setState(false, undefined, true);
             this._groupRender();
             this._groupRender();
 
 
             if (!this._isScreenSpace) {
             if (!this._isScreenSpace) {
@@ -1832,15 +1864,9 @@
             //    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");
             //}
             //}
 
 
-            if (settings.trackNode != null) {
-                this._trackNode = settings.trackNode;
-                this._trackNodeOffset = (settings.trackNodeOffset != null) ? settings.trackNodeOffset : Vector3.Zero();
-                this._trackNodeBillboard = (settings.trackNodeBillboard != null) ? settings.trackNodeBillboard : false;
-            } else {
-                this._trackNode = null;
-                this._trackNodeOffset = null;
-                this._trackNodeBillboard = false;
-            }
+            this._trackNode          = (settings.trackNode != null)          ? settings.trackNode          : null;
+            this._trackNodeOffset    = (settings.trackNodeOffset != null)    ? settings.trackNodeOffset    : Vector3.Zero();
+            this._trackNodeBillboard = (settings.trackNodeBillboard != null) ? settings.trackNodeBillboard : true;
 
 
             let createWorldSpaceNode = !settings || (settings.customWorldSpaceNode == null);
             let createWorldSpaceNode = !settings || (settings.customWorldSpaceNode == null);
             this._customWorldSpaceNode = !createWorldSpaceNode;
             this._customWorldSpaceNode = !createWorldSpaceNode;
@@ -1884,6 +1910,9 @@
             }
             }
 
 
             this.propertyChanged.add((e, st) => {
             this.propertyChanged.add((e, st) => {
+                if (e.propertyName !== "isVisible") {
+                    return;
+                }
                 let mesh = this._worldSpaceNode as AbstractMesh;
                 let mesh = this._worldSpaceNode as AbstractMesh;
                 if (mesh) {
                 if (mesh) {
                     mesh.isVisible = e.newValue;
                     mesh.isVisible = e.newValue;
@@ -1902,6 +1931,38 @@
             }
             }
         }
         }
 
 
+        public get trackNode(): Node {
+            return this._trackNode;
+        }
+
+        public set trackNode(value: Node) {
+            if (this._trackNode === value) {
+                return;
+            }
+
+            this._trackNode = value;
+        }
+
+        public get trackNodeOffset(): Vector3 {
+            return this._trackNodeOffset;
+        }
+
+        public set trackNodeOffset(value: Vector3) {
+            if (!this._trackNodeOffset) {
+                this._trackNodeOffset = value.clone();
+            } else {
+                this._trackNodeOffset.copyFrom(value);
+            }
+        }
+
+        public get trackNodeBillboard(): boolean {
+            return this._trackNodeBillboard;
+        }
+
+        public set trackNodeBillboard(value: boolean) {
+            this._trackNodeBillboard = value;
+        }
+
         private _customWorldSpaceNode: boolean;
         private _customWorldSpaceNode: boolean;
     }
     }
 
 
@@ -1944,31 +2005,34 @@
          */
          */
         constructor(scene: Scene, settings?: {
         constructor(scene: Scene, settings?: {
 
 
-            children?: Array<Prim2DBase>,
-            id?: string,
-            x?: number,
-            y?: number,
-            position?: Vector2,
-            origin?: Vector2,
-            width?: number,
-            height?: number,
-            size?: Size,
-            renderingPhase?: {camera: Camera, renderingGroupID: number },
-            designSize?: Size,
-            designUseHorizAxis?: boolean,
-            cachingStrategy?: number,
-            cacheBehavior?: number,
-            enableInteraction?: boolean,
-            isVisible?: boolean,
-            backgroundRoundRadius?: number,
-            backgroundFill?: IBrush2D | string,
-            backgroundBorder?: IBrush2D | string,
-            backgroundBorderThickNess?: number,
-            paddingTop?: number | string,
-            paddingLeft?: number | string,
-            paddingRight?: number | string,
-            paddingBottom?: number | string,
-            padding?: string,
+            children                   ?: Array<Prim2DBase>,
+            id                         ?: string,
+            x                          ?: number,
+            y                          ?: number,
+            position                   ?: Vector2,
+            origin                     ?: Vector2,
+            width                      ?: number,
+            height                     ?: number,
+            size                       ?: Size,
+            renderingPhase             ?: {camera: Camera, renderingGroupID: number },
+            designSize                 ?: Size,
+            designUseHorizAxis         ?: boolean,
+            cachingStrategy            ?: number,
+            cacheBehavior              ?: number,
+            enableInteraction          ?: boolean,
+            enableCollisionManager     ?: boolean,
+            customCollisionManager     ?: (owner: Canvas2D, enableBorders: boolean) => PrimitiveCollisionManagerBase,
+            collisionManagerUseBorders ?: boolean,
+            isVisible                  ?: boolean,
+            backgroundRoundRadius      ?: number,
+            backgroundFill             ?: IBrush2D | string,
+            backgroundBorder           ?: IBrush2D | string,
+            backgroundBorderThickNess  ?: number,
+            paddingTop                 ?: number | string,
+            paddingLeft                ?: number | string,
+            paddingRight               ?: number | string,
+            paddingBottom              ?: number | string,
+            padding                    ?: string,
 
 
         }) {
         }) {
             Prim2DBase._isCanvasInit = true;
             Prim2DBase._isCanvasInit = true;

+ 472 - 4
canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts

@@ -1,5 +1,9 @@
 module BABYLON {
 module BABYLON {
 
 
+    export interface ILayoutData {
+        
+    }
+
     @className("LayoutEngineBase", "BABYLON")
     @className("LayoutEngineBase", "BABYLON")
     /**
     /**
      * This is the base class you have to extend in order to implement your own Layout Engine.
      * This is the base class you have to extend in order to implement your own Layout Engine.
@@ -41,7 +45,18 @@
      * This layout must be used as a Singleton through the CanvasLayoutEngine.Singleton property.
      * This layout must be used as a Singleton through the CanvasLayoutEngine.Singleton property.
      */
      */
     export class CanvasLayoutEngine extends LayoutEngineBase {
     export class CanvasLayoutEngine extends LayoutEngineBase {
-        public static Singleton: CanvasLayoutEngine = new CanvasLayoutEngine();
+        private  static _singleton: CanvasLayoutEngine = null;
+        public static get Singleton(): CanvasLayoutEngine {
+            if (!CanvasLayoutEngine._singleton) {
+                CanvasLayoutEngine._singleton = new CanvasLayoutEngine();
+            }
+            return CanvasLayoutEngine._singleton;
+        } 
+
+        constructor() {
+            super();
+            this.layoutDirtyOnPropertyChangedMask = Prim2DBase.sizeProperty.flagId | Prim2DBase.actualSizeProperty.flagId;
+        }
 
 
         // A very simple (no) layout computing...
         // A very simple (no) layout computing...
         // The Canvas and its direct children gets the Canvas' size as Layout Area
         // The Canvas and its direct children gets the Canvas' size as Layout Area
@@ -50,7 +65,6 @@
 
 
             // If this prim is layoutDiry we update  its layoutArea and also the one of its direct children
             // If this prim is layoutDiry we update  its layoutArea and also the one of its direct children
             if (prim._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
             if (prim._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
-
                 for (let child of prim.children) {
                 for (let child of prim.children) {
                     this._doUpdate(child);
                     this._doUpdate(child);
                 }
                 }
@@ -90,7 +104,7 @@
     export class StackPanelLayoutEngine extends LayoutEngineBase {
     export class StackPanelLayoutEngine extends LayoutEngineBase {
         constructor() {
         constructor() {
             super();
             super();
-            this.layoutDirtyOnPropertyChangedMask = Prim2DBase.sizeProperty.flagId;
+            this.layoutDirtyOnPropertyChangedMask = Prim2DBase.sizeProperty.flagId | Prim2DBase.actualSizeProperty.flagId;
         }
         }
 
 
         public static get Horizontal(): StackPanelLayoutEngine {
         public static get Horizontal(): StackPanelLayoutEngine {
@@ -146,7 +160,7 @@
                     }
                     }
                     let layoutArea: Size;
                     let layoutArea: Size;
                     if (child._hasMargin) {
                     if (child._hasMargin) {
-                        child.margin.computeWithAlignment(prim.layoutArea, child.actualSize, child.marginAlignment, StackPanelLayoutEngine.dstOffset, StackPanelLayoutEngine.dstArea, true);
+                        child.margin.computeWithAlignment(prim.layoutArea, child.actualSize, child.marginAlignment, child.actualScale, StackPanelLayoutEngine.dstOffset, StackPanelLayoutEngine.dstArea, true);
                         layoutArea = StackPanelLayoutEngine.dstArea.clone();
                         layoutArea = StackPanelLayoutEngine.dstArea.clone();
                         child.layoutArea = layoutArea;
                         child.layoutArea = layoutArea;
                     } else {
                     } else {
@@ -183,4 +197,458 @@
             return false;
             return false;
         }
         }
     }
     }
+
+    /**
+     * GridData is used specify what row(s) and column(s) a primitive is placed in when its parent is using a Grid Panel Layout. 
+     */
+    export class GridData implements ILayoutData{
+
+        /**
+         * the row number of the grid
+         **/
+        public row:number;
+
+        /**
+         * the column number of the grid 
+         **/
+        public column:number;
+
+        /**
+         * the number of rows a primitive will occupy
+         **/
+        public rowSpan:number;
+
+        /**
+         * the number of columns a primitive will occupy 
+         **/
+        public columnSpan:number;
+
+        /**
+         * Create a Grid Data that describes where a primitive will be placed in a Grid Panel Layout.
+         * @param row the row number of the grid
+         * @param column the column number of the grid 
+         * @param rowSpan the number of rows a primitive will occupy
+         * @param columnSpan the number of columns a primitive will occupy 
+         **/
+        constructor(row:number, column:number, rowSpan?:number, columnSpan?:number){
+
+            this.row = row;
+            this.column = column;
+            this.rowSpan = (rowSpan == null) ? 1 : rowSpan;
+            this.columnSpan = (columnSpan == null) ? 1 : columnSpan;
+
+        }
+
+    }
+
+    class GridDimensionDefinition {
+        public static Pixels = 1;
+        public static Stars = 2;
+        public static Auto = 3;
+        _parse(value: string, res: (v: number, vp: number, t: number) => void) {
+            let v = value.toLocaleLowerCase().trim();
+            if (v.indexOf("auto") === 0) {
+                res(null, null, GridDimensionDefinition.Auto);
+            } else if (v.indexOf("*") !== -1) {
+                let i = v.indexOf("*");
+                let w = 1;
+                if(i > 0){
+                    w = parseFloat(v.substr(0, i));
+                }
+                res(w, null, GridDimensionDefinition.Stars);
+            } else {
+                let w = parseFloat(v);
+                res(w, w, GridDimensionDefinition.Pixels);
+            }
+        }
+    }
+    class RowDefinition extends GridDimensionDefinition {
+        heightPixels: number;
+        height: number;
+        heightType: number;
+    }
+    class ColumnDefinition extends GridDimensionDefinition {
+        widthPixels: number;
+        width: number;
+        widthType: number;
+    }
+
+    @className("GridPanelLayoutEngine", "BABYLON")
+    /**
+     * A grid panel layout.  Grid panel is a table that has rows and columns.
+     * When adding children to a primitive that is using grid panel layout, you must assign a GridData object to the child to indicate where the child will appear in the grid.
+     */
+    export class GridPanelLayoutEngine extends LayoutEngineBase {
+        constructor(settings: { rows: [{ height: string }], columns: [{ width: string }] }) {
+            super();
+            this.layoutDirtyOnPropertyChangedMask = Prim2DBase.sizeProperty.flagId | Prim2DBase.actualSizeProperty.flagId;
+            this._rows = new Array<RowDefinition>();
+            this._columns = new Array<ColumnDefinition>();
+            if (settings.rows) {
+                for (let row of settings.rows) {
+                    let r = new RowDefinition();
+                    r._parse(row.height, (v, vp, t) => {
+                        r.height = v;
+                        r.heightPixels = vp;
+                        r.heightType = t;
+                    });
+                    this._rows.push(r);
+                }
+            }
+            if (settings.columns) {
+                for (let col of settings.columns) {
+                    let r = new ColumnDefinition();
+                    r._parse(col.width, (v, vp, t) => {
+                        r.width = v;
+                        r.widthPixels = vp;
+                        r.widthType = t;
+                    });
+                    this._columns.push(r);
+                }
+            }
+
+        }
+
+        private _rows: Array<RowDefinition>;
+        private _columns: Array<ColumnDefinition>;
+        private _children: Prim2DBase[][] = [];
+        
+        private _rowBottoms: Array<number> = [];
+        private _columnLefts: Array<number> = [];
+
+        private _rowHeights: Array<number> = [];
+        private _columnWidths: Array<number> = [];
+
+        private static dstOffset = Vector4.Zero();
+        private static dstArea = Size.Zero();
+
+        public updateLayout(prim: Prim2DBase) {
+            if (prim._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
+
+                for (let child of prim.children) {
+                    if (child._isFlagSet(SmartPropertyPrim.flagNoPartOfLayout)) {
+                        continue;
+                    }
+                    if (child._hasMargin) {
+                        child.margin.computeWithAlignment(prim.layoutArea, child.actualSize, child.marginAlignment, child.actualScale, GridPanelLayoutEngine.dstOffset, GridPanelLayoutEngine.dstArea, true);
+                        child.layoutArea = GridPanelLayoutEngine.dstArea.clone();
+                    } else {
+                        child.margin.computeArea(child.actualSize, child.layoutArea);
+                    }
+                }
+
+                this._updateGrid(prim);
+
+                let _children = this._children;
+                let rl = this._rows.length;
+                let cl = this._columns.length;
+                let columnWidth = 0;
+                let rowHeight = 0;
+                let layoutArea = new BABYLON.Size(0, 0);
+
+                for(let i = 0; i < _children.length; i++){
+                    let children = _children[i];
+
+                    if(children){
+
+                        let bottom = this._rowBottoms[i];
+                        let rowHeight = this._rowHeights[i];
+
+                        let oBottom = bottom;
+                        let oRowHeight = rowHeight;
+
+                        for(let j = 0; j < children.length; j++){
+                            
+                            let left = this._columnLefts[j];
+                            let columnWidth = this._columnWidths[j];
+
+                            let child = children[j];
+
+                            if(child){
+
+                                let gd = <GridData>child.layoutData;
+
+                                if(gd.columnSpan > 1){
+                                    for(let k = j+1; k < gd.columnSpan + j && k < cl; k++){
+                                        columnWidth += this._columnWidths[k];
+                                    }
+                                }
+
+                                if(gd.rowSpan > 1){
+                                    
+                                    for(let k = i+1; k < gd.rowSpan + i && k < rl; k++){
+                                        rowHeight += this._rowHeights[k];
+                                        bottom = this._rowBottoms[k];
+                                    }
+                                    
+                                }
+
+                                layoutArea.width = columnWidth;
+                                layoutArea.height = rowHeight;                                
+
+                                child.margin.computeWithAlignment(layoutArea, child.actualSize, child.marginAlignment, child.actualScale, GridPanelLayoutEngine.dstOffset, GridPanelLayoutEngine.dstArea);
+                                child.layoutAreaPos = new BABYLON.Vector2(left + GridPanelLayoutEngine.dstOffset.x, bottom + GridPanelLayoutEngine.dstOffset.y);
+                                
+                                bottom = oBottom;
+                                rowHeight = oRowHeight;
+                                
+                            }
+
+                        }
+
+                    }
+                    
+                }
+
+                prim._clearFlags(SmartPropertyPrim.flagLayoutDirty);
+            }
+        }
+
+        get isChildPositionAllowed(): boolean {
+            return false;
+        }
+
+        private _getMaxChildHeightInRow(rowNum:number):number{
+
+            let rows = this._rows;
+            let cl = this._columns.length;
+            let rl = this._rows.length;
+            let children = this._children;
+            let row = rows[rowNum];
+            let maxHeight = 0;
+
+            if(children){
+
+                for(let i = 0; i < cl; i++){
+                    let child = children[rowNum][i];
+                    
+                    if(child){
+                        let span = (<GridData>child.layoutData).rowSpan;
+                        if(maxHeight < child.layoutArea.height/span){
+                            maxHeight = child.layoutArea.height/span;
+                        }
+                    }
+                }
+
+            }
+
+            return maxHeight;
+
+        }
+
+        private _getMaxChildWidthInColumn(colNum:number):number{
+
+            let columns = this._columns;
+            let cl = this._columns.length;
+            let rl = this._rows.length;
+            let children = this._children;
+            let column = columns[colNum];
+            let maxWidth = 0;
+
+            if(children){
+
+                for(let i = 0; i < rl; i++){
+                    let child = children[i][colNum];
+                    if(child){
+                        let span = (<GridData>child.layoutData).columnSpan;
+                        if(maxWidth < child.layoutArea.width/span){
+                            maxWidth = child.layoutArea.width/span;
+                        }
+                    }
+                }
+
+            }
+
+            return maxWidth;
+
+        }
+
+        private _updateGrid(prim:Prim2DBase){
+
+            let _children = this._children;
+
+            //remove prim.children from _children
+            for(let i = 0; i < _children.length; i++){
+                let children = _children[i];
+                if(children){
+                    children.length = 0;
+                }
+            }
+
+            let childrenThatSpan:Array<Prim2DBase>;
+
+            //add prim.children to _children
+            for(let child of prim.children){
+                
+                if(!child.layoutData){
+                    continue;
+                }
+
+                let gd = <GridData>child.layoutData;
+
+                if(!_children[gd.row]){
+                    _children[gd.row] = [];
+                }
+
+                if(gd.columnSpan == 1 && gd.rowSpan == 1){
+                    _children[gd.row][gd.column] = child;
+                }else{
+                    if(!childrenThatSpan){
+                        childrenThatSpan = [];
+                    }
+                    //when children span, we need to add them to _children whereever they span to so that 
+                    //_getMaxChildHeightInRow and _getMaxChildWidthInColumn will work correctly.
+                    childrenThatSpan.push(child);
+                    for(let i = gd.row; i < gd.row + gd.rowSpan; i++){
+                        for(let j = gd.column; j < gd.column + gd.columnSpan; j++){
+                            _children[i][j] = child;
+                        }
+                    }
+                }
+
+            }
+
+
+            let rows = this._rows;
+            let columns = this._columns;
+
+            let rl = this._rows.length;
+            let cl = this._columns.length;
+
+            //get fixed and auto row heights first
+
+            var starIndexes = [];
+            var totalStars = 0;
+            var rowHeights = 0;
+            let columnWidths = 0;
+
+            for (let i = 0; i < rl; i++) {
+
+                let row = this._rows[i];
+
+                if(row.heightType == GridDimensionDefinition.Auto){
+
+                    this._rowHeights[i] = this._getMaxChildHeightInRow(i);
+                    rowHeights += this._rowHeights[i];
+
+                }else if(row.heightType == GridDimensionDefinition.Pixels){
+
+                    this._rowHeights[i] = row.heightPixels;
+                    rowHeights += this._rowHeights[i];
+
+                }else if(row.heightType == GridDimensionDefinition.Stars){
+
+                    starIndexes.push(i);
+
+                    totalStars += row.height;
+
+                }
+
+            }
+
+            //star row heights
+
+            if(starIndexes.length > 0){
+
+                let remainingHeight = prim.contentArea.height - rowHeights;
+
+                for(let i = 0; i < starIndexes.length; i++){
+
+                    let rowIndex = starIndexes[i];
+
+                    this._rowHeights[rowIndex] = (this._rows[rowIndex].height / totalStars) * remainingHeight;
+
+                }
+            }
+
+
+            //get fixed and auto column widths
+
+            starIndexes.length = 0;
+            totalStars = 0;
+
+            for (let i = 0; i < cl; i++) {
+
+                let column = this._columns[i];
+
+                if(column.widthType == GridDimensionDefinition.Auto){
+
+                    this._columnWidths[i] = this._getMaxChildWidthInColumn(i);
+                    columnWidths += this._columnWidths[i];
+
+                }else if(column.widthType == GridDimensionDefinition.Pixels){
+
+                    this._columnWidths[i] = column.widthPixels;
+                    columnWidths += this._columnWidths[i];
+
+                }else if(column.widthType == GridDimensionDefinition.Stars){
+
+                    starIndexes.push(i);
+
+                    totalStars += column.width;
+
+                }
+
+            }
+
+            //star column widths
+
+            if(starIndexes.length > 0){
+
+                let remainingWidth = prim.contentArea.width - columnWidths;
+
+                for(let i = 0; i < starIndexes.length; i++){
+
+                    let columnIndex = starIndexes[i];
+
+                    this._columnWidths[columnIndex] = (this._columns[columnIndex].width / totalStars) * remainingWidth;
+
+                }
+            }
+
+
+            let y = 0;
+            this._rowBottoms[rl - 1] = y;
+
+            for (let i = rl - 2; i >= 0; i--) {
+
+                y += this._rowHeights[i+1];
+                this._rowBottoms[i] = y;
+
+            }
+
+            let x = 0;
+            this._columnLefts[0] = x;
+            
+            for (let i = 1; i < cl; i++) {
+
+                x += this._columnWidths[i-1];
+                this._columnLefts[i] = x;
+
+            }
+
+            //remove duplicate references to children that span
+            if(childrenThatSpan){
+                for(var i = 0; i < childrenThatSpan.length; i++){
+                    
+                    let child = childrenThatSpan[i];
+                    let gd = <GridData>child.layoutData;
+
+                    for(let i = gd.row; i < gd.row + gd.rowSpan; i++){
+                        for(let j = gd.column; j < gd.column + gd.columnSpan; j++){
+                            if(i == gd.row && j == gd.column){
+                                continue;
+                            }
+                            if(_children[i][j] == child){
+                                _children[i][j] = null;
+                            }
+                        }
+                    }
+                }
+            }
+
+        }
+
+    }
+
 }
 }

+ 39 - 4
canvas2D/src/Engine/babylon.ellipse2d.ts

@@ -38,7 +38,7 @@
                 engine.setDepthFunctionToLessOrEqual();
                 engine.setDepthFunctionToLessOrEqual();
             }
             }
 
 
-            let curAlphaMode = engine.getAlphaMode();
+            var curAlphaMode = engine.getAlphaMode();
 
 
             if (this.effectFill) {
             if (this.effectFill) {
                 let partIndex = instanceInfo.partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
                 let partIndex = instanceInfo.partIndexFromId.get(Shape2D.SHAPE2D_FILLPARTID.toString());
@@ -230,6 +230,9 @@
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -271,6 +274,9 @@
             isPickable            ?: boolean,
             isPickable            ?: boolean,
             isContainer           ?: boolean,
             isContainer           ?: boolean,
             childrenFlatZOrder    ?: boolean,
             childrenFlatZOrder    ?: boolean,
+            levelCollision        ?: boolean,
+            deepCollision         ?: boolean,
+            layoutData            ?: ILayoutData,
             marginTop             ?: number | string,
             marginTop             ?: number | string,
             marginLeft            ?: number | string,
             marginLeft            ?: number | string,
             marginRight           ?: number | string,
             marginRight           ?: number | string,
@@ -305,6 +311,35 @@
             this.subdivisions = sub;
             this.subdivisions = sub;
         }
         }
 
 
+        protected updateTriArray() {
+            let subDiv = this._subdivisions;
+            if (this._primTriArray == null) {
+                this._primTriArray = new Tri2DArray(subDiv);
+            } else {
+                this._primTriArray.clear(subDiv);
+            }
+
+            let size = this.actualSize;
+            let center = new Vector2(0.5 * size.width, 0.5 * size.height);
+            let v1 = Vector2.Zero();
+            let v2 = Vector2.Zero();
+
+            for (let i = 0; i < subDiv; i++) {
+	            let angle1 = Math.PI * 2 * (i-1) / subDiv;
+	            let angle2 = Math.PI * 2 * i / subDiv;
+
+                v1.x = ((Math.cos(angle1) / 2.0) + 0.5) * size.width;
+                v1.y = ((Math.sin(angle1) / 2.0) + 0.5) * size.height;
+
+                v2.x = ((Math.cos(angle2) / 2.0) + 0.5) * size.width;
+                v2.y = ((Math.sin(angle2) / 2.0) + 0.5) * size.height;
+
+                this._primTriArray.storeTriangle(i, center, v1, v2);
+
+            }
+
+        }
+
         protected createModelRenderCache(modelKey: string): ModelRenderCache {
         protected createModelRenderCache(modelKey: string): ModelRenderCache {
             let renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey);
             let renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey);
             return renderCache;
             return renderCache;
@@ -327,10 +362,10 @@
                 let ib = new Float32Array(triCount * 3);
                 let ib = new Float32Array(triCount * 3);
                 for (let i = 0; i < triCount; i++) {
                 for (let i = 0; i < triCount; i++) {
                     ib[i * 3 + 0] = 0;
                     ib[i * 3 + 0] = 0;
-                    ib[i * 3 + 2] = i + 1;
-                    ib[i * 3 + 1] = i + 2;
+                    ib[i * 3 + 2] = i + 2;
+                    ib[i * 3 + 1] = i + 1;
                 }
                 }
-                ib[triCount * 3 - 2] = 1;
+                ib[triCount * 3 - 1] = 1;
 
 
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIB = engine.createIndexBuffer(ib);
                 renderCache.fillIndicesCount = triCount * 3;
                 renderCache.fillIndicesCount = triCount * 3;

+ 2 - 0
canvas2D/src/Engine/babylon.fontTexture.ts

@@ -712,6 +712,8 @@
         // More info here: https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
         // More info here: https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
         private getFontHeight(font: string): {height: number, offset: number} {
         private getFontHeight(font: string): {height: number, offset: number} {
             var fontDraw = document.createElement("canvas");
             var fontDraw = document.createElement("canvas");
+            fontDraw.width = 600;
+            fontDraw.height = 600;
             var ctx = fontDraw.getContext('2d');
             var ctx = fontDraw.getContext('2d');
             ctx.fillRect(0, 0, fontDraw.width, fontDraw.height);
             ctx.fillRect(0, 0, fontDraw.width, fontDraw.height);
             ctx.textBaseline = 'top';
             ctx.textBaseline = 'top';

+ 39 - 2
canvas2D/src/Engine/babylon.group2d.ts

@@ -43,6 +43,8 @@
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - trackNode: if you want the ScreenSpaceCanvas to track the position of a given Scene Node, use this setting to specify the Node to track
+         * - trackNodeOffset: if you use trackNode you may want to specify a 3D Offset to apply to shift the Canvas
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
          * - origin: define the normalized origin point location, default [0.5;0.5]
@@ -53,6 +55,9 @@
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -80,6 +85,7 @@
             scaleY                  ?: number,
             scaleY                  ?: number,
             dontInheritParentScale  ?: boolean,
             dontInheritParentScale  ?: boolean,
             trackNode               ?: Node,
             trackNode               ?: Node,
+            trackNodeOffset         ?: Vector3,
             opacity                 ?: number,
             opacity                 ?: number,
             zOrder                  ?: number, 
             zOrder                  ?: number, 
             origin                  ?: Vector2,
             origin                  ?: Vector2,
@@ -92,6 +98,9 @@
             isPickable              ?: boolean,
             isPickable              ?: boolean,
             isContainer             ?: boolean,
             isContainer             ?: boolean,
             childrenFlatZOrder      ?: boolean,
             childrenFlatZOrder      ?: boolean,
+            levelCollision          ?: boolean,
+            deepCollision           ?: boolean,
+            layoutData              ?: ILayoutData,
             marginTop               ?: number | string,
             marginTop               ?: number | string,
             marginLeft              ?: number | string,
             marginLeft              ?: number | string,
             marginRight             ?: number | string,
             marginRight             ?: number | string,
@@ -118,6 +127,7 @@
             let size = (!settings.size && !settings.width && !settings.height) ? null : (settings.size || (new Size(settings.width || 0, settings.height || 0)));
             let size = (!settings.size && !settings.width && !settings.height) ? null : (settings.size || (new Size(settings.width || 0, settings.height || 0)));
 
 
             this._trackedNode = (settings.trackNode == null) ? null : settings.trackNode;
             this._trackedNode = (settings.trackNode == null) ? null : settings.trackNode;
+            this._trackedNodeOffset = (settings.trackNodeOffset == null) ? null : settings.trackNodeOffset;
             if (this._trackedNode && this.owner) {
             if (this._trackedNode && this.owner) {
                 this.owner._registerTrackedNode(this);
                 this.owner._registerTrackedNode(this);
             }
             }
@@ -247,7 +257,7 @@
          * BEWARE: if the Group is a RenderableGroup and its content is cache the texture will be resized each time the group is getting bigger. For performance reason the opposite won't be true: the texture won't shrink if the group does.
          * BEWARE: if the Group is a RenderableGroup and its content is cache the texture will be resized each time the group is getting bigger. For performance reason the opposite won't be true: the texture won't shrink if the group does.
          */
          */
         public set size(val: Size) {
         public set size(val: Size) {
-            this._size = val;
+            this.internalSetSize(val);
         }
         }
 
 
         public get viewportSize(): ISize {
         public get viewportSize(): ISize {
@@ -289,7 +299,11 @@
         }
         }
 
 
         public set actualSize(value: Size) {
         public set actualSize(value: Size) {
-            this._actualSize = value;
+            if (!this._actualSize) {
+                this._actualSize = value.clone();
+            } else {
+                this._actualSize.copyFrom(value);
+            }
         }
         }
 
 
 
 
@@ -335,6 +349,21 @@
             }
             }
         }
         }
 
 
+        /**
+         * Get/set the offset of the tracked node in the tracked node's local space.
+         */
+        public get trackedNodeOffset(): Vector3 {
+            return this._trackedNodeOffset;
+        }
+
+        public set trackedNodeOffset(val: Vector3) {
+            if (!this._trackedNodeOffset) {
+                this._trackedNodeOffset = val.clone();
+            } else {
+                this._trackedNodeOffset.copyFrom(val);
+            }
+        }
+
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
             // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
             // If we've made it so far it means the boundingInfo intersection test succeed, the Group2D is shaped the same, so we always return true
             return true;
             return true;
@@ -1015,7 +1044,15 @@
             }
             }
         }
         }
 
 
+        public get _cachedTexture(): MapTexture {
+            if (this._renderableData) {
+                return this._renderableData._cacheTexture;
+            }
+            return null;
+        }
+
         private _trackedNode: Node;
         private _trackedNode: Node;
+        private _trackedNodeOffset: Vector3;
         protected _isRenderableGroup: boolean;
         protected _isRenderableGroup: boolean;
         protected _isCachedGroup: boolean;
         protected _isCachedGroup: boolean;
         private _cacheGroupDirty: boolean;
         private _cacheGroupDirty: boolean;

+ 90 - 80
canvas2D/src/Engine/babylon.lines2d.ts

@@ -30,6 +30,7 @@
             }
             }
             let canvas = instanceInfo.owner.owner;
             let canvas = instanceInfo.owner.owner;
             var engine = canvas.engine;
             var engine = canvas.engine;
+            engine.setState(false, undefined, true);
 
 
             let depthFunction = 0;
             let depthFunction = 0;
             if (this.effectFill && this.effectBorder) {
             if (this.effectFill && this.effectBorder) {
@@ -223,7 +224,7 @@
 
 
         public set points(value: Vector2[]) {
         public set points(value: Vector2[]) {
             this._points = value;
             this._points = value;
-            this._contour = null;
+            this._primTriArrayDirty = true;
             this._boundingBoxDirty();
             this._boundingBoxDirty();
         }
         }
 
 
@@ -282,66 +283,10 @@
         private static _curB: Vector2 = Vector2.Zero();
         private static _curB: Vector2 = Vector2.Zero();
 
 
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
-            if (this._contour == null) {
-                this._computeLines2D();
-            }
-
-            let pl = this.points.length;
-            let l = this.closed ? pl + 1 : pl;
-
             let p = intersectInfo._localPickPosition;
             let p = intersectInfo._localPickPosition;
+            this.updateTriArray();
 
 
-            this.transformPointWithOriginToRef(this._contour[0], null, Lines2D._prevA);
-            this.transformPointWithOriginToRef(this._contour[1], null, Lines2D._prevB);
-            for (let i = 1; i < l; i++) {
-                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, Lines2D._prevA, Lines2D._prevB, Lines2D._curA)) {
-                    return true;
-                }
-                if (Vector2.PointInTriangle(p, Lines2D._curA, Lines2D._prevB, Lines2D._curB)) {
-                    return true;
-                }
-
-                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 (this._startCapTriIndices && capIntersect(this._startCapTriIndices, this._startCapContour)) {
-                    return true;
-                }
-                if (this._endCapTriIndices && capIntersect(this._endCapTriIndices, this._endCapContour)) {
-                    return true;
-                }
-            }
-
-            return false;
+            return this._primTriArray.doesContain(p);
         }
         }
 
 
         protected get boundingMin(): Vector2 {
         protected get boundingMin(): Vector2 {
@@ -370,6 +315,9 @@
         }
         }
 
 
         protected updateLevelBoundingInfo(): boolean {
         protected updateLevelBoundingInfo(): boolean {
+            if (!this._size) {
+                return false;
+            }
             if (!this._boundingMin) {
             if (!this._boundingMin) {
                 this._computeLines2D();
                 this._computeLines2D();
             }
             }
@@ -402,6 +350,9 @@
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -443,6 +394,9 @@
             isPickable            ?: boolean,
             isPickable            ?: boolean,
             isContainer           ?: boolean,
             isContainer           ?: boolean,
             childrenFlatZOrder    ?: boolean,
             childrenFlatZOrder    ?: boolean,
+            levelCollision        ?: boolean,
+            deepCollision         ?: boolean,
+            layoutData            ?: ILayoutData,
             marginTop             ?: number | string,
             marginTop             ?: number | string,
             marginLeft            ?: number | string,
             marginLeft            ?: number | string,
             marginRight           ?: number | string,
             marginRight           ?: number | string,
@@ -1084,6 +1038,12 @@
             return renderCache;
             return renderCache;
         }
         }
 
 
+        protected updateTriArray() {
+            if (this._primTriArrayDirty) {
+                this._computeLines2D();
+            }
+        }
+
         private _computeLines2D() {
         private _computeLines2D() {
             // 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._boundingMin = new Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
@@ -1165,23 +1125,80 @@
                 this._buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, Lines2D._endDir, endCapContour);
                 this._buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, Lines2D._endDir, endCapContour);
             }
             }
 
 
-            this._contour = contour;
+            let startCapTri: any[];
             if (startCapContour.length > 0) {
             if (startCapContour.length > 0) {
-                let startCapTri = Earcut.earcut(startCapContour, null, 2);
-                this._startCapTriIndices = startCapTri;
-                this._startCapContour = startCapContour;
-            } else {
-                this._startCapTriIndices = null;
-                this._startCapContour = null;
+                startCapTri = Earcut.earcut(startCapContour, null, 2);
             }
             }
+
+            let endCapTri: any[];
             if (endCapContour.length > 0) {
             if (endCapContour.length > 0) {
-                let endCapTri = Earcut.earcut(endCapContour, null, 2);
-                this._endCapContour = endCapContour;
-                this._endCapTriIndices = endCapTri;
-            } else {
-                this._endCapContour = null;
-                this._endCapTriIndices = null;
+                endCapTri = Earcut.earcut(endCapContour, null, 2);
             }
             }
+
+            // Build the Tri2DArray using the contour info from the shape and the caps (if any)
+            {
+                let pl = this.points.length;
+
+                // Count the number of needed triangles
+                let count = ((this.closed ? pl : (pl-1)) * 2) + (startCapTri != null ? ((startCapTri.length + endCapTri.length)/3) : 0);
+
+                // Init/Clear the TriArray
+                if (!this._primTriArray) {
+                    this._primTriArray = new Tri2DArray(count);
+                } else {
+                    this._primTriArray.clear(count);
+                }
+
+                let pta = this._primTriArray;
+                let l = this.closed ? pl + 1 : pl;
+
+                this.transformPointWithOriginToRef(contour[0], null, Lines2D._prevA);
+                this.transformPointWithOriginToRef(contour[1], null, Lines2D._prevB);
+                let si = 0;
+                for (let i = 1; i < l; i++) {
+                    this.transformPointWithOriginToRef(contour[(i % pl) * 2 + 0], null, Lines2D._curA);
+                    this.transformPointWithOriginToRef(contour[(i % pl) * 2 + 1], null, Lines2D._curB);
+
+                    pta.storeTriangle(si++, Lines2D._prevA, Lines2D._prevB, Lines2D._curA);
+                    pta.storeTriangle(si++, Lines2D._curA,  Lines2D._prevB, Lines2D._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);
+
+                        pta.storeTriangle(si++, Lines2D._prevA, Lines2D._prevB, Lines2D._curB);
+                    }
+                    return false;
+                }
+
+                if (startCapTri) {
+                    if (startCapTri) {
+                        capIntersect(startCapTri, startCapContour);
+                    }
+                    if (endCapTri) {
+                        capIntersect(endCapTri, endCapContour);
+                    }
+                }
+                this._primTriArrayDirty = false;
+            }
+
             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;
@@ -1246,18 +1263,11 @@
         private _borderIB: Float32Array;
         private _borderIB: Float32Array;
         private _boundingMin: Vector2;
         private _boundingMin: Vector2;
         private _boundingMax: Vector2;
         private _boundingMax: 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;
         private _endCap: number;
         private _endCap: number;
         private _fillThickness: number;
         private _fillThickness: number;
         private _points: Vector2[];
         private _points: Vector2[];
-
-
     }
     }
 }
 }

+ 4 - 0
canvas2D/src/Engine/babylon.modelRenderCache.ts

@@ -231,6 +231,10 @@
             return this._modelKey;
             return this._modelKey;
         }
         }
 
 
+        public updateModelRenderCache(prim: Prim2DBase): boolean {
+            return false;
+        }
+
         /**
         /**
          * Render the model instances
          * Render the model instances
          * @param instanceInfo
          * @param instanceInfo

+ 180 - 29
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1082,7 +1082,7 @@
          * @param dstOffset the position of the content, x, y, z, w are left, bottom, right, top
          * @param dstOffset the position of the content, x, y, z, w are left, bottom, right, top
          * @param dstArea the new size of the content
          * @param dstArea the new size of the content
          */
          */
-        public computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, dstOffset: Vector4, dstArea: Size, computeLayoutArea = false) {
+        public computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, contentScale: Vector2, dstOffset: Vector4, dstArea: Size, computeLayoutArea = false) {
             // Fetch some data
             // Fetch some data
             let topType = this._getType(0, true);
             let topType = this._getType(0, true);
             let leftType = this._getType(1, true);
             let leftType = this._getType(1, true);
@@ -1090,6 +1090,8 @@
             let bottomType = this._getType(3, true);
             let bottomType = this._getType(3, true);
             let hasWidth = contentSize && (contentSize.width != null);
             let hasWidth = contentSize && (contentSize.width != null);
             let hasHeight = contentSize && (contentSize.height != null);
             let hasHeight = contentSize && (contentSize.height != null);
+            let sx = contentScale.x;
+            let sy = contentScale.y;
             let width = hasWidth ? contentSize.width : 0;
             let width = hasWidth ? contentSize.width : 0;
             let height = hasHeight ? contentSize.height : 0;
             let height = hasHeight ? contentSize.height : 0;
             let isTopAuto = topType === PrimitiveThickness.Auto;
             let isTopAuto = topType === PrimitiveThickness.Auto;
@@ -1110,17 +1112,17 @@
                         if (computeLayoutArea) {
                         if (computeLayoutArea) {
                             dstArea.width += this.leftPixels;
                             dstArea.width += this.leftPixels;
                         }
                         }
-                        dstOffset.z = sourceArea.width - (dstOffset.x + width);
+                        dstOffset.z = sourceArea.width - (dstOffset.x + (width * sx));
                         break;
                         break;
 
 
                     }
                     }
                 case PrimitiveAlignment.AlignRight:
                 case PrimitiveAlignment.AlignRight:
                     {
                     {
                         if (isRightAuto) {
                         if (isRightAuto) {
-                            dstOffset.x = Math.round(sourceArea.width - width);
+                            dstOffset.x = Math.round(sourceArea.width - (width * sx));
                         } else {
                         } else {
                             this._computePixels(2, sourceArea, true);
                             this._computePixels(2, sourceArea, true);
-                            dstOffset.x = Math.round(sourceArea.width - (width + this.rightPixels));
+                            dstOffset.x = Math.round(sourceArea.width - ((width * sx) + this.rightPixels));
                         }
                         }
                         dstArea.width = width;
                         dstArea.width = width;
                         if (computeLayoutArea) {
                         if (computeLayoutArea) {
@@ -1157,9 +1159,9 @@
                         }
                         }
 
 
                         let offset = (isLeftAuto ? 0 : this.leftPixels) - (isRightAuto ? 0 : this.rightPixels);
                         let offset = (isLeftAuto ? 0 : this.leftPixels) - (isRightAuto ? 0 : this.rightPixels);
-                        dstOffset.x = Math.round(((sourceArea.width - width) / 2) + offset);
+                        dstOffset.x = Math.round(((sourceArea.width - (width*sx)) / 2) + offset);
                         dstArea.width = width;
                         dstArea.width = width;
-                        dstOffset.z = sourceArea.width - (dstOffset.x + width);
+                        dstOffset.z = sourceArea.width - (dstOffset.x + (width*sx));
                         break;
                         break;
                     }
                     }
             }
             }
@@ -1168,10 +1170,10 @@
                 case PrimitiveAlignment.AlignTop:
                 case PrimitiveAlignment.AlignTop:
                     {
                     {
                         if (isTopAuto) {
                         if (isTopAuto) {
-                            dstOffset.y = sourceArea.height - height;
+                            dstOffset.y = sourceArea.height - (height * sy);
                         } else {
                         } else {
                             this._computePixels(0, sourceArea, true);
                             this._computePixels(0, sourceArea, true);
-                            dstOffset.y = Math.round(sourceArea.height - (height + this.topPixels));
+                            dstOffset.y = Math.round(sourceArea.height - ((height * sy) + this.topPixels));
                         }
                         }
                         dstArea.height = height;
                         dstArea.height = height;
                         if (computeLayoutArea) {
                         if (computeLayoutArea) {
@@ -1193,7 +1195,7 @@
                         if (computeLayoutArea) {
                         if (computeLayoutArea) {
                             dstArea.height += this.bottomPixels;
                             dstArea.height += this.bottomPixels;
                         }
                         }
-                        dstOffset.w = sourceArea.height - (dstOffset.y + height);
+                        dstOffset.w = sourceArea.height - (dstOffset.y + (height * sy));
                         break;
                         break;
 
 
                     }
                     }
@@ -1225,9 +1227,9 @@
                         }
                         }
 
 
                         let offset = (isBottomAuto ? 0 : this.bottomPixels) - (isTopAuto ? 0 : this.topPixels);
                         let offset = (isBottomAuto ? 0 : this.bottomPixels) - (isTopAuto ? 0 : this.topPixels);
-                        dstOffset.y = Math.round(((sourceArea.height - height) / 2) + offset);
+                        dstOffset.y = Math.round(((sourceArea.height - (height * sy)) / 2) + offset);
                         dstArea.height = height;
                         dstArea.height = height;
-                        dstOffset.w = sourceArea.height - (dstOffset.y + height);
+                        dstOffset.w = sourceArea.height - (dstOffset.y + (height * sy));
                         break;
                         break;
                     }
                     }
             }
             }
@@ -1383,6 +1385,9 @@
             isPickable              ?: boolean,
             isPickable              ?: boolean,
             isContainer             ?: boolean,
             isContainer             ?: boolean,
             childrenFlatZOrder      ?: boolean,
             childrenFlatZOrder      ?: boolean,
+            levelCollision          ?: boolean,
+            deepCollision           ?: boolean,
+            layoutData              ?: ILayoutData,
             marginTop               ?: number | string,
             marginTop               ?: number | string,
             marginLeft              ?: number | string,
             marginLeft              ?: number | string,
             marginRight             ?: number | string,
             marginRight             ?: number | string,
@@ -1443,7 +1448,6 @@
             this._lastAutoSizeArea = Size.Zero();
             this._lastAutoSizeArea = Size.Zero();
             this._contentArea = new Size(null, null);
             this._contentArea = new Size(null, null);
             this._pointerEventObservable = new Observable<PrimitivePointerInfo>();
             this._pointerEventObservable = new Observable<PrimitivePointerInfo>();
-            this._boundingInfo = new BoundingInfo2D();
             this._owner = owner;
             this._owner = owner;
             this._parent = null;
             this._parent = null;
             this._margin = null;
             this._margin = null;
@@ -1466,6 +1470,11 @@
             this._actualScale = Vector2.Zero();
             this._actualScale = Vector2.Zero();
             this._displayDebugAreas = false;
             this._displayDebugAreas = false;
             this._debugAreaGroup = null;
             this._debugAreaGroup = null;
+            this._primTriArray = null;
+            this._primTriArrayDirty = true;
+
+            this._levelBoundingInfo.worldMatrixAccess = () => this.globalTransform;
+            this._boundingInfo.worldMatrixAccess = () => this.globalTransform;
 
 
             let isPickable = true;
             let isPickable = true;
             let isContainer = true;
             let isContainer = true;
@@ -1612,9 +1621,44 @@
                 this.padding.fromString(settings.padding);
                 this.padding.fromString(settings.padding);
             }
             }
 
 
+            if (settings.layoutData) {
+                this.layoutData = settings.layoutData;
+            }
+
             // Dirty layout and positioning
             // Dirty layout and positioning
             this._parentLayoutDirty();
             this._parentLayoutDirty();
             this._positioningDirty();
             this._positioningDirty();
+
+            // Add in the PCM
+            if (settings.levelCollision || settings.deepCollision) {
+                this._actorInfo = this.owner._primitiveCollisionManager._addActor(this, settings.deepCollision === true);
+                this._setFlags(SmartPropertyPrim.flagCollisionActor);
+            } else {
+                this._actorInfo = null;
+            }
+
+        }
+
+        /**
+         * Return the ChangedDictionary observable of the StringDictionary containing the primitives intersecting with this one
+         */
+        public get intersectWithObservable(): Observable<DictionaryChanged<ActorInfoBase>> {
+            if (!this._actorInfo) {
+                return null;
+            }
+            return this._actorInfo.intersectWith.dictionaryChanged;
+        }
+
+        /**
+         * Return the ObservableStringDictionary containing all the primitives intersecting with this one.
+         * The key is the primitive uid, the value is the ActorInfo object
+         * @returns {} 
+         */
+        public get intersectWith(): ObservableStringDictionary<ActorInfoBase> {
+            if (!this._actorInfo) {
+                return null;
+            }
+            return this._actorInfo.intersectWith;
         }
         }
 
 
         public get actionManager(): ActionManager {
         public get actionManager(): ActionManager {
@@ -1824,7 +1868,11 @@
          * DO NOT INVOKE for internal purpose only
          * DO NOT INVOKE for internal purpose only
          */
          */
         public set actualPosition(val: Vector2) {
         public set actualPosition(val: Vector2) {
-            this._actualPosition = val;
+            if (!this._actualPosition) {
+                this._actualPosition = val.clone();
+            } else {
+                this._actualPosition.copyFrom(val);
+            }
         }
         }
 
 
         /**
         /**
@@ -1871,7 +1919,11 @@
             if (!this._checkPositionChange()) {
             if (!this._checkPositionChange()) {
                 return;
                 return;
             }
             }
-            this._position = value;
+            if (!this._position) {
+                this._position = value.clone();
+            } else {
+                this._position.copyFrom(value);
+            }
             this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
             this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
         }
         }
 
 
@@ -1971,7 +2023,13 @@
         }
         }
 
 
         protected internalSetSize(value: Size) {
         protected internalSetSize(value: Size) {
-            this._size = value;
+            if (!this._size) {
+                this._size = (value != null) ? value.clone() : null;
+            } else {
+                this._size.copyFrom(value);
+            }
+            this._actualSize = null;
+            this._positioningDirty();
         }
         }
 
 
         /**
         /**
@@ -1997,6 +2055,7 @@
                 this.size.width = value;
                 this.size.width = value;
             }
             }
 
 
+            this._actualSize = null;
             this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
             this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
             this._positioningDirty();
             this._positioningDirty();
         }
         }
@@ -2024,6 +2083,7 @@
                 this.size.height = value;
                 this.size.height = value;
             }
             }
 
 
+            this._actualSize = null;
             this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
             this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
             this._positioningDirty();
             this._positioningDirty();
         }
         }
@@ -2073,7 +2133,11 @@
                 return;
                 return;
             }
             }
 
 
-            this._actualSize = value;
+            if (!this._actualSize) {
+                this._actualSize = value.clone();
+            } else {
+                this._actualSize.copyFrom(value);
+            }
         }
         }
 
 
         /**
         /**
@@ -2126,7 +2190,11 @@
                 return;
                 return;
             }
             }
 
 
-            this._minSize = value;
+            if (!this._minSize) {
+                this._minSize = value.clone();
+            } else {
+                this._minSize.copyFrom(value);
+            }
             this._parentLayoutDirty();
             this._parentLayoutDirty();
         }
         }
 
 
@@ -2144,7 +2212,11 @@
                 return;
                 return;
             }
             }
 
 
-            this._maxSize = value;
+            if (!this._maxSize) {
+                this._maxSize = value.clone();
+            } else {
+                this._maxSize.copyFrom(value);
+            }
             this._parentLayoutDirty();
             this._parentLayoutDirty();
         }
         }
 
 
@@ -2163,7 +2235,11 @@
         }
         }
 
 
         public set origin(value: Vector2) {
         public set origin(value: Vector2) {
-            this._origin = value;
+            if (!this._origin) {
+                this._origin = value.clone();
+            } else {
+                this._origin.copyFrom(value);
+            }
         }
         }
 
 
         @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 15, pi => Prim2DBase.levelVisibleProperty = pi)
         @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 15, pi => Prim2DBase.levelVisibleProperty = pi)
@@ -2440,7 +2516,7 @@
             if (this.parent) {
             if (this.parent) {
                 this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty | SmartPropertyPrim.flagGlobalTransformDirty);
                 this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty | SmartPropertyPrim.flagGlobalTransformDirty);
             }
             }
-            this._layoutArea = val;
+            this._layoutArea.copyFrom(val);
         }
         }
 
 
         /**
         /**
@@ -2462,7 +2538,11 @@
                 this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty | SmartPropertyPrim.flagGlobalTransformDirty);
                 this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty | SmartPropertyPrim.flagGlobalTransformDirty);
             }
             }
             this._positioningDirty();
             this._positioningDirty();
-            this._layoutAreaPos = val;
+            if (!this._layoutAreaPos) {
+                this._layoutAreaPos = val.clone();
+            } else {
+                this._layoutAreaPos.copyFrom(val);
+            }
         }
         }
 
 
         /**
         /**
@@ -2508,7 +2588,9 @@
          * Get the global transformation matrix of the primitive
          * Get the global transformation matrix of the primitive
          */
          */
         public get globalTransform(): Matrix {
         public get globalTransform(): Matrix {
-            this._updateLocalTransform();
+            if (this._globalTransformProcessStep !== this.owner._globalTransformProcessStep) {
+                this.updateCachedStates(false);
+            }
             return this._globalTransform;
             return this._globalTransform;
         }
         }
 
 
@@ -2759,7 +2841,13 @@
             this._displayDebugAreas = value;
             this._displayDebugAreas = value;
         }
         }
 
 
+        private static _updatingDebugArea = false;
         private _updateDebugArea() {
         private _updateDebugArea() {
+            if (Prim2DBase._updatingDebugArea === true) {
+                return;
+            }
+            Prim2DBase._updatingDebugArea = true;
+
             let areaNames = ["Layout", "Margin", "Padding", "Content"];
             let areaNames = ["Layout", "Margin", "Padding", "Content"];
             let areaZones = ["Area", "Frame", "Top", "Left", "Right", "Bottom"];
             let areaZones = ["Area", "Frame", "Top", "Left", "Right", "Bottom"];
 
 
@@ -2927,6 +3015,8 @@
                     ++curAreaIndex;
                     ++curAreaIndex;
                 }
                 }
             }
             }
+
+            Prim2DBase._updatingDebugArea = false;
         }
         }
 
 
         public findById(id: string): Prim2DBase {
         public findById(id: string): Prim2DBase {
@@ -2968,12 +3058,12 @@
             return this.owner._releasePointerCapture(pointerId, this);
             return this.owner._releasePointerCapture(pointerId, this);
         }
         }
 
 
+        private static _bypassGroup2DExclusion = false;
+
         /**
         /**
          * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
          * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
          * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
          * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
          */
          */
-        private static _bypassGroup2DExclusion = false;
-
         public intersect(intersectInfo: IntersectInfo2D): boolean {
         public intersect(intersectInfo: IntersectInfo2D): boolean {
             if (!intersectInfo) {
             if (!intersectInfo) {
                 return false;
                 return false;
@@ -3073,6 +3163,38 @@
             return intersectInfo.isIntersected;
             return intersectInfo.isIntersected;
         }
         }
 
 
+        public intersectOtherPrim(other: Prim2DBase): boolean {
+            let setA = this.triList;
+            let setB = other.triList;
+
+            return Tri2DArray.doesIntersect(setA, setB, other.globalTransform.multiply(this.globalTransform.clone().invert()));
+        }
+
+        public get triList(): Tri2DArray {
+            if (this._primTriArrayDirty) {
+                this.updateTriArray();
+                this._primTriArrayDirty = false;
+            }
+            return this._primTriArray;
+        }
+
+        // This is the worst implementation, if the top level primitive doesn't override this method we will just store a quad that defines the bounding rect of the prim
+        protected updateTriArray() {
+            if (this._primTriArray == null) {
+                this._primTriArray = new Tri2DArray(2);
+            } else {
+                this._primTriArray.clear(2);
+            }
+
+            let size = this.actualSize;
+            let lb = new Vector2(0, 0);
+            let rt = new Vector2(size.width, size.height);
+            let lt = new Vector2(0, size.height);
+            let rb = new Vector2(size.width, 0);
+            this._primTriArray.storeTriangle(0, lb, lt, rt);
+            this._primTriArray.storeTriangle(1, lb, rt, rb);
+        }
+
         /**
         /**
          * Move a child object into a new position regarding its siblings to change its rendering order.
          * Move a child object into a new position regarding its siblings to change its rendering order.
          * You can also use the shortcut methods to move top/bottom: moveChildToTop, moveChildToBottom, moveToTop, moveToBottom.
          * You can also use the shortcut methods to move top/bottom: moveChildToTop, moveChildToBottom, moveToTop, moveToBottom.
@@ -3155,6 +3277,11 @@
                 return false;
                 return false;
             }
             }
 
 
+            if (this._isFlagSet(SmartPropertyPrim.flagCollisionActor)) {
+                this.owner._primitiveCollisionManager._removeActor(this);
+                this._actorInfo = null;
+            }
+
             if (this._pointerEventObservable) {
             if (this._pointerEventObservable) {
                 this._pointerEventObservable.clear();
                 this._pointerEventObservable.clear();
                 this._pointerEventObservable = null;
                 this._pointerEventObservable = null;
@@ -3207,7 +3334,7 @@
         }
         }
 
 
         public _needPrepare(): boolean {
         public _needPrepare(): boolean {
-            return this._areSomeFlagsSet(SmartPropertyPrim.flagVisibilityChanged | SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagNeedRefresh) || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
+            return this._areSomeFlagsSet(SmartPropertyPrim.flagVisibilityChanged | SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagModelUpdate | SmartPropertyPrim.flagNeedRefresh) || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         }
         }
 
 
         public _prepareRender(context: PrepareRender2DContext) {
         public _prepareRender(context: PrepareRender2DContext) {
@@ -3455,6 +3582,9 @@
 
 
                     this._invGlobalTransform = Matrix.Invert(this._globalTransform);
                     this._invGlobalTransform = Matrix.Invert(this._globalTransform);
 
 
+                    this._levelBoundingInfo.dirtyWorldAABB();
+                    this._boundingInfo.dirtyWorldAABB();
+
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._globalTransformStep = this.owner._globalTransformProcessStep + 1;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
                     this._parentTransformStep = this._parent ? this._parent._globalTransformStep : 0;
                     this._clearFlags(SmartPropertyPrim.flagGlobalTransformDirty);
                     this._clearFlags(SmartPropertyPrim.flagGlobalTransformDirty);
@@ -3499,8 +3629,8 @@
 
 
             // Apply margin
             // Apply margin
             if (this._hasMargin) {
             if (this._hasMargin) {
-                this.margin.computeWithAlignment(this.layoutArea, this.size || this.actualSize, this.marginAlignment, this._marginOffset, Prim2DBase._size);
-                this.actualSize = Prim2DBase._size.clone();
+                let contentSize = this.size || this.actualSize;
+                this.margin.computeWithAlignment(this.layoutArea, contentSize, this.marginAlignment, this.actualScale, this._marginOffset, Prim2DBase._size);
             }
             }
 
 
             if (this._hasPadding) {
             if (this._hasPadding) {
@@ -3508,7 +3638,7 @@
                 if (isSizeAuto) {
                 if (isSizeAuto) {
                     // Changing the padding has resize the prim, which forces us to recompute margin again
                     // Changing the padding has resize the prim, which forces us to recompute margin again
                     if (this._hasMargin) {
                     if (this._hasMargin) {
-                        this.margin.computeWithAlignment(this.layoutArea, Prim2DBase._size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
+                        this.margin.computeWithAlignment(this.layoutArea, Prim2DBase._size, this.marginAlignment, this.actualScale, this._marginOffset, Prim2DBase._size);
                     }
                     }
 
 
                 } else {
                 } else {
@@ -3558,7 +3688,7 @@
                 }
                 }
 
 
                 if (!this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
                 if (!this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
-                    return this._boundingSize;
+                    return this._contentArea;
                 }
                 }
 
 
                 Prim2DBase.boundinbBoxReentrency = true;
                 Prim2DBase.boundinbBoxReentrency = true;
@@ -3798,6 +3928,21 @@
             newPrimSize.copyFrom(primSize);
             newPrimSize.copyFrom(primSize);
         }
         }
 
 
+        /**
+         * Get/set the layout data to use for this primitive.
+         */
+        public get layoutData(): ILayoutData {
+            return this._layoutData;
+        }
+
+        public set layoutData(value: ILayoutData) {
+            if (this._layoutData === value) {
+                return;
+            }
+
+            this._layoutData = value;
+        }
+
         private _owner: Canvas2D;
         private _owner: Canvas2D;
         private _parent: Prim2DBase;
         private _parent: Prim2DBase;
         private _actionManager: ActionManager;
         private _actionManager: ActionManager;
@@ -3831,6 +3976,7 @@
         private _lastAutoSizeArea: Size;
         private _lastAutoSizeArea: Size;
         private _layoutAreaPos: Vector2;
         private _layoutAreaPos: Vector2;
         private _layoutArea: Size;
         private _layoutArea: Size;
+        private _layoutData: ILayoutData;
         private _contentArea: Size;
         private _contentArea: Size;
         private _rotation: number;
         private _rotation: number;
         private _scale: Vector2;
         private _scale: Vector2;
@@ -3840,6 +3986,7 @@
         private _actualScale : Vector2;
         private _actualScale : Vector2;
         private _displayDebugAreas: boolean;
         private _displayDebugAreas: boolean;
         private _debugAreaGroup: Group2D;
         private _debugAreaGroup: Group2D;
+        private _actorInfo: ActorInfoBase;
 
 
         // Stores the step of the parent for which the current global transform was computed
         // Stores the step of the parent for which the current global transform was computed
         // If the parent has a new step, it means this prim's global transform must be updated
         // If the parent has a new step, it means this prim's global transform must be updated
@@ -3854,6 +4001,10 @@
         protected _localTransform: Matrix;
         protected _localTransform: Matrix;
         protected _globalTransform: Matrix;
         protected _globalTransform: Matrix;
         protected _invGlobalTransform: Matrix;
         protected _invGlobalTransform: Matrix;
+
+        // Intersection related data
+        protected _primTriArrayDirty: boolean;
+        protected _primTriArray: Tri2DArray;
     }
     }
 
 
 }
 }

+ 730 - 0
canvas2D/src/Engine/babylon.primitiveCollisionManager.ts

@@ -0,0 +1,730 @@
+module BABYLON {
+
+    /**
+     * The base class for all implementation of a Primitive Collision Manager
+     */
+    export abstract class PrimitiveCollisionManagerBase {
+        constructor(owner: Canvas2D) {
+            this._owner = owner;
+        }
+
+        abstract _addActor(actor: Prim2DBase, deep: boolean): ActorInfoBase;
+        abstract _removeActor(actor: Prim2DBase);
+
+        abstract _update();
+
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the left border
+         */
+        abstract get leftBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase>;
+
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the bottom border
+         */
+        abstract get bottomBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase>;
+
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the right border
+         */
+        abstract get rightBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase>;
+
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the top border
+         */
+        abstract get topBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase>;
+
+        /**
+         * This dictionary contains all the couple of intersecting primitives
+         */
+        abstract get intersectedActors(): ObservableStringDictionary<{ a: Prim2DBase, b: Prim2DBase }>;
+
+        /**
+         * Renders the World AABB of all Actors
+         */
+        abstract get debugRenderAABB(): boolean;
+        abstract set debugRenderAABB(val: boolean);
+
+        /**
+         * Renders the area of the Clusters
+         */
+        abstract get debugRenderClusters(): boolean;
+        abstract set debugRenderClusters(val: boolean);
+
+        /**
+         * Display stats about the PCM on screen
+         */
+        abstract get debugStats(): boolean;
+        abstract set debugStats(val: boolean);
+
+        public static allocBasicPCM(owner: Canvas2D, enableBorders: boolean): PrimitiveCollisionManagerBase {
+            return new BasicPrimitiveCollisionManager(owner, enableBorders);
+        }
+
+        protected _owner: Canvas2D;
+    }
+
+    /**
+     * Base class of an Actor
+     */
+    export abstract class ActorInfoBase {
+        /**
+         * Access the World AABB of the Actor, the vector4 is x:left, y: bottom, z: right, w: top
+         */
+        abstract get worldAABB(): Vector4;
+
+        /**
+         * Return true if the actor is enable, false otherwise
+         */
+        abstract get isEnabled(): boolean;
+
+        /**
+         * Return true is the actor boundingInfo is use, false if its levelBoundingInfo is used.
+         */
+        abstract get isDeep(): boolean;
+
+        /**
+         * Return the primitive of the actor
+         */
+        abstract get prim(): Prim2DBase;
+
+        /**
+         * Return a dictionary containing all the actors intersecting with this one
+         */
+        abstract get intersectWith(): ObservableStringDictionary<ActorInfoBase>;
+    }
+
+    class ActorInfo extends ActorInfoBase {
+        constructor(owner: BasicPrimitiveCollisionManager, actor: Prim2DBase, deep: boolean) {
+            super();
+            this.owner = owner;
+            this.prim = actor;
+            this.flags = 0;
+            this.presentInClusters = new StringDictionary<ClusterInfo>();
+            this.intersectWith = new ObservableStringDictionary<ActorInfo>(false);
+            this.setFlags((deep ? ActorInfo.flagDeep : 0) | ActorInfo.flagDirty);
+
+            let bi = (deep ? actor.boundingInfo : actor.levelBoundingInfo);
+
+            // Dirty Actor if its WorldAABB changed
+            bi.worldAABBDirtyObservable.add((e, d) => {
+                this.owner.actorDirty(this);
+            });
+
+            // Dirty Actor if it's getting enabled/disabled
+            actor.propertyChanged.add((e, d) => {
+                if (d.mask === -1) {
+                    return;
+                }
+                this.setFlagsValue(ActorInfo.flagEnabled, e.newValue === true);
+                this.owner.actorDirty(this);
+            }, Prim2DBase.isVisibleProperty.flagId);
+        }
+
+        setFlags(flags: number) {
+            this.flags |= flags;
+        }
+
+        clearFlags(flags: number) {
+            this.flags &= ~flags;
+        }
+
+        isAllFlagsSet(flags: number) {
+            return (this.flags & flags) === flags;
+        }
+
+        isSomeFlagsSet(flags: number) {
+            return (this.flags & flags) !== 0;
+        }
+
+        setFlagsValue(flags: number, value: boolean) {
+            if (value) {
+                this.flags |= flags;
+            } else {
+                this.flags &= ~flags;
+            }
+        }
+
+        get worldAABB(): Vector4 {
+            return (this.isSomeFlagsSet(ActorInfo.flagDeep) ? this.prim.boundingInfo : this.prim.levelBoundingInfo).worldAABB;
+        }
+
+        get isEnabled(): boolean {
+            return this.isSomeFlagsSet(ActorInfo.flagEnabled);
+        }
+
+        get isDeep(): boolean {
+            return this.isSomeFlagsSet(ActorInfo.flagDeep);
+        }
+
+        get isDirty(): boolean {
+            return this.isSomeFlagsSet(ActorInfo.flagDirty);
+        }
+
+        get isRemoved(): boolean {
+            return this.isSomeFlagsSet(ActorInfo.flagRemoved);
+        }
+
+        prim: Prim2DBase;
+        flags: number;
+        owner: BasicPrimitiveCollisionManager;
+        presentInClusters: StringDictionary<ClusterInfo>;
+        intersectWith: ObservableStringDictionary<ActorInfoBase>;
+
+        public static flagDeep       = 0x0001;      // set if the actor boundingInfo must be used instead of the levelBoundingInfo
+        public static flagEnabled    = 0x0002;      // set if the actor is enabled and should be considered for intersection tests
+        public static flagDirty      = 0x0004;      // set if the actor's AABB is dirty
+        public static flagRemoved    = 0x0008;      // set if the actor was removed from the PCM
+    }
+
+    class ClusterInfo {
+        constructor() {
+            this.actors = new StringDictionary<ActorInfo>();
+        }
+
+        clear() {
+            this.actors.clear();
+        }
+
+        actors: StringDictionary<ActorInfo>;
+    }
+
+    class BasicPrimitiveCollisionManager extends PrimitiveCollisionManagerBase {
+
+        constructor(owner: Canvas2D, enableBorders: boolean) {
+            super(owner);
+            this._actors = new StringDictionary<ActorInfo>();
+            this._dirtyActors = new StringDictionary<ActorInfo>();
+            this._clusters = null;
+            this._maxActorByCluster = 0;
+            this._AABBRenderPrim = null;
+            this._canvasSize = Size.Zero();
+            this._ClusterRenderPrim = null;
+            this._debugTextBackground = null;
+            this._clusterDirty = true;
+            this._clusterSize = new Size(2, 2);
+            this._clusterStep = Vector2.Zero();
+            this._lastClusterResizeCounter = 0;
+            this._freeClusters = new Array<ClusterInfo>();
+            this._enableBorder = enableBorders;
+            this._debugUpdateOpCount = new PerfCounter();
+            this._debugUpdateTime = new PerfCounter();
+            this._intersectedActors = new ObservableStringDictionary<{ a: Prim2DBase; b: Prim2DBase }>(false);
+            this._borderIntersecteddActors = new Array<ObservableStringDictionary<Prim2DBase>>(4);
+            for (let j = 0; j < 4; j++) {
+                this._borderIntersecteddActors[j] = new ObservableStringDictionary<Prim2DBase>(false);
+            }
+            let flagId = Canvas2D.actualSizeProperty.flagId;
+
+            if (!BasicPrimitiveCollisionManager.WAABBCorners) {
+                BasicPrimitiveCollisionManager.WAABBCorners = new Array<Vector2>(4);
+                for (let i = 0; i < 4; i++) {
+                    BasicPrimitiveCollisionManager.WAABBCorners[i] = Vector2.Zero();
+                }
+                BasicPrimitiveCollisionManager.WAABBCornersCluster = new Array<Vector2>(4);
+                for (let i = 0; i < 4; i++) {
+                    BasicPrimitiveCollisionManager.WAABBCornersCluster[i] = Vector2.Zero();
+                }
+            }
+
+            owner.propertyChanged.add((e: PropertyChangedInfo, d) => {
+                if (d.mask === -1) {
+                    return;
+                }
+                this._clusterDirty = true;
+                console.log("canvas size changed");
+            }, flagId);
+
+            this.debugRenderAABB = false;
+            this.debugRenderClusters = false;
+            this.debugStats = false;
+        }
+
+        _addActor(actor: Prim2DBase, deep: boolean): ActorInfoBase {
+            return this._actors.getOrAddWithFactory(actor.uid, () => {
+                let ai = new ActorInfo(this, actor, deep);
+                this.actorDirty(ai);
+                return ai;
+            });
+        }
+
+        _removeActor(actor: Prim2DBase) {
+            let ai = this._actors.getAndRemove(actor.uid);
+            ai.setFlags(ActorInfo.flagRemoved);
+            this.actorDirty(ai);
+        }
+
+        actorDirty(actor: ActorInfo) {
+            actor.setFlags(ActorInfo.flagDirty);
+            this._dirtyActors.add(actor.prim.uid, actor);
+        }
+
+        _update() {
+            this._canvasSize.copyFrom(this._owner.actualSize);
+
+            // Should we update the WireFrame2D Primitive that displays the WorldAABB ?
+            if (this.debugRenderAABB) {
+                if (this._dirtyActors.count > 0 || this._debugRenderAABBDirty) {
+                    this._updateAABBDisplay();
+                }
+            }
+            if (this._AABBRenderPrim) {
+                this._AABBRenderPrim.levelVisible = this.debugRenderAABB;
+            }
+
+            let cw = this._clusterSize.width;
+            let ch = this._clusterSize.height;
+
+            // Check for Cluster resize
+            if (((this._clusterSize.width < 16 && this._clusterSize.height < 16 && this._maxActorByCluster >= 10) ||
+                 (this._clusterSize.width > 2 && this._clusterSize.height > 2 && this._maxActorByCluster <= 7)) &&
+                this._lastClusterResizeCounter > 100) {
+
+                if (this._maxActorByCluster >= 10) {
+                    ++cw;
+                    ++ch;
+                } else {
+                    --cw;
+                    --ch;
+                }
+                console.log(`Change cluster size to ${cw}:${ch}, max actor ${this._maxActorByCluster}`);
+                this._clusterDirty = true;
+            }
+
+            // Should we update the WireFrame2D Primitive that displays the clusters
+            if (this.debugRenderClusters && this._clusterDirty) {
+                this._updateClusterDisplay(cw, ch);
+            }
+            if (this._ClusterRenderPrim) {
+                this._ClusterRenderPrim.levelVisible = this.debugRenderClusters;
+            }
+
+
+            let updateStats = this.debugStats && (this._dirtyActors.count > 0 || this._clusterDirty);
+
+            this._debugUpdateTime.beginMonitoring();
+
+            // If the Cluster Size changed: rebuild it and add all actors. Otherwise add only new (dirty) actors
+            if (this._clusterDirty) {
+                this._initializeCluster(cw, ch);
+                this._rebuildAllActors();
+            } else {
+                this._rebuildDirtyActors();
+                ++this._lastClusterResizeCounter;
+            }
+
+            // Proceed to the collision detection between primitives
+            this._collisionDetection();
+
+            this._debugUpdateTime.endMonitoring();
+
+            if (updateStats) {
+                this._updateDebugStats();
+            }
+
+            if (this._debugTextBackground) {
+                this._debugTextBackground.levelVisible = updateStats;
+            }
+
+            // Reset the dirty actor list: everything is processed
+            this._dirtyActors.clear();
+        }
+
+        /**
+         * Renders the World AABB of all Actors
+         */
+        public get debugRenderAABB(): boolean {
+            return this._debugRenderAABB;
+        }
+
+        public set debugRenderAABB(val: boolean) {
+            if (this._debugRenderAABB === val) {
+                return;
+            }
+
+            this._debugRenderAABB = val;
+            this._debugRenderAABBDirty = true;
+        }
+
+        /**
+         * Renders the area of the Clusters
+         */
+        public debugRenderClusters: boolean;
+
+        /**
+         * Display stats about the PCM on screen
+         */
+        public debugStats: boolean;
+
+        get intersectedActors(): ObservableStringDictionary<{ a: Prim2DBase; b: Prim2DBase }> {
+            return this._intersectedActors;
+        }
+
+        get leftBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase> {
+            return this._borderIntersecteddActors[0];
+        }
+
+        get bottomBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase> {
+            return this._borderIntersecteddActors[1];
+        }
+
+        get rightBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase> {
+            return this._borderIntersecteddActors[2];
+        }
+
+        get topBorderIntersectedActors(): ObservableStringDictionary<Prim2DBase> {
+            return this._borderIntersecteddActors[3];
+        }
+
+        private _initializeCluster(countW: number, countH: number) {
+            // Check for free
+            if (this._clusters) {
+                for (let w = 0; w < this._clusterSize.height; w++) {
+                    for (let h = 0; h < this._clusterSize.width; h++) {
+                        this._freeClusterInfo(this._clusters[w][h]);
+                    }
+                }
+            }
+
+            // Allocate
+            this._clusterSize.copyFromFloats(countW, countH);
+            this._clusters = [];
+            for (let w = 0; w < this._clusterSize.height; w++) {
+                this._clusters[w] = [];
+                for (let h = 0; h < this._clusterSize.width; h++) {
+                    let ci = this._allocClusterInfo();
+                    this._clusters[w][h] = ci;
+                }
+            }
+
+            this._clusterStep.copyFromFloats(this._owner.actualWidth / countW, this._owner.actualHeight / countH);
+            this._maxActorByCluster = 0;
+            this._lastClusterResizeCounter = 0;
+
+            this._clusterDirty = false;
+        }
+
+        private _rebuildAllActors() {
+            this._actors.forEach((k, ai) => {
+                this._processActor(ai);
+            });
+        }
+
+        private _rebuildDirtyActors() {
+            this._dirtyActors.forEach((k, ai) => {
+                this._processActor(ai);
+            });
+        }
+
+        static WAABBCorners: Array<Vector2> = null;
+        static WAABBCornersCluster: Array<Vector2> = null;
+
+        private _processActor(actor: ActorInfo) {
+            // Check if the actor is being disabled or removed
+            if (!actor.isEnabled || actor.isRemoved) {
+                actor.presentInClusters.forEach((k, ci) => {
+                    ci.actors.remove(actor.prim.uid);
+                });
+                actor.presentInClusters.clear();
+                return;
+            }
+
+            let wab = actor.worldAABB;
+
+            // Build the worldAABB corners
+            let wac = BasicPrimitiveCollisionManager.WAABBCorners;
+            wac[0].copyFromFloats(wab.x, wab.y); // Bottom/Left
+            wac[1].copyFromFloats(wab.z, wab.y); // Bottom/Right
+            wac[2].copyFromFloats(wab.z, wab.w); // Top/Right
+            wac[3].copyFromFloats(wab.x, wab.w); // Top/Left
+
+            let cs = this._clusterStep;
+            let wacc = BasicPrimitiveCollisionManager.WAABBCornersCluster;
+            for (let i = 0; i < 4; i++) {
+                let p = wac[i];
+                let cx = (p.x - (p.x % cs.x)) / cs.x;
+                let cy = (p.y - (p.y % cs.y)) / cs.y;
+                wacc[i].copyFromFloats(Math.floor(cx), Math.floor(cy));
+            }
+
+            let opCount = 0;
+            let totalClusters = 0;
+            let newCI = new Array<ClusterInfo>();
+            let sx = Math.max(0, wacc[0].x);                              // Start Cluster X
+            let sy = Math.max(0, wacc[0].y);                              // Start Cluster Y
+            let ex = Math.min(this._clusterSize.width - 1,  wacc[2].x);   // End Cluster X
+            let ey = Math.min(this._clusterSize.height - 1, wacc[2].y);   // End Cluster Y
+
+            if (this._enableBorder) {
+                if (wac[0].x < 0) {
+                    this._borderIntersecteddActors[0].add(actor.prim.uid, actor.prim);
+                } else {
+                    this._borderIntersecteddActors[0].remove(actor.prim.uid);
+                }
+                if (wac[0].y < 0) {
+                    this._borderIntersecteddActors[1].add(actor.prim.uid, actor.prim);
+                } else {
+                    this._borderIntersecteddActors[1].remove(actor.prim.uid);
+                }
+                if (wac[2].x >= this._canvasSize.width) {
+                    this._borderIntersecteddActors[2].add(actor.prim.uid, actor.prim);
+                } else {
+                    this._borderIntersecteddActors[2].remove(actor.prim.uid);
+                }
+                if (wac[2].y >= this._canvasSize.height) {
+                    this._borderIntersecteddActors[3].add(actor.prim.uid, actor.prim);
+                } else {
+                    this._borderIntersecteddActors[3].remove(actor.prim.uid);
+                }
+            }
+
+            for (var y = sy; y <= ey; y++) {
+                for (let x = sx; x <= ex; x++) {
+                    let k = `${x}:${y}`;
+                    let cx = x, cy = y;
+                    let ci = actor.presentInClusters.getOrAddWithFactory(k,
+                        (k) => {
+                            let nci = this._getCluster(cx, cy);
+                            nci.actors.add(actor.prim.uid, actor);
+                            this._maxActorByCluster = Math.max(this._maxActorByCluster, nci.actors.count);
+                            ++opCount;
+                            ++totalClusters;
+                            return nci;
+                        });
+                    newCI.push(ci);
+                }
+            }
+
+            // Check if there were no change
+            if (opCount === 0 && actor.presentInClusters.count === totalClusters) {
+                return;
+            }
+
+            // Build the array of the cluster where the actor is no longer in
+            let clusterToRemove = new Array<string>();
+            actor.presentInClusters.forEach((k, ci) => {
+                if (newCI.indexOf(ci) === -1) {
+                    clusterToRemove.push(k);
+                    // remove the primitive from the Cluster Info object
+                    ci.actors.remove(actor.prim.uid);
+                }
+            });
+
+            // Remove these clusters from the actor's dictionary
+            for (let key of clusterToRemove) {
+                actor.presentInClusters.remove(key);
+            }
+        }
+
+        private static CandidatesActors = new StringDictionary<ActorInfoBase>();
+        private static PreviousIntersections = new StringDictionary<ActorInfoBase>();
+
+        // The algorithm is simple, we have previously partitioned the Actors in the Clusters: each actor has a list of the Cluster(s) it's inside.
+        // Then for a given Actor that is dirty we evaluate the intersection with all the other actors present in the same Cluster(s)
+        // So it's basically O(n²), BUT only inside a Cluster and only for dirty Actors.
+        private _collisionDetection() {
+            let hash = BasicPrimitiveCollisionManager.CandidatesActors;
+            let prev = BasicPrimitiveCollisionManager.PreviousIntersections;
+            let opCount = 0;
+
+            this._dirtyActors.forEach((k1, ai1) => {
+                ++opCount;
+
+                // Build the list of candidates
+                hash.clear();
+                ai1.presentInClusters.forEach((k, ci) => {
+                    ++opCount;
+                    ci.actors.forEach((k, v) => hash.add(k, v));
+                });
+
+                let wab1 = ai1.worldAABB;
+
+                // Save the previous intersections
+                prev.clear();
+                prev.copyFrom(ai1.intersectWith);
+
+                ai1.intersectWith.clear();
+
+                // For each candidate
+                hash.forEach((k2, ai2) => {
+                    ++opCount;
+
+                    // Check if we're testing against itself
+                    if (k1 === k2) {
+                        return;
+                    }
+
+                    let wab2 = ai2.worldAABB;
+                     
+                    if (wab2.z >= wab1.x && wab2.x <= wab1.z && wab2.w >= wab1.y && wab2.y <= wab1.w) {
+
+                        if (ai1.prim.intersectOtherPrim(ai2.prim)) {
+                            ++opCount;
+                            ai1.intersectWith.add(k2, ai2);
+
+                            if (k1 < k2) {
+                                this._intersectedActors.add(`${k1};${k2}`, { a: ai1.prim, b: ai2.prim });
+                            } else {
+                                this._intersectedActors.add(`${k2};${k1}`, { a: ai2.prim, b: ai1.prim });
+                            }
+                        }
+                    }
+                });
+
+                // Check and remove the associations that no longer exist in the main intersection list
+                prev.forEach((k, ai) => {
+                    if (!ai1.intersectWith.contains(k)) {
+                        ++opCount;
+                        this._intersectedActors.remove(`${k<k1 ? k : k1};${k<k1 ? k1 : k}`);
+                    }
+                });
+
+            });
+
+            this._debugUpdateOpCount.fetchNewFrame();
+            this._debugUpdateOpCount.addCount(opCount, true);
+        }
+
+        private _getCluster(x: number, y: number): ClusterInfo {
+            return this._clusters[x][y];
+        }
+
+        private _updateDebugStats() {
+
+            let format = (v: number) => (Math.round(v*100)/100).toString();
+            let txt =   `Primitive Collision Stats\n` + 
+                        ` - PCM Execution Time: ${format(this._debugUpdateTime.lastSecAverage)}ms\n` +
+                        ` - Operation Count: ${format(this._debugUpdateOpCount.current)}, (avg:${format(this._debugUpdateOpCount.lastSecAverage)}, t:${format(this._debugUpdateOpCount.total)})\n` +
+                        ` - Max Actor per Cluster: ${this._maxActorByCluster}\n` +
+                        ` - Intersections count: ${this.intersectedActors.count}`;
+
+            if (!this._debugTextBackground) {
+
+                this._debugTextBackground = new Rectangle2D({
+                    id: "###DEBUG PMC STATS###", parent: this._owner, marginAlignment: "h: left, v: top", fill: "#C0404080", padding: "10", margin: "10", roundRadius: 10, children: [
+                        new Text2D(txt, { id: "###DEBUG PMC TEXT###", fontName: "12pt Lucida Console" })
+                    ]
+                });
+                    
+            } else {
+                this._debugTextBackground.levelVisible = true;
+                let text2d = this._debugTextBackground.children[0] as Text2D;
+                text2d.text = txt;
+            }
+        }
+
+        private _updateAABBDisplay() {
+            let g = new WireFrameGroup2D("main", new Color4(0.5, 0.8, 1.0, 1.0));
+
+            let v = Vector2.Zero();
+
+            this._actors.forEach((k, ai) => {
+                if (ai.isEnabled) {
+                    let ab = ai.worldAABB;
+
+                    v.x = ab.x;
+                    v.y = ab.y;
+                    g.startLineStrip(v);
+
+                    v.x = ab.z;
+                    g.pushVertex(v);
+
+                    v.y = ab.w;
+                    g.pushVertex(v);
+
+                    v.x = ab.x;
+                    g.pushVertex(v);
+
+                    v.y = ab.y;
+                    g.endLineStrip(v);
+                }
+            });
+
+            if (!this._AABBRenderPrim) {
+                this._AABBRenderPrim = new WireFrame2D([g], { parent: this._owner, alignToPixel: false, id: "###DEBUG PCM AABB###" });
+            } else {
+                this._AABBRenderPrim.wireFrameGroups.set("main", g);
+                this._AABBRenderPrim.wireFrameGroupsDirty();
+            }
+
+            this._debugRenderAABBDirty = false;
+        }
+
+        private _updateClusterDisplay(cw: number, ch: number) {
+            let g = new WireFrameGroup2D("main", new Color4(0.8, 0.1, 0.5, 1.0));
+
+            let v1 = Vector2.Zero();
+            let v2 = Vector2.Zero();
+
+            // Vertical lines
+            let step = (this._owner.actualWidth-1) / cw;
+            v1.y = 0;
+            v2.y = this._owner.actualHeight;
+            for (let x = 0; x <= cw; x++) {
+                g.pushVertex(v1);
+                g.pushVertex(v2);
+
+                v1.x += step;
+                v2.x += step;
+            }
+
+            // Horizontal lines
+            step = (this._owner.actualHeight-1) / ch;
+            v1.x = v1.y = v2.y = 0;
+            v2.x = this._owner.actualWidth;
+            for (let y = 0; y <= ch; y++) {
+                g.pushVertex(v1);
+                g.pushVertex(v2);
+
+                v1.y += step;
+                v2.y += step;
+            }
+
+            if (!this._ClusterRenderPrim) {
+                this._ClusterRenderPrim = new WireFrame2D([g], { parent: this._owner, alignToPixel: true, id: "###DEBUG PCM Clusters###" });
+            } else {
+                this._ClusterRenderPrim.wireFrameGroups.set("main", g);
+                this._ClusterRenderPrim.wireFrameGroupsDirty();
+            }
+        }
+
+        // Basically: we don't want to spend our time playing with the GC each time the Cluster Array is rebuilt, so we keep a list of available
+        //  ClusterInfo object and we have two method to allocate/free them. This way we always deal with the same objects.
+        // The free array never shrink, always grows...For the better...and the worst!
+        private _allocClusterInfo(): ClusterInfo {
+            if (this._freeClusters.length === 0) {
+                for (let i = 0; i < 8; i++) {
+                    this._freeClusters.push(new ClusterInfo());
+                }
+            }
+
+            return this._freeClusters.pop();
+        }
+
+        private _freeClusterInfo(ci: ClusterInfo) {
+            ci.clear();
+            this._freeClusters.push(ci);
+        }
+
+        private _canvasSize: Size;
+        private _clusterDirty: boolean;
+        private _clusterSize: Size;
+        private _clusterStep: Vector2;
+        private _clusters: ClusterInfo[][];
+        private _maxActorByCluster: number;
+        private _lastClusterResizeCounter: number;
+        private _actors: StringDictionary<ActorInfo>;
+        private _dirtyActors: StringDictionary<ActorInfo>;
+
+        private _freeClusters: Array<ClusterInfo>;
+        private _enableBorder: boolean;
+        private _intersectedActors: ObservableStringDictionary<{ a: Prim2DBase; b: Prim2DBase }>;
+        private _borderIntersecteddActors: ObservableStringDictionary<Prim2DBase>[];
+        private _debugUpdateOpCount: PerfCounter;
+        private _debugUpdateTime: PerfCounter;
+        private _debugRenderAABB: boolean;
+        private _debugRenderAABBDirty: boolean;
+        private _AABBRenderPrim: WireFrame2D;
+        private _ClusterRenderPrim: WireFrame2D;
+        private _debugTextBackground: Rectangle2D;
+    }
+}

+ 85 - 2
canvas2D/src/Engine/babylon.rectangle2d.ts

@@ -28,6 +28,7 @@
                 }
                 }
                 this.effectsReady = true;
                 this.effectsReady = true;
             }
             }
+
             let canvas = instanceInfo.owner.owner;
             let canvas = instanceInfo.owner.owner;
             var engine = canvas.engine;
             var engine = canvas.engine;
 
 
@@ -40,7 +41,6 @@
             var curAlphaMode = engine.getAlphaMode();
             var curAlphaMode = engine.getAlphaMode();
 
 
             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];
 
 
@@ -182,7 +182,11 @@
         }
         }
 
 
         public set actualSize(value: Size) {
         public set actualSize(value: Size) {
-            this._actualSize = value;
+            if (!this._actualSize) {
+                this._actualSize = value.clone();
+            } else {
+                this._actualSize.copyFrom(value);
+            }
         }
         }
 
 
         @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Rectangle2D.notRoundedProperty = pi)
         @modelLevelProperty(Shape2D.SHAPE2D_PROPCOUNT + 2, pi => Rectangle2D.notRoundedProperty = pi)
@@ -315,6 +319,9 @@
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -355,6 +362,9 @@
             isPickable            ?: boolean,
             isPickable            ?: boolean,
             isContainer           ?: boolean,
             isContainer           ?: boolean,
             childrenFlatZOrder    ?: boolean,
             childrenFlatZOrder    ?: boolean,
+            levelCollision        ?: boolean,
+            deepCollision         ?: boolean,
+            layoutData            ?: ILayoutData,
             marginTop             ?: number | string,
             marginTop             ?: number | string,
             marginLeft            ?: number | string,
             marginLeft            ?: number | string,
             marginRight           ?: number | string,
             marginRight           ?: number | string,
@@ -401,6 +411,79 @@
             return renderCache;
             return renderCache;
         }
         }
 
 
+        protected updateTriArray() {
+            // Not Rounded = sharp edge rect, the default implementation is the right one!
+            if (this.notRounded) {
+                super.updateTriArray();
+                return;
+            }
+
+            // Rounded Corner? It's more complicated! :)
+
+            let subDiv = Rectangle2D.roundSubdivisions * 4;
+            if (this._primTriArray == null) {
+                this._primTriArray = new Tri2DArray(subDiv);
+            } else {
+                this._primTriArray.clear(subDiv);
+            }
+
+            let size = this.actualSize;
+			let w = size.width;
+			let h = size.height;
+            let r = this.roundRadius;
+            let rsub0 = subDiv * 0.25;
+            let rsub1 = subDiv * 0.50;
+            let rsub2 = subDiv * 0.75;
+            let center = new Vector2(0.5 * size.width, 0.5 * size.height);
+            let twopi = Math.PI * 2;
+			let nru = r / w;
+			let nrv = r / h;
+
+            let computePos = (index: number, p: Vector2) => {
+			    // right/bottom
+			    if (index < rsub0) {
+				    p.x = 1.0 - nru;
+				    p.y = nrv;
+			    }
+			    // left/bottom
+			    else if (index < rsub1) {
+				    p.x = nru;
+				    p.y = nrv;
+			    }
+			    // left/top
+			    else if (index < rsub2) {
+				    p.x = nru;
+				    p.y = 1.0 - nrv;
+			    }
+			    // right/top
+			    else {
+				    p.x = 1.0 - nru;
+				    p.y = 1.0 - nrv;
+			    }
+
+                let angle = twopi - (index * twopi / (subDiv - 0.5));
+			    p.x += Math.cos(angle) * nru;
+                p.y += Math.sin(angle) * nrv;
+                p.x *= w;
+                p.y *= h;
+            }
+
+            console.log("Genetre TriList for " + this.id);
+            let first = Vector2.Zero();
+            let cur = Vector2.Zero();
+            computePos(0, first);
+            let prev = first.clone();
+            for (let index = 1; index < subDiv; index++) {
+                computePos(index, cur);
+                this._primTriArray.storeTriangle(index - 1, center, prev, cur);
+                console.log(`${index-1}, ${center}, ${prev}, ${cur}`);
+                prev.copyFrom(cur);
+            }
+            this._primTriArray.storeTriangle(subDiv-1, center, first, prev);
+                console.log(`${subDiv-1}, ${center}, ${prev}, ${first}`);
+
+        }
+
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
             let renderCache = <Rectangle2DRenderCache>modelRenderCache;
             let renderCache = <Rectangle2DRenderCache>modelRenderCache;
             let engine = this.owner.engine;
             let engine = this.owner.engine;

+ 5 - 0
canvas2D/src/Engine/babylon.renderablePrim2d.ts

@@ -492,6 +492,11 @@
                 this.setupModelRenderCache(this._modelRenderCache);
                 this.setupModelRenderCache(this._modelRenderCache);
             }
             }
 
 
+            if (this._isFlagSet(SmartPropertyPrim.flagModelUpdate)) {
+                if (this._modelRenderCache.updateModelRenderCache(this)) {
+                    this._clearFlags(SmartPropertyPrim.flagModelUpdate);
+                }
+            }
             // 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.

+ 26 - 2
canvas2D/src/Engine/babylon.smartPropertyPrim.ts

@@ -993,6 +993,7 @@
         constructor() {
         constructor() {
             super();
             super();
             this._flags = 0;
             this._flags = 0;
+            this._uid = null;
             this._modelKey = null;
             this._modelKey = null;
             this._levelBoundingInfo = new BoundingInfo2D();
             this._levelBoundingInfo = new BoundingInfo2D();
             this._boundingInfo = new BoundingInfo2D();
             this._boundingInfo = new BoundingInfo2D();
@@ -1022,6 +1023,16 @@
         public animations: Animation[];
         public animations: Animation[];
 
 
         /**
         /**
+         * return a unique identifier for the Canvas2D
+         */
+        public get uid(): string {
+            if (!this._uid) {
+                this._uid = Tools.RandomId();
+            }
+            return this._uid;
+        }
+
+        /**
          * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
          * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
          * Look at Sprite2D for more information
          * Look at Sprite2D for more information
          */
          */
@@ -1062,7 +1073,10 @@
                         if (v.typeLevelCompare) {
                         if (v.typeLevelCompare) {
                             value = Tools.getClassName(propVal);
                             value = Tools.getClassName(propVal);
                         } else {
                         } else {
-                            if (propVal instanceof BaseTexture) {
+                            // String Dictionaries' content are too complex, with use a Random GUID to make the model unique
+                            if (propVal instanceof StringDictionary) {
+                                value = Tools.RandomId();
+                            } else if (propVal instanceof BaseTexture) {
                                 value = propVal.uid;
                                 value = propVal.uid;
                             } else {
                             } else {
                                 value = propVal.toString();
                                 value = propVal.toString();
@@ -1085,7 +1099,7 @@
          * @returns true is dirty, false otherwise
          * @returns true is dirty, false otherwise
          */
          */
         public get isDirty(): boolean {
         public get isDirty(): boolean {
-            return (this._instanceDirtyFlags !== 0) || this._areSomeFlagsSet(SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagLayoutDirty);
+            return (this._instanceDirtyFlags !== 0) || this._areSomeFlagsSet(SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagModelUpdate | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagLayoutDirty);
         }
         }
 
 
         protected _boundingBoxDirty() {
         protected _boundingBoxDirty() {
@@ -1137,6 +1151,11 @@
                 if (p != null && p.layoutEngine && (p.layoutEngine.layoutDirtyOnPropertyChangedMask & propInfo.flagId) !== 0) {
                 if (p != null && p.layoutEngine && (p.layoutEngine.layoutDirtyOnPropertyChangedMask & propInfo.flagId) !== 0) {
                     p._setLayoutDirty();
                     p._setLayoutDirty();
                 }
                 }
+
+                let that = this as Prim2DBase;
+                if (that.layoutEngine && (that.layoutEngine.layoutDirtyOnPropertyChangedMask & propInfo.flagId) !== 0) {
+                    (<any>this)._setLayoutDirty();
+                }
             }
             }
 
 
             // For type level compare, if there's a change of type it's a change of model, otherwise we issue an instance change
             // For type level compare, if there's a change of type it's a change of model, otherwise we issue an instance change
@@ -1151,6 +1170,7 @@
             // Set the dirty flags
             // Set the dirty flags
             if (!instanceDirty && (propInfo.kind === Prim2DPropInfo.PROPKIND_MODEL)) {
             if (!instanceDirty && (propInfo.kind === Prim2DPropInfo.PROPKIND_MODEL)) {
                 if (!this.isDirty) {
                 if (!this.isDirty) {
+                    this.onPrimBecomesDirty();
                     this._setFlags(SmartPropertyPrim.flagModelDirty);
                     this._setFlags(SmartPropertyPrim.flagModelDirty);
                 }
                 }
             } else if (instanceDirty || (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) || (propInfo.kind === Prim2DPropInfo.PROPKIND_DYNAMIC)) {
             } else if (instanceDirty || (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) || (propInfo.kind === Prim2DPropInfo.PROPKIND_DYNAMIC)) {
@@ -1179,6 +1199,7 @@
         public get levelBoundingInfo(): BoundingInfo2D {
         public get levelBoundingInfo(): BoundingInfo2D {
             if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
             if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
                 if (this.updateLevelBoundingInfo()) {
                 if (this.updateLevelBoundingInfo()) {
+                    this._boundingInfo.dirtyWorldAABB();
                     this._clearFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
                     this._clearFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
                 } else {
                 } else {
                     this._levelBoundingInfo.clear();
                     this._levelBoundingInfo.clear();
@@ -1282,7 +1303,10 @@
         public static flagDontInheritParentScale  = 0x0080000;    // set if the actualScale must not use its parent's scale to be computed
         public static flagDontInheritParentScale  = 0x0080000;    // set if the actualScale must not use its parent's scale to be computed
         public static flagGlobalTransformDirty    = 0x0100000;    // set if the global transform must be recomputed due to a local transform change
         public static flagGlobalTransformDirty    = 0x0100000;    // set if the global transform must be recomputed due to a local transform change
         public static flagLayoutBoundingInfoDirty = 0x0200000;    // set if the layout bounding info is dirty
         public static flagLayoutBoundingInfoDirty = 0x0200000;    // set if the layout bounding info is dirty
+        public static flagCollisionActor          = 0x0400000;    // set if the primitive is part of the collision engine
+        public static flagModelUpdate             = 0x0800000;    // set if the primitive's model data is to update
 
 
+        private   _uid                : string;
         private   _flags              : number;
         private   _flags              : number;
         private   _modelKey           : string;
         private   _modelKey           : string;
         protected _levelBoundingInfo  : BoundingInfo2D;
         protected _levelBoundingInfo  : BoundingInfo2D;

+ 24 - 6
canvas2D/src/Engine/babylon.sprite2d.ts

@@ -157,10 +157,14 @@
         }
         }
 
 
         public set actualSize(value: Size) {
         public set actualSize(value: Size) {
-            this._actualSize = value;
+            if (!this._actualSize) {
+                this._actualSize = value.clone();
+            } else {
+                this._actualSize.copyFrom(value);
+            }
         }
         }
 
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Sprite2D.spriteSizeProperty = pi)
+        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Sprite2D.spriteSizeProperty = pi, false, true)
         /**
         /**
          * Get/set the sprite location (in pixels) in the texture
          * Get/set the sprite location (in pixels) in the texture
          */
          */
@@ -169,7 +173,11 @@
         }
         }
 
 
         public set spriteSize(value: Size) {
         public set spriteSize(value: Size) {
-            this._spriteSize = value;
+            if (!this._spriteSize) {
+                this._spriteSize = value.clone();
+            } else {
+                this._spriteSize.copyFrom(value);
+            }
             this._updateSpriteScaleFactor();
             this._updateSpriteScaleFactor();
         }
         }
 
 
@@ -182,7 +190,11 @@
         }
         }
 
 
         public set spriteLocation(value: Vector2) {
         public set spriteLocation(value: Vector2) {
-            this._spriteLocation = value;
+            if (!this._spriteLocation) {
+                this._spriteLocation = value.clone();
+            } else {
+                this._spriteLocation.copyFrom(value);
+            }
         }
         }
 
 
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 6, pi => Sprite2D.spriteFrameProperty = pi)
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 6, pi => Sprite2D.spriteFrameProperty = pi)
@@ -292,6 +304,9 @@
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -333,6 +348,9 @@
             isPickable            ?: boolean,
             isPickable            ?: boolean,
             isContainer           ?: boolean,
             isContainer           ?: boolean,
             childrenFlatZOrder    ?: boolean,
             childrenFlatZOrder    ?: boolean,
+            levelCollision        ?: boolean,
+            deepCollision         ?: boolean,
+            layoutData            ?: ILayoutData,
             marginTop             ?: number | string,
             marginTop             ?: number | string,
             marginLeft            ?: number | string,
             marginLeft            ?: number | string,
             marginRight           ?: number | string,
             marginRight           ?: number | string,
@@ -358,8 +376,8 @@
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
             this._useSize = false;
             this._useSize = false;
-            this.spriteSize = (settings.spriteSize!=null) ? settings.spriteSize.clone() : null;
-            this.spriteLocation = (settings.spriteLocation!=null) ? settings.spriteLocation.clone() : new Vector2(0, 0);
+            this._spriteSize = (settings.spriteSize!=null) ? settings.spriteSize.clone() : null;
+            this._spriteLocation = (settings.spriteLocation!=null) ? settings.spriteLocation.clone() : new Vector2(0, 0);
             if (settings.size != null) {
             if (settings.size != null) {
                 this.size = settings.size;
                 this.size = settings.size;
             }
             }

+ 18 - 7
canvas2D/src/Engine/babylon.text2d.ts

@@ -204,7 +204,11 @@
         }
         }
 
 
         public set defaultFontColor(value: Color4) {
         public set defaultFontColor(value: Color4) {
-            this._defaultFontColor = value;
+            if (!this._defaultFontColor) {
+                this._defaultFontColor = value.clone();
+            } else {
+                this._defaultFontColor.copyFrom(value);
+            }
         }
         }
 
 
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Text2D.textProperty = pi, false, true)
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Text2D.textProperty = pi, false, true)
@@ -242,7 +246,7 @@
         }
         }
 
 
         public set size(value: Size) {
         public set size(value: Size) {
-            this._size = value;
+            this.internalSetSize(value);
         }
         }
 
 
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Text2D.fontSuperSampleProperty = pi, false, false)
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Text2D.fontSuperSampleProperty = pi, false, false)
@@ -370,6 +374,9 @@
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -415,6 +422,9 @@
             isPickable              ?: boolean,
             isPickable              ?: boolean,
             isContainer             ?: boolean,
             isContainer             ?: boolean,
             childrenFlatZOrder      ?: boolean,
             childrenFlatZOrder      ?: boolean,
+            levelCollision          ?: boolean,
+            deepCollision           ?: boolean,
+            layoutData              ?: ILayoutData,
             marginTop               ?: number | string,
             marginTop               ?: number | string,
             marginLeft              ?: number | string,
             marginLeft              ?: number | string,
             marginRight             ?: number | string,
             marginRight             ?: number | string,
@@ -450,7 +460,7 @@
                 this._fontSuperSample= (settings.fontSuperSample!=null && settings.fontSuperSample);
                 this._fontSuperSample= (settings.fontSuperSample!=null && settings.fontSuperSample);
                 this._fontSDF        = (settings.fontSignedDistanceField!=null && settings.fontSignedDistanceField);
                 this._fontSDF        = (settings.fontSignedDistanceField!=null && settings.fontSignedDistanceField);
             }
             }
-            this.defaultFontColor    = (settings.defaultFontColor==null) ? new Color4(1,1,1,1) : settings.defaultFontColor;
+            this._defaultFontColor   = (settings.defaultFontColor==null) ? new Color4(1,1,1,1) : settings.defaultFontColor.clone();
             this._tabulationSize     = (settings.tabulationSize == null) ? 4 : settings.tabulationSize;
             this._tabulationSize     = (settings.tabulationSize == null) ? 4 : settings.tabulationSize;
             this._textSize           = null;
             this._textSize           = null;
             this.text                = text;
             this.text                = text;
@@ -709,10 +719,9 @@
                         let char = text[charNum];
                         let char = text[charNum];
                         let charWidth = charWidths[charNum];
                         let charWidth = charWidths[charNum];
 
 
-                        this.updateInstanceDataPart(d, offset);
-                        offset.x += charWidth;
-
-                        if (!this._isWhiteSpaceCharHoriz(char)) {
+                        if(char !== "\t" && !this._isWhiteSpaceCharVert(char)){ 
+                            //make sure space char gets processed here or overlapping can occur when text is set
+                            this.updateInstanceDataPart(d, offset);
                             let ci = texture.getChar(char);
                             let ci = texture.getChar(char);
                             d.topLeftUV = ci.topLeftUV;
                             d.topLeftUV = ci.topLeftUV;
                             let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
                             let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
@@ -722,6 +731,8 @@
                             d.superSampleFactor = superSampleFactor;
                             d.superSampleFactor = superSampleFactor;
                             ++d.curElement;
                             ++d.curElement;
                         }
                         }
+
+                        offset.x += charWidth;
                         charNum++;
                         charNum++;
                     }
                     }
 
 

+ 483 - 0
canvas2D/src/Engine/babylon.wireFrame2d.ts

@@ -0,0 +1,483 @@
+module BABYLON {
+    export class WireFrame2DRenderCache extends ModelRenderCache {
+        effectsReady: boolean = false;
+        vb: WebGLBuffer = null;
+        vtxCount = 0;
+        instancingAttributes: InstancingAttributeInfo[] = null;
+        effect: Effect = null;
+        effectInstanced: Effect = null;
+
+        render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean {
+            // Do nothing if the shader is still loading/preparing 
+            if (!this.effectsReady) {
+                if ((this.effect && (!this.effect.isReady() || (this.effectInstanced && !this.effectInstanced.isReady())))) {
+                    return false;
+                }
+                this.effectsReady = true;
+            }
+
+            // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+            let canvas = instanceInfo.owner.owner;
+            var engine = canvas.engine;
+
+            var cur = engine.getAlphaMode();
+            let effect = context.useInstancing ? this.effectInstanced : this.effect;
+
+            engine.enableEffect(effect);
+            engine.bindBuffersDirectly(this.vb, null, [2, 4], 24, effect);
+
+            if (context.renderMode !== Render2DContext.RenderModeOpaque) {
+                engine.setAlphaMode(Engine.ALPHA_COMBINE, true);
+            }
+
+            let pid = context.groupInfoPartData[0];
+            if (context.useInstancing) {
+                if (!this.instancingAttributes) {
+                    this.instancingAttributes = this.loadInstancingAttributes(WireFrame2D.WIREFRAME2D_MAINPARTID, effect);
+                }
+                let glBuffer = context.instancedBuffers ? context.instancedBuffers[0] : pid._partBuffer;
+                let count = context.instancedBuffers ? context.instancesCount : pid._partData.usedElementCount;
+                canvas._addDrawCallCount(1, context.renderMode);
+                engine.updateAndBindInstancesBuffer(glBuffer, null, this.instancingAttributes);
+                engine.drawUnIndexed(false, 0, this.vtxCount, count);
+//                engine.draw(true, 0, 6, count);
+                engine.unbindInstanceAttributes();
+            } else {
+                canvas._addDrawCallCount(context.partDataEndIndex - context.partDataStartIndex, context.renderMode);
+                for (let i = context.partDataStartIndex; i < context.partDataEndIndex; i++) {
+                    this.setupUniforms(effect, 0, pid._partData, i);
+                    engine.drawUnIndexed(false, 0, this.vtxCount);
+  //                  engine.draw(true, 0, 6);
+                }
+            }
+
+            engine.setAlphaMode(cur, true);
+
+            return true;
+        }
+
+        public updateModelRenderCache(prim: Prim2DBase): boolean {
+            let w = prim as WireFrame2D;
+            w._updateVertexBuffer(this);
+            return true;
+        }
+
+        public dispose(): boolean {
+            if (!super.dispose()) {
+                return false;
+            }
+
+            if (this.vb) {
+                this._engine._releaseBuffer(this.vb);
+                this.vb = null;
+            }
+
+            this.effect = null;
+            this.effectInstanced = null;
+
+            return true;
+        }
+    }
+
+    @className("WireFrameVertex2D", "BABYLON")
+    export class WireFrameVertex2D {
+        x: number;
+        y: number;
+        r: number;
+        g: number;
+        b: number;
+        a: number;
+
+        constructor(p: Vector2, c: Color4=null) {
+            this.fromVector2(p);
+            if (c != null) {
+                this.fromColor4(c);
+            } else {
+                this.r = this.g = this.b = this.a = 1;
+            }
+        }
+
+        fromVector2(p: Vector2) {
+            this.x = p.x;
+            this.y = p.y;
+        }
+
+        fromColor3(c: Color3) {
+            this.r = c.r;
+            this.g = c.g;
+            this.b = c.b;
+            this.a = 1;
+        }
+
+        fromColor4(c: Color4) {
+            this.r = c.r;
+            this.g = c.g;
+            this.b = c.b;
+            this.a = c.a;
+        }
+    }
+
+    @className("WireFrameGroup2D", "BABYLON")
+    /**
+     * A WireFrameGroup2D has a unique id (among the WireFrame2D primitive) and a collection of WireFrameVertex2D which form a Line list.
+     * A Line is defined by two vertices, the storage of vertices doesn't follow the Line Strip convention, so to create consecutive lines the intermediate vertex must be doubled. The best way to build a Line Strip is to use the startLineStrip, pushVertex and endLineStrip methods.
+     * You can manually add vertices using the pushVertex method, but mind that the vertices array must be a multiple of 2 as each line are defined with TWO SEPARATED vertices. I hope this is clear enough.
+     */
+    export class WireFrameGroup2D {
+        /**
+         * Construct a WireFrameGroup2D object
+         * @param id a unique ID among the Groups added to a given WireFrame2D primitive, if you don't specify an id, a random one will be generated. The id is immutable.
+         * @param defaultColor specify the default color that will be used when a vertex is pushed, white will be used if not specified.
+         */
+        constructor(id: string=null, defaultColor: Color4=null) {
+            this._id = (id == null) ? Tools.RandomId() : id;
+            this._uid = Tools.RandomId();
+            this._defaultColor = (defaultColor == null) ? new Color4(1,1,1,1) : defaultColor;
+            this._buildingStrip = false;
+            this._vertices = new Array<WireFrameVertex2D>();
+        }
+
+        public get uid() {
+            return this._uid;
+        }
+
+        /**
+         * Retrieve the ID of the group
+         */
+        public get id(): string {
+            return this._id;
+        }
+
+        /**
+         * Push a vertex in the array of vertices.
+         * If you're previously called startLineStrip, the vertex will be pushed twice in order to describe the end of a line and the start of a new one.
+         * @param p Position of the vertex
+         * @param c Color of the vertex, if null the default color of the group will be used
+         */
+        public pushVertex(p: Vector2, c: Color4=null) {
+            let v = new WireFrameVertex2D(p, (c == null) ? this._defaultColor : c);
+            this._vertices.push(v);
+            if (this._buildingStrip) {
+                let v2 = new WireFrameVertex2D(p, (c == null) ? this._defaultColor : c);
+                this._vertices.push(v2);
+            }
+        }
+
+        /**
+         * Start to store a Line Strip. The given vertex will be pushed in the array. The you have to call pushVertex to add subsequent vertices describing the strip and don't forget to call endLineStrip to close the strip!!!
+         * @param p Position of the vertex
+         * @param c Color of the vertex, if null the default color of the group will be used
+         */
+        public startLineStrip(p: Vector2, c: Color4=null) {
+            this.pushVertex(p, (c == null) ? this._defaultColor : c);
+            this._buildingStrip = true;
+        }
+
+        /**
+         * Close the Strip by storing a last vertex
+         * @param p Position of the vertex
+         * @param c Color of the vertex, if null the default color of the group will be used
+         */
+        public endLineStrip(p: Vector2, c: Color4=null) {
+            this._buildingStrip = false;
+            this.pushVertex(p, (c == null) ? this._defaultColor : c);
+        }
+
+        /**
+         * Access to the array of Vertices, you can manipulate its content but BEWARE of what you're doing!
+         */
+        public get vertices(): Array<WireFrameVertex2D> {
+            return this._vertices;
+        }
+
+        private _uid: string;
+        private _id: string;
+        private _defaultColor: Color4;
+        private _vertices: Array<WireFrameVertex2D>;
+        private _buildingStrip: boolean;
+    }
+
+    @className("WireFrame2D", "BABYLON")
+    /**
+     * Primitive that displays a WireFrame
+     */
+    export class WireFrame2D extends RenderablePrim2D {
+        static WIREFRAME2D_MAINPARTID = 1;
+
+        public static wireFrameGroupsProperty: Prim2DPropInfo;
+
+        @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => WireFrame2D.wireFrameGroupsProperty = pi)
+        /**
+         * Get/set the texture that contains the sprite to display
+         */
+        public get wireFrameGroups(): StringDictionary<WireFrameGroup2D> {
+            return this._wireFrameGroups;
+        }
+
+        /**
+         * If you change the content of the wireFrameGroups you MUST call this method for the changes to be reflected during rendering
+         */
+        public wireFrameGroupsDirty() {
+            this._setFlags(SmartPropertyPrim.flagModelUpdate);
+            this.onPrimBecomesDirty();
+        }
+
+        public get size(): Size {
+            if (this._size == null) {
+                this._computeMinMaxTrans();
+            }
+            return this._size;
+        }
+
+        public set size(value: Size) {
+            this.internalSetSize(value);
+        }
+
+        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 2, pi => WireFrame2D.actualSizeProperty = pi, false, true)
+        /**
+         * Get/set the actual size of the sprite to display
+         */
+        public get actualSize(): Size {
+            if (this._actualSize) {
+                return this._actualSize;
+            }
+            return this.size;
+        }
+
+        public set actualSize(value: Size) {
+            if (!this._actualSize) {
+                this._actualSize.clone();
+            } else {
+                this._actualSize.copyFrom(value);
+            }
+        }
+
+        protected updateLevelBoundingInfo(): boolean {
+            let v = this._computeMinMaxTrans();
+            BoundingInfo2D.CreateFromMinMaxToRef(v.x, v.z, v.y, v.w, this._levelBoundingInfo);
+            return true;
+        }
+
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
+            // TODO !
+            return true;
+        }
+
+        /**
+         * Create an WireFrame 2D primitive
+         * @param wireFrameGroups an array of WireFrameGroup. 
+         * @param settings a combination of settings, possible ones are
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
+         * - size: the size of the sprite displayed in the canvas, if not specified the spriteSize will be used
+         * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
+         * - zOrder: override the zOrder with the specified value
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - alignToPixel: the rendered lines will be aligned to the rendering device' pixels
+         * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
+         * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
+         * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
+         * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
+         * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
+         * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         */
+        constructor(wireFrameGroups: Array<WireFrameGroup2D>, settings?: {
+
+            parent                ?: Prim2DBase,
+            children              ?: Array<Prim2DBase>,
+            id                    ?: string,
+            position              ?: Vector2,
+            x                     ?: number,
+            y                     ?: number,
+            rotation              ?: number,
+            size                  ?: Size,
+            scale                 ?: number,
+            scaleX                ?: number,
+            scaleY                ?: number,
+            dontInheritParentScale?: boolean,
+            opacity               ?: number,
+            zOrder                ?: number, 
+            origin                ?: Vector2,
+            alignToPixel          ?: boolean,
+            isVisible             ?: boolean,
+            isPickable            ?: boolean,
+            isContainer           ?: boolean,
+            childrenFlatZOrder    ?: boolean,
+            levelCollision        ?: boolean,
+            deepCollision         ?: boolean,
+            layoutData            ?: ILayoutData,
+            marginTop             ?: number | string,
+            marginLeft            ?: number | string,
+            marginRight           ?: number | string,
+            marginBottom          ?: number | string,
+            margin                ?: number | string,
+            marginHAlignment      ?: number,
+            marginVAlignment      ?: number,
+            marginAlignment       ?: string,
+            paddingTop            ?: number | string,
+            paddingLeft           ?: number | string,
+            paddingRight          ?: number | string,
+            paddingBottom         ?: number | string,
+            padding               ?: string,
+        }) {
+
+            if (!settings) {
+                settings = {};
+            }
+
+            super(settings);
+
+            this._wireFrameGroups = new StringDictionary<WireFrameGroup2D>();
+            for (let wfg of wireFrameGroups) {
+                this._wireFrameGroups.add(wfg.id, wfg);
+            }
+
+            this._vtxTransparent = false;
+            if (settings.size != null) {
+                this.size = settings.size;
+            }
+
+            this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
+        }
+
+        /**
+         * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
+         */
+        public get alignToPixel(): boolean {
+            return this._alignToPixel;
+        }
+
+        public set alignToPixel(value: boolean) {
+            this._alignToPixel = value;
+        }
+
+        protected createModelRenderCache(modelKey: string): ModelRenderCache {
+            let renderCache = new WireFrame2DRenderCache(this.owner.engine, modelKey);
+            return renderCache;
+        }
+
+        protected setupModelRenderCache(modelRenderCache: ModelRenderCache) {
+            let renderCache = <WireFrame2DRenderCache>modelRenderCache;
+            let engine = this.owner.engine;
+
+            // Create the VertexBuffer
+            this._updateVertexBuffer(renderCache);
+
+            // 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
+            let ei = this.getDataPartEffectInfo(WireFrame2D.WIREFRAME2D_MAINPARTID, ["pos", "col"], [], true);
+            if (ei) {
+                renderCache.effectInstanced = engine.createEffect("wireframe2d", ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+
+            ei = this.getDataPartEffectInfo(WireFrame2D.WIREFRAME2D_MAINPARTID, ["pos", "col"], [], false);
+            renderCache.effect = engine.createEffect("wireframe2d", ei.attributes, ei.uniforms, [], ei.defines, null);
+
+            return renderCache;
+        }
+
+        public _updateVertexBuffer(mrc: WireFrame2DRenderCache) {
+            let engine = this.owner.engine;
+
+            if (mrc.vb != null) {
+                engine._releaseBuffer(mrc.vb);
+            }
+
+            let vtxCount = 0;
+            this._wireFrameGroups.forEach((k, v) => vtxCount += v.vertices.length);
+
+            let vb = new Float32Array(vtxCount * 6);
+            let i = 0;
+            this._wireFrameGroups.forEach((k, v) => {
+                for (let vtx of v.vertices) {
+                    vb[i++] = vtx.x;
+                    vb[i++] = vtx.y;
+                    vb[i++] = vtx.r;
+                    vb[i++] = vtx.g;
+                    vb[i++] = vtx.b;
+                    vb[i++] = vtx.a;
+                }
+            });
+
+            mrc.vb = engine.createVertexBuffer(vb);
+            mrc.vtxCount = vtxCount;
+        }
+
+        protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
+            if (!super.refreshInstanceDataPart(part)) {
+                return false;
+            }
+
+            if (part.id === WireFrame2D.WIREFRAME2D_MAINPARTID) {
+                let d = <WireFrame2DInstanceData>this._instanceDataParts[0];
+                d.properties = new Vector3(this.alignToPixel ? 1 : 0, 2/this.renderGroup.actualWidth, 2/this.renderGroup.actualHeight);
+            }
+            return true;
+        }
+
+        private _computeMinMaxTrans(): Vector4 {
+            let xmin = Number.MAX_VALUE;
+            let xmax = Number.MIN_VALUE;
+            let ymin = Number.MAX_VALUE;
+            let ymax = Number.MIN_VALUE;
+            let transparent = false;
+
+            this._wireFrameGroups.forEach((k, v) => {
+                for (let vtx of v.vertices) {
+                    xmin = Math.min(xmin, vtx.x);
+                    xmax = Math.max(xmax, vtx.x);
+                    ymin = Math.min(ymin, vtx.y);
+                    ymax = Math.max(ymax, vtx.y);
+
+                    if (vtx.a < 1) {
+                        transparent = true;
+                    }
+                }
+            });
+
+            this._vtxTransparent = transparent;
+            this._size = new Size(xmax - xmin, ymax - ymin);
+            return new Vector4(xmin, ymin, xmax, ymax);
+        }
+
+        protected createInstanceDataParts(): InstanceDataBase[] {
+            return [new WireFrame2DInstanceData(WireFrame2D.WIREFRAME2D_MAINPARTID)];
+        }
+
+        private _vtxTransparent: boolean;
+        private _wireFrameGroups: StringDictionary<WireFrameGroup2D>;
+        private _alignToPixel: boolean;
+    }
+
+    export class WireFrame2DInstanceData extends InstanceDataBase {
+        constructor(partId: number) {
+            super(partId, 1);
+        }
+
+        // the properties is for now the alignedToPixel value
+        @instanceData()
+        get properties(): Vector3 {
+            return null;
+        }
+        set properties(value: Vector3) {
+        }
+    }
+}

+ 278 - 0
canvas2D/src/Tools/babylon.math2D.ts

@@ -0,0 +1,278 @@
+module BABYLON {
+
+    export class Tri2DInfo {
+        /**
+         * Construct an instance of Tri2DInfo, you can either pass null to a, b and c and the instance will be allocated "clear", or give actual triangle info and the center/radius will be computed
+         */
+        constructor(a: Vector2, b: Vector2, c: Vector2) {
+            if (a === null && b === null && c === null) {
+                this.a = Vector2.Zero();
+                this.b = Vector2.Zero();
+                this.c = Vector2.Zero();
+                this.center = Vector2.Zero();
+                this.radius = 0;
+                return;
+            }
+
+            this.a = a.clone();
+            this.b = b.clone();
+            this.c = c.clone();
+
+            this._updateCenterRadius();
+        }
+
+        a: Vector2;
+        b: Vector2;
+        c: Vector2;
+        center: Vector2;
+        radius: number;
+
+        public static Zero() {
+            return new Tri2DInfo(null, null, null);
+        }
+
+        public set(a: Vector2, b: Vector2, c: Vector2) {
+            this.a.copyFrom(a);
+            this.b.copyFrom(b);
+            this.c.copyFrom(c);
+
+            this._updateCenterRadius();
+        }
+
+        public transformInPlace(transform: Matrix) {
+            Vector2.TransformToRef(this.a, transform, this.a);
+            Vector2.TransformToRef(this.b, transform, this.b);
+            Vector2.TransformToRef(this.c, transform, this.c);
+
+            this._updateCenterRadius();
+        }
+
+        public doesContain(p: Vector2): boolean {
+            return Vector2.PointInTriangle(p, this.a, this.b, this.c);
+        }
+
+        private _updateCenterRadius() {
+            this.center.x = (this.a.x + this.b.x + this.c.x) / 3;
+            this.center.y = (this.a.y + this.b.y + this.c.y) / 3;
+
+            let la = Vector2.DistanceSquared(this.a, this.center);
+            let lb = Vector2.DistanceSquared(this.b, this.center);
+            let lc = Vector2.DistanceSquared(this.c, this.center);
+
+            let rs = Math.max(Math.max(la, lb), lc);
+            this.radius = Math.sqrt(rs);
+        }
+    }
+
+    export class Tri2DArray {
+        constructor(count: number) {
+            this._count = count;
+            this._array = new Float32Array(9 * count);
+        }
+
+        public clear(count: number) {
+            if (this._count === count) {
+                return;
+            }
+
+            this._count = count;
+            this._array = new Float32Array(9 * count);
+        }
+
+        public storeTriangle(index: number, a: Vector2, b: Vector2, c: Vector2) {
+            let center = new Vector2((a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3);
+
+            let la = Vector2.DistanceSquared(a, center);
+            let lb = Vector2.DistanceSquared(b, center);
+            let lc = Vector2.DistanceSquared(c, center);
+
+            let rs = Math.max(Math.max(la, lb), lc);
+            let radius = Math.sqrt(rs);
+
+            let offset = index * 9;
+            this._array[offset + 0] = a.x;
+            this._array[offset + 1] = a.y;
+            this._array[offset + 2] = b.x;
+            this._array[offset + 3] = b.y;
+            this._array[offset + 4] = c.x;
+            this._array[offset + 5] = c.y;
+            this._array[offset + 6] = center.x;
+            this._array[offset + 7] = center.y;
+            this._array[offset + 8] = radius;
+        }
+
+        /**
+         * Store a triangle in a Tri2DInfo object
+         * @param index the index of the triangle to store
+         * @param tri2dInfo the instance that will contain the data, it must be already allocated with its inner object also allocated
+         */
+        public storeToTri2DInfo(index: number, tri2dInfo: Tri2DInfo) {
+            if (index >= this._count) {
+                throw new Error(`Can't fetch the triangle at index ${index}, max index is ${this._count - 1}`);
+            }
+
+            let offset = index * 9;
+            tri2dInfo.a.x      = this._array[offset + 0];
+            tri2dInfo.a.y      = this._array[offset + 1];
+            tri2dInfo.b.x      = this._array[offset + 2];
+            tri2dInfo.b.y      = this._array[offset + 3];
+            tri2dInfo.c.x      = this._array[offset + 4];
+            tri2dInfo.c.y      = this._array[offset + 5];
+            tri2dInfo.center.x = this._array[offset + 6];
+            tri2dInfo.center.y = this._array[offset + 7];
+            tri2dInfo.radius   = this._array[offset + 8];
+        }
+
+        public transformAndStoreToTri2DInfo(index: number, tri2dInfo: Tri2DInfo, transform: Matrix) {
+            if (index >= this._count) {
+                throw new Error(`Can't fetch the triangle at index ${index}, max index is ${this._count - 1}`);
+            }
+
+            let offset = index * 9;
+            tri2dInfo.a.x = this._array[offset + 0];
+            tri2dInfo.a.y = this._array[offset + 1];
+            tri2dInfo.b.x = this._array[offset + 2];
+            tri2dInfo.b.y = this._array[offset + 3];
+            tri2dInfo.c.x = this._array[offset + 4];
+            tri2dInfo.c.y = this._array[offset + 5];
+
+            tri2dInfo.transformInPlace(transform);
+        }
+
+        public get count(): number {
+            return this._count;
+        }
+
+        public doesContain(p: Vector2): boolean {
+            Tri2DArray._checkInitStatics();
+            let a = Tri2DArray.tempT[0];
+
+            for (let i = 0; i < this.count; i++) {
+                this.storeToTri2DInfo(i, a);
+                if (a.doesContain(p)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public static doesIntersect(setA: Tri2DArray, setB: Tri2DArray, bToATransform: Matrix): boolean {
+            Tri2DArray._checkInitStatics();
+
+            let a = Tri2DArray.tempT[0];
+            let b = Tri2DArray.tempT[1];
+            let v0 = Tri2DArray.tempV[0];
+
+            for (let curB = 0; curB < setB.count; curB++) {
+                setB.transformAndStoreToTri2DInfo(curB, b, bToATransform);
+
+                for (let curA = 0; curA < setA.count; curA++) {
+                    setA.storeToTri2DInfo(curA, a);
+
+
+                    // Fast rejection first
+                    v0.x = a.center.x - b.center.x;
+                    v0.y = a.center.y - b.center.y;
+                    if (v0.lengthSquared() > ((a.radius * a.radius) + (b.radius * b.radius))) {
+                        continue;
+                    }
+
+                    // Actual intersection test
+                    if (Math2D.TriangleTriangleDosIntersect(a.a, a.b, a.c, b.a, b.b, b.c)) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private static _checkInitStatics() {
+            if (Tri2DArray.tempT !== null) {
+                return;
+            }
+
+            Tri2DArray.tempT = new Array<Tri2DInfo>(2);
+            Tri2DArray.tempT[0] = new Tri2DInfo(null, null, null);
+            Tri2DArray.tempT[1] = new Tri2DInfo(null, null, null);
+
+            Tri2DArray.tempV = new Array<Vector2>(6);
+            for (let i = 0; i < 6; i++) {
+                Tri2DArray.tempV[i] = Vector2.Zero();
+            }
+        }
+
+        private _count: number;
+        private _array: Float32Array;
+
+        private static tempV: Vector2[] = null;
+        private static tempT: Tri2DInfo[] = null;
+    }
+
+    class Math2D {
+
+        static Dot(a: Vector2, b: Vector2): number {
+            return a.x * b.x + a.y * b.y;
+        }
+
+        // From http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
+        // Note: this one might also be considered with the above one proves to not be good enough: http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
+        static LineLineDoesIntersect(segA1: Vector2, segA2: Vector2, segB1: Vector2, segB2: Vector2): boolean {
+            let s1_x = segA2.x - segA1.x; let s1_y = segA2.y - segA1.y;
+            let s2_x = segB2.x - segB1.x; let s2_y = segB2.y - segB1.y;
+            let s = (-s1_y * (segA1.x - segB1.x) + s1_x * (segA1.y - segB1.y)) / (-s2_x * s1_y + s1_x * s2_y);
+            let t = ( s2_x * (segA1.y - segB1.y) - s2_y * (segA1.x - segB1.x)) / (-s2_x * s1_y + s1_x * s2_y);
+
+            if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
+                return true;
+            }
+
+            return false;
+        }
+
+        // From http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
+        static LineLineIntersection(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2): { res: boolean, xr: number, yr: number } {
+            let s1_x = p1.x - p0.x; let s1_y = p1.y - p0.y;
+            let s2_x = p3.x - p2.x; let s2_y = p3.y - p2.y;
+            let s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / (-s2_x * s1_y + s1_x * s2_y);
+            let t = (s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / (-s2_x * s1_y + s1_x * s2_y);
+
+            if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
+                return { res: true, xr: p0.x + (t * s1_x), yr: p0.y + (t * s1_y) };
+            }
+
+            return { res: false, xr: 0, yr: 0 };
+        }
+
+        private static v0 = Vector2.Zero();
+        private static v1 = Vector2.Zero();
+        private static v2 = Vector2.Zero();
+
+        // Tell me that it's slow and I'll answer: yes it is!
+        // If you fancy to implement the SAT (Separating Axis Theorem) version: BE MY VERY WELCOMED GUEST!
+        static TriangleTriangleDosIntersect(tri1A: Vector2, tri1B: Vector2, tri1C: Vector2, tri2A: Vector2, tri2B: Vector2, tri2C: Vector2): boolean {
+            if (Math2D.LineLineDoesIntersect(tri1A, tri1B, tri2A, tri2B))     return true;
+            if (Math2D.LineLineDoesIntersect(tri1A, tri1B, tri2A, tri2C))     return true;
+            if (Math2D.LineLineDoesIntersect(tri1A, tri1B, tri2B, tri2C))     return true;
+
+            if (Math2D.LineLineDoesIntersect(tri1A, tri1C, tri2A, tri2B))     return true;
+            if (Math2D.LineLineDoesIntersect(tri1A, tri1C, tri2A, tri2C))     return true;
+            if (Math2D.LineLineDoesIntersect(tri1A, tri1C, tri2B, tri2C))     return true;
+
+            if (Math2D.LineLineDoesIntersect(tri1B, tri1C, tri2A, tri2B))     return true;
+            if (Math2D.LineLineDoesIntersect(tri1B, tri1C, tri2A, tri2C))     return true;
+            if (Math2D.LineLineDoesIntersect(tri1B, tri1C, tri2B, tri2C))     return true;
+
+            if (Vector2.PointInTriangle(tri2A, tri1A, tri1B, tri1C) &&
+                Vector2.PointInTriangle(tri2B, tri1A, tri1B, tri1C) &&
+                Vector2.PointInTriangle(tri2C, tri1A, tri1B, tri1C))           return true;
+
+            if (Vector2.PointInTriangle(tri1A, tri2A, tri2B, tri2C) &&
+                Vector2.PointInTriangle(tri1B, tri2A, tri2B, tri2C) &&
+                Vector2.PointInTriangle(tri1C, tri2A, tri2B, tri2C))           return true;
+
+            return false;
+        }
+    }
+
+}

+ 8 - 6
canvas2D/src/Tools/babylon.observableStringDictionary.ts

@@ -129,7 +129,7 @@
          * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
          * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
          */
          */
         public add(key: string, value: T): boolean {
         public add(key: string, value: T): boolean {
-            return this._add(key, value, true, true);
+            return this._add(key, value, true, this._watchObjectsPropertyChange);
         }
         }
 
 
         public getAndRemove(key: string): T {
         public getAndRemove(key: string): T {
@@ -218,11 +218,13 @@
          * Clear the whole content of the dictionary
          * Clear the whole content of the dictionary
          */
          */
         public clear() {
         public clear() {
-            this._watchedObjectList.forEach((k, v) => {
-                let el = this.get(k);
-                this._removeWatchedElement(k, el);
-            });
-            this._watchedObjectList.clear();
+            if (this._watchedObjectList) {
+                this._watchedObjectList.forEach((k, v) => {
+                    let el = this.get(k);
+                    this._removeWatchedElement(k, el);
+                });
+                this._watchedObjectList.clear();
+            }
 
 
             let oldCount = this.count;
             let oldCount = this.count;
             super.clear();
             super.clear();

+ 1 - 1
canvas2D/src/shaders/text2d.fragment.fx

@@ -19,7 +19,7 @@ void main(void) {
 	//float opacity = dist * smoothstep(edgeDistance - edgeWidth, edgeDistance + edgeWidth, dist);
 	//float opacity = dist * smoothstep(edgeDistance - edgeWidth, edgeDistance + edgeWidth, dist);
 
 
 	//float opacity = smoothstep(0.25, 0.75, dist);
 	//float opacity = smoothstep(0.25, 0.75, dist);
-	gl_FragColor = vec4(vColor.xyz*dist, 1.0);
+	gl_FragColor = vec4(vColor.xyz*dist, vColor.a);
 #else
 #else
 	vec4 color = texture2D(diffuseSampler, vUV);
 	vec4 color = texture2D(diffuseSampler, vUV);
 	gl_FragColor = color*vColor;
 	gl_FragColor = color*vColor;

+ 5 - 0
canvas2D/src/shaders/wireframe2d.fragment.fx

@@ -0,0 +1,5 @@
+varying vec4 vColor;
+
+void main(void) {
+	gl_FragColor = vColor;
+}

+ 40 - 0
canvas2D/src/shaders/wireframe2d.vertex.fx

@@ -0,0 +1,40 @@
+// based on if Instanced Array are supported or not, declare the field either as attribute or uniform
+#ifdef Instanced
+#define att attribute
+#else
+#define att uniform
+#endif
+
+// Attributes
+attribute vec2 pos;
+attribute vec4 col;
+
+// x, y, z are
+//  x : alignedToPixel: 1.0 === yes, otherwise no
+//  y : 1/renderGroup.Width
+//  z : 1/renderGroup.height
+att vec3 properties;
+
+att vec2 zBias;
+att vec4 transformX;
+att vec4 transformY;
+att float opacity;
+
+// Uniforms
+
+// Output
+varying vec4 vColor;
+
+void main(void) {
+
+	vec4 p = vec4(pos.xy, 1.0, 1.0);
+	vColor = vec4(col.xyz, col.w*opacity);
+
+	vec4 pp = vec4(dot(p, transformX), dot(p, transformY), zBias.x, 1);
+	
+	if (properties.x == 1.0) {
+		pp.xy = pp.xy - mod(pp.xy, properties.yz) + (properties.yz*0.5);
+	}
+
+	gl_Position = pp;
+}	

Datei-Diff unterdrückt, da er zu groß ist
+ 26 - 26
dist/preview release/babylon.core.js


Datei-Diff unterdrückt, da er zu groß ist
+ 963 - 832
dist/preview release/babylon.d.ts


Datei-Diff unterdrückt, da er zu groß ist
+ 36 - 36
dist/preview release/babylon.js


Datei-Diff unterdrückt, da er zu groß ist
+ 529 - 353
dist/preview release/babylon.max.js


Datei-Diff unterdrückt, da er zu groß ist
+ 35 - 35
dist/preview release/babylon.noworker.js


+ 449 - 13
dist/preview release/canvas2D/babylon.canvas2d.d.ts

@@ -1,4 +1,43 @@
 declare module BABYLON {
 declare module BABYLON {
+    class Tri2DInfo {
+        /**
+         * Construct an instance of Tri2DInfo, you can either pass null to a, b and c and the instance will be allocated "clear", or give actual triangle info and the center/radius will be computed
+         */
+        constructor(a: Vector2, b: Vector2, c: Vector2);
+        a: Vector2;
+        b: Vector2;
+        c: Vector2;
+        center: Vector2;
+        radius: number;
+        static Zero(): Tri2DInfo;
+        set(a: Vector2, b: Vector2, c: Vector2): void;
+        transformInPlace(transform: Matrix): void;
+        doesContain(p: Vector2): boolean;
+        private _updateCenterRadius();
+    }
+    class Tri2DArray {
+        constructor(count: number);
+        clear(count: number): void;
+        storeTriangle(index: number, a: Vector2, b: Vector2, c: Vector2): void;
+        /**
+         * Store a triangle in a Tri2DInfo object
+         * @param index the index of the triangle to store
+         * @param tri2dInfo the instance that will contain the data, it must be already allocated with its inner object also allocated
+         */
+        storeToTri2DInfo(index: number, tri2dInfo: Tri2DInfo): void;
+        transformAndStoreToTri2DInfo(index: number, tri2dInfo: Tri2DInfo, transform: Matrix): void;
+        readonly count: number;
+        doesContain(p: Vector2): boolean;
+        static doesIntersect(setA: Tri2DArray, setB: Tri2DArray, bToATransform: Matrix): boolean;
+        private static _checkInitStatics();
+        private _count;
+        private _array;
+        private static tempV;
+        private static tempT;
+    }
+}
+
+declare module BABYLON {
     class PropertyChangedInfo {
     class PropertyChangedInfo {
         /**
         /**
          * Previous value of the property
          * Previous value of the property
@@ -719,6 +758,7 @@ declare module BABYLON {
          * @return a new instance containing the result of the union
          * @return a new instance containing the result of the union
          */
          */
         union(other: BoundingInfo2D): BoundingInfo2D;
         union(other: BoundingInfo2D): BoundingInfo2D;
+        worldAABBIntersectionTest(other: BoundingInfo2D): boolean;
         private static _transform;
         private static _transform;
         /**
         /**
          * 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.
@@ -727,6 +767,15 @@ declare module BABYLON {
          * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
          * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
          */
          */
         transformToRef(matrix: Matrix, result: BoundingInfo2D): void;
         transformToRef(matrix: Matrix, result: BoundingInfo2D): void;
+        private _updateWorldAABB(worldMatrix);
+        worldMatrixAccess: () => Matrix;
+        readonly worldAABBDirtyObservable: Observable<BoundingInfo2D>;
+        readonly isWorldAABBDirty: boolean;
+        dirtyWorldAABB(): void;
+        /**
+         * Retrieve the world AABB, the Vector4's data is x=xmin, y=ymin, z=xmax, w=ymax
+         */
+        readonly worldAABB: Vector4;
         /**
         /**
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
          * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
          * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it 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.
@@ -741,10 +790,89 @@ declare module BABYLON {
          * @return true if the point is inside, false otherwise
          * @return true if the point is inside, false otherwise
          */
          */
         doesIntersect(pickPosition: Vector2): boolean;
         doesIntersect(pickPosition: Vector2): boolean;
+        private _worldAABBDirtyObservable;
+        private _worldAABBDirty;
+        private _worldAABB;
     }
     }
 }
 }
 
 
 declare module BABYLON {
 declare module BABYLON {
+    /**
+     * The base class for all implementation of a Primitive Collision Manager
+     */
+    abstract class PrimitiveCollisionManagerBase {
+        constructor(owner: Canvas2D);
+        abstract _addActor(actor: Prim2DBase, deep: boolean): ActorInfoBase;
+        abstract _removeActor(actor: Prim2DBase): any;
+        abstract _update(): any;
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the left border
+         */
+        readonly abstract leftBorderIntersectedActors: ObservableStringDictionary<Prim2DBase>;
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the bottom border
+         */
+        readonly abstract bottomBorderIntersectedActors: ObservableStringDictionary<Prim2DBase>;
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the right border
+         */
+        readonly abstract rightBorderIntersectedActors: ObservableStringDictionary<Prim2DBase>;
+        /**
+         * If collisionManagerUseBorders is true during the Canvas creation, this dictionary contains all the primitives intersecting with the top border
+         */
+        readonly abstract topBorderIntersectedActors: ObservableStringDictionary<Prim2DBase>;
+        /**
+         * This dictionary contains all the couple of intersecting primitives
+         */
+        readonly abstract intersectedActors: ObservableStringDictionary<{
+            a: Prim2DBase;
+            b: Prim2DBase;
+        }>;
+        /**
+         * Renders the World AABB of all Actors
+         */
+        abstract debugRenderAABB: boolean;
+        /**
+         * Renders the area of the Clusters
+         */
+        abstract debugRenderClusters: boolean;
+        /**
+         * Display stats about the PCM on screen
+         */
+        abstract debugStats: boolean;
+        static allocBasicPCM(owner: Canvas2D, enableBorders: boolean): PrimitiveCollisionManagerBase;
+        protected _owner: Canvas2D;
+    }
+    /**
+     * Base class of an Actor
+     */
+    abstract class ActorInfoBase {
+        /**
+         * Access the World AABB of the Actor, the vector4 is x:left, y: bottom, z: right, w: top
+         */
+        readonly abstract worldAABB: Vector4;
+        /**
+         * Return true if the actor is enable, false otherwise
+         */
+        readonly abstract isEnabled: boolean;
+        /**
+         * Return true is the actor boundingInfo is use, false if its levelBoundingInfo is used.
+         */
+        readonly abstract isDeep: boolean;
+        /**
+         * Return the primitive of the actor
+         */
+        readonly abstract prim: Prim2DBase;
+        /**
+         * Return a dictionary containing all the actors intersecting with this one
+         */
+        readonly abstract intersectWith: ObservableStringDictionary<ActorInfoBase>;
+    }
+}
+
+declare module BABYLON {
+    interface ILayoutData {
+    }
     class LayoutEngineBase implements ILockable {
     class LayoutEngineBase implements ILockable {
         constructor();
         constructor();
         updateLayout(prim: Prim2DBase): void;
         updateLayout(prim: Prim2DBase): void;
@@ -755,7 +883,9 @@ declare module BABYLON {
         private _isLocked;
         private _isLocked;
     }
     }
     class CanvasLayoutEngine extends LayoutEngineBase {
     class CanvasLayoutEngine extends LayoutEngineBase {
-        static Singleton: CanvasLayoutEngine;
+        private static _singleton;
+        static readonly Singleton: CanvasLayoutEngine;
+        constructor();
         updateLayout(prim: Prim2DBase): void;
         updateLayout(prim: Prim2DBase): void;
         private _doUpdate(prim);
         private _doUpdate(prim);
         readonly isChildPositionAllowed: boolean;
         readonly isChildPositionAllowed: boolean;
@@ -773,6 +903,59 @@ declare module BABYLON {
         updateLayout(prim: Prim2DBase): void;
         updateLayout(prim: Prim2DBase): void;
         readonly isChildPositionAllowed: boolean;
         readonly isChildPositionAllowed: boolean;
     }
     }
+    /**
+     * GridData is used specify what row(s) and column(s) a primitive is placed in when its parent is using a Grid Panel Layout.
+     */
+    class GridData implements ILayoutData {
+        /**
+         * the row number of the grid
+         **/
+        row: number;
+        /**
+         * the column number of the grid
+         **/
+        column: number;
+        /**
+         * the number of rows a primitive will occupy
+         **/
+        rowSpan: number;
+        /**
+         * the number of columns a primitive will occupy
+         **/
+        columnSpan: number;
+        /**
+         * Create a Grid Data that describes where a primitive will be placed in a Grid Panel Layout.
+         * @param row the row number of the grid
+         * @param column the column number of the grid
+         * @param rowSpan the number of rows a primitive will occupy
+         * @param columnSpan the number of columns a primitive will occupy
+         **/
+        constructor(row: number, column: number, rowSpan?: number, columnSpan?: number);
+    }
+    class GridPanelLayoutEngine extends LayoutEngineBase {
+        constructor(settings: {
+            rows: [{
+                height: string;
+            }];
+            columns: [{
+                width: string;
+            }];
+        });
+        private _rows;
+        private _columns;
+        private _children;
+        private _rowBottoms;
+        private _columnLefts;
+        private _rowHeights;
+        private _columnWidths;
+        private static dstOffset;
+        private static dstArea;
+        updateLayout(prim: Prim2DBase): void;
+        readonly isChildPositionAllowed: boolean;
+        private _getMaxChildHeightInRow(rowNum);
+        private _getMaxChildWidthInColumn(colNum);
+        private _updateGrid(prim);
+    }
 }
 }
 
 
 declare module BABYLON {
 declare module BABYLON {
@@ -1125,6 +1308,10 @@ declare module BABYLON {
          */
          */
         animations: Animation[];
         animations: Animation[];
         /**
         /**
+         * return a unique identifier for the Canvas2D
+         */
+        readonly uid: string;
+        /**
          * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
          * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
          * Look at Sprite2D for more information
          * Look at Sprite2D for more information
          */
          */
@@ -1214,6 +1401,9 @@ declare module BABYLON {
         static flagDontInheritParentScale: number;
         static flagDontInheritParentScale: number;
         static flagGlobalTransformDirty: number;
         static flagGlobalTransformDirty: number;
         static flagLayoutBoundingInfoDirty: number;
         static flagLayoutBoundingInfoDirty: number;
+        static flagCollisionActor: number;
+        static flagModelUpdate: number;
+        private _uid;
         private _flags;
         private _flags;
         private _modelKey;
         private _modelKey;
         protected _levelBoundingInfo: BoundingInfo2D;
         protected _levelBoundingInfo: BoundingInfo2D;
@@ -1641,7 +1831,7 @@ declare module BABYLON {
          * @param dstOffset the position of the content, x, y, z, w are left, bottom, right, top
          * @param dstOffset the position of the content, x, y, z, w are left, bottom, right, top
          * @param dstArea the new size of the content
          * @param dstArea the new size of the content
          */
          */
-        computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, dstOffset: Vector4, dstArea: Size, computeLayoutArea?: boolean): void;
+        computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, contentScale: Vector2, dstOffset: Vector4, dstArea: Size, computeLayoutArea?: boolean): void;
         /**
         /**
          * Compute an area and its position considering this thickness properties based on a given source area
          * Compute an area and its position considering this thickness properties based on a given source area
          * @param sourceArea the source area
          * @param sourceArea the source area
@@ -1714,6 +1904,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -1728,6 +1921,16 @@ declare module BABYLON {
             paddingBottom?: number | string;
             paddingBottom?: number | string;
             padding?: string;
             padding?: string;
         });
         });
+        /**
+         * Return the ChangedDictionary observable of the StringDictionary containing the primitives intersecting with this one
+         */
+        readonly intersectWithObservable: Observable<DictionaryChanged<ActorInfoBase>>;
+        /**
+         * Return the ObservableStringDictionary containing all the primitives intersecting with this one.
+         * The key is the primitive uid, the value is the ActorInfo object
+         * @returns {}
+         */
+        readonly intersectWith: ObservableStringDictionary<ActorInfoBase>;
         readonly actionManager: ActionManager;
         readonly actionManager: ActionManager;
         /**
         /**
          * From 'this' primitive, traverse up (from parent to parent) until the given predicate is true
          * From 'this' primitive, traverse up (from parent to parent) until the given predicate is true
@@ -2069,6 +2272,7 @@ declare module BABYLON {
         readonly pointerEventObservable: Observable<PrimitivePointerInfo>;
         readonly pointerEventObservable: Observable<PrimitivePointerInfo>;
         readonly zActualOrderChangedObservable: Observable<number>;
         readonly zActualOrderChangedObservable: Observable<number>;
         displayDebugAreas: boolean;
         displayDebugAreas: boolean;
+        private static _updatingDebugArea;
         private _updateDebugArea();
         private _updateDebugArea();
         findById(id: string): Prim2DBase;
         findById(id: string): Prim2DBase;
         protected onZOrderChanged(): void;
         protected onZOrderChanged(): void;
@@ -2084,12 +2288,15 @@ declare module BABYLON {
          * @param pointerId the Id of the pointer to release the capture from.
          * @param pointerId the Id of the pointer to release the capture from.
          */
          */
         releasePointerEventsCapture(pointerId: number): boolean;
         releasePointerEventsCapture(pointerId: number): boolean;
+        private static _bypassGroup2DExclusion;
         /**
         /**
          * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
          * Make an intersection test with the primitive, all inputs/outputs are stored in the IntersectInfo2D class, see its documentation for more information.
          * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
          * @param intersectInfo contains the settings of the intersection to perform, to setup before calling this method as well as the result, available after a call to this method.
          */
          */
-        private static _bypassGroup2DExclusion;
         intersect(intersectInfo: IntersectInfo2D): boolean;
         intersect(intersectInfo: IntersectInfo2D): boolean;
+        intersectOtherPrim(other: Prim2DBase): boolean;
+        readonly triList: Tri2DArray;
+        protected updateTriArray(): void;
         /**
         /**
          * Move a child object into a new position regarding its siblings to change its rendering order.
          * Move a child object into a new position regarding its siblings to change its rendering order.
          * You can also use the shortcut methods to move top/bottom: moveChildToTop, moveChildToBottom, moveToTop, moveToBottom.
          * You can also use the shortcut methods to move top/bottom: moveChildToTop, moveChildToBottom, moveToTop, moveToBottom.
@@ -2177,6 +2384,10 @@ declare module BABYLON {
          * @param newPrimSize the new size of the primitive. PLEASE ROUND THE values, we're talking about pixels and fraction of them are not our friends!
          * @param newPrimSize the new size of the primitive. PLEASE ROUND THE values, we're talking about pixels and fraction of them are not our friends!
          */
          */
         protected _getActualSizeFromContentToRef(primSize: Size, newPrimSize: Size): void;
         protected _getActualSizeFromContentToRef(primSize: Size, newPrimSize: Size): void;
+        /**
+         * Get/set the layout data to use for this primitive.
+         */
+        layoutData: ILayoutData;
         private _owner;
         private _owner;
         private _parent;
         private _parent;
         private _actionManager;
         private _actionManager;
@@ -2210,6 +2421,7 @@ declare module BABYLON {
         private _lastAutoSizeArea;
         private _lastAutoSizeArea;
         private _layoutAreaPos;
         private _layoutAreaPos;
         private _layoutArea;
         private _layoutArea;
+        private _layoutData;
         private _contentArea;
         private _contentArea;
         private _rotation;
         private _rotation;
         private _scale;
         private _scale;
@@ -2219,12 +2431,15 @@ declare module BABYLON {
         private _actualScale;
         private _actualScale;
         private _displayDebugAreas;
         private _displayDebugAreas;
         private _debugAreaGroup;
         private _debugAreaGroup;
+        private _actorInfo;
         protected _parentTransformStep: number;
         protected _parentTransformStep: number;
         protected _globalTransformStep: number;
         protected _globalTransformStep: number;
         protected _globalTransformProcessStep: number;
         protected _globalTransformProcessStep: number;
         protected _localTransform: Matrix;
         protected _localTransform: Matrix;
         protected _globalTransform: Matrix;
         protected _globalTransform: Matrix;
         protected _invGlobalTransform: Matrix;
         protected _invGlobalTransform: Matrix;
+        protected _primTriArrayDirty: boolean;
+        protected _primTriArray: Tri2DArray;
     }
     }
 }
 }
 
 
@@ -2293,6 +2508,7 @@ declare module BABYLON {
         readonly isDisposed: boolean;
         readonly isDisposed: boolean;
         addRef(): number;
         addRef(): number;
         readonly modelKey: string;
         readonly modelKey: string;
+        updateModelRenderCache(prim: Prim2DBase): boolean;
         /**
         /**
          * Render the model instances
          * Render the model instances
          * @param instanceInfo
          * @param instanceInfo
@@ -2541,6 +2757,9 @@ declare module BABYLON {
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -2567,6 +2786,7 @@ declare module BABYLON {
             scaleY?: number;
             scaleY?: number;
             dontInheritParentScale?: boolean;
             dontInheritParentScale?: boolean;
             trackNode?: Node;
             trackNode?: Node;
+            trackNodeOffset?: Vector3;
             opacity?: number;
             opacity?: number;
             zOrder?: number;
             zOrder?: number;
             origin?: Vector2;
             origin?: Vector2;
@@ -2579,6 +2799,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -2641,6 +2864,10 @@ declare module BABYLON {
          * Get/set the Scene's Node that should be tracked, the group's position will follow the projected position of the Node.
          * Get/set the Scene's Node that should be tracked, the group's position will follow the projected position of the Node.
          */
          */
         trackedNode: Node;
         trackedNode: Node;
+        /**
+         * Get/set the offset of the tracked node in the tracked node's local space.
+         */
+        trackedNodeOffset: Vector3;
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
         protected updateLevelBoundingInfo(): boolean;
         protected updateLevelBoundingInfo(): boolean;
         protected _prepareGroupRender(context: PrepareRender2DContext): void;
         protected _prepareGroupRender(context: PrepareRender2DContext): void;
@@ -2658,7 +2885,9 @@ declare module BABYLON {
         protected static _unS: Vector2;
         protected static _unS: Vector2;
         protected handleGroupChanged(prop: Prim2DPropInfo): void;
         protected handleGroupChanged(prop: Prim2DPropInfo): void;
         private detectGroupStates();
         private detectGroupStates();
+        readonly _cachedTexture: MapTexture;
         private _trackedNode;
         private _trackedNode;
+        private _trackedNodeOffset;
         protected _isRenderableGroup: boolean;
         protected _isRenderableGroup: boolean;
         protected _isCachedGroup: boolean;
         protected _isCachedGroup: boolean;
         private _cacheGroupDirty;
         private _cacheGroupDirty;
@@ -2699,6 +2928,178 @@ declare module BABYLON {
 }
 }
 
 
 declare module BABYLON {
 declare module BABYLON {
+    class WireFrame2DRenderCache extends ModelRenderCache {
+        effectsReady: boolean;
+        vb: WebGLBuffer;
+        vtxCount: number;
+        instancingAttributes: InstancingAttributeInfo[];
+        effect: Effect;
+        effectInstanced: Effect;
+        render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean;
+        updateModelRenderCache(prim: Prim2DBase): boolean;
+        dispose(): boolean;
+    }
+    class WireFrameVertex2D {
+        x: number;
+        y: number;
+        r: number;
+        g: number;
+        b: number;
+        a: number;
+        constructor(p: Vector2, c?: Color4);
+        fromVector2(p: Vector2): void;
+        fromColor3(c: Color3): void;
+        fromColor4(c: Color4): void;
+    }
+    class WireFrameGroup2D {
+        /**
+         * Construct a WireFrameGroup2D object
+         * @param id a unique ID among the Groups added to a given WireFrame2D primitive, if you don't specify an id, a random one will be generated. The id is immutable.
+         * @param defaultColor specify the default color that will be used when a vertex is pushed, white will be used if not specified.
+         */
+        constructor(id?: string, defaultColor?: Color4);
+        readonly uid: string;
+        /**
+         * Retrieve the ID of the group
+         */
+        readonly id: string;
+        /**
+         * Push a vertex in the array of vertices.
+         * If you're previously called startLineStrip, the vertex will be pushed twice in order to describe the end of a line and the start of a new one.
+         * @param p Position of the vertex
+         * @param c Color of the vertex, if null the default color of the group will be used
+         */
+        pushVertex(p: Vector2, c?: Color4): void;
+        /**
+         * Start to store a Line Strip. The given vertex will be pushed in the array. The you have to call pushVertex to add subsequent vertices describing the strip and don't forget to call endLineStrip to close the strip!!!
+         * @param p Position of the vertex
+         * @param c Color of the vertex, if null the default color of the group will be used
+         */
+        startLineStrip(p: Vector2, c?: Color4): void;
+        /**
+         * Close the Strip by storing a last vertex
+         * @param p Position of the vertex
+         * @param c Color of the vertex, if null the default color of the group will be used
+         */
+        endLineStrip(p: Vector2, c?: Color4): void;
+        /**
+         * Access to the array of Vertices, you can manipulate its content but BEWARE of what you're doing!
+         */
+        readonly vertices: Array<WireFrameVertex2D>;
+        private _uid;
+        private _id;
+        private _defaultColor;
+        private _vertices;
+        private _buildingStrip;
+    }
+    class WireFrame2D extends RenderablePrim2D {
+        static WIREFRAME2D_MAINPARTID: number;
+        static wireFrameGroupsProperty: Prim2DPropInfo;
+        readonly wireFrameGroups: StringDictionary<WireFrameGroup2D>;
+        /**
+         * If you change the content of the wireFrameGroups you MUST call this method for the changes to be reflected during rendering
+         */
+        wireFrameGroupsDirty(): void;
+        size: Size;
+        actualSize: Size;
+        protected updateLevelBoundingInfo(): boolean;
+        protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
+        /**
+         * Create an WireFrame 2D primitive
+         * @param wireFrameGroups an array of WireFrameGroup.
+         * @param settings a combination of settings, possible ones are
+         * - parent: the parent primitive/canvas, must be specified if the primitive is not constructed as a child of another one (i.e. as part of the children array setting)
+         * - children: an array of direct children
+         * - id a text identifier, for information purpose
+         * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
+         * - rotation: the initial rotation (in radian) of the primitive. default is 0
+         * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
+         * - size: the size of the sprite displayed in the canvas, if not specified the spriteSize will be used
+         * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
+         * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
+         * - zOrder: override the zOrder with the specified value
+         * - origin: define the normalized origin point location, default [0.5;0.5]
+         * - alignToPixel: the rendered lines will be aligned to the rendering device' pixels
+         * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
+         * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
+         * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
+         * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
+         * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - marginBottom: bottom margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - margin: top, left, right and bottom margin formatted as a single string (see PrimitiveThickness.fromString)
+         * - marginHAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginVAlignment: one value of the PrimitiveAlignment type's static properties
+         * - marginAlignment: a string defining the alignment, see PrimitiveAlignment.fromString
+         * - paddingTop: top padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingLeft: left padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
+         * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         */
+        constructor(wireFrameGroups: Array<WireFrameGroup2D>, settings?: {
+            parent?: Prim2DBase;
+            children?: Array<Prim2DBase>;
+            id?: string;
+            position?: Vector2;
+            x?: number;
+            y?: number;
+            rotation?: number;
+            size?: Size;
+            scale?: number;
+            scaleX?: number;
+            scaleY?: number;
+            dontInheritParentScale?: boolean;
+            opacity?: number;
+            zOrder?: number;
+            origin?: Vector2;
+            alignToPixel?: boolean;
+            isVisible?: boolean;
+            isPickable?: boolean;
+            isContainer?: boolean;
+            childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
+            marginTop?: number | string;
+            marginLeft?: number | string;
+            marginRight?: number | string;
+            marginBottom?: number | string;
+            margin?: number | string;
+            marginHAlignment?: number;
+            marginVAlignment?: number;
+            marginAlignment?: string;
+            paddingTop?: number | string;
+            paddingLeft?: number | string;
+            paddingRight?: number | string;
+            paddingBottom?: number | string;
+            padding?: string;
+        });
+        /**
+         * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
+         */
+        alignToPixel: boolean;
+        protected createModelRenderCache(modelKey: string): ModelRenderCache;
+        protected setupModelRenderCache(modelRenderCache: ModelRenderCache): WireFrame2DRenderCache;
+        _updateVertexBuffer(mrc: WireFrame2DRenderCache): void;
+        protected refreshInstanceDataPart(part: InstanceDataBase): boolean;
+        private _computeMinMaxTrans();
+        protected createInstanceDataParts(): InstanceDataBase[];
+        private _vtxTransparent;
+        private _wireFrameGroups;
+        private _alignToPixel;
+    }
+    class WireFrame2DInstanceData extends InstanceDataBase {
+        constructor(partId: number);
+        properties: Vector3;
+    }
+}
+
+declare module BABYLON {
     class Rectangle2DRenderCache extends ModelRenderCache {
     class Rectangle2DRenderCache extends ModelRenderCache {
         effectsReady: boolean;
         effectsReady: boolean;
         fillVB: WebGLBuffer;
         fillVB: WebGLBuffer;
@@ -2755,6 +3156,9 @@ declare module BABYLON {
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -2795,6 +3199,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -2811,6 +3218,7 @@ declare module BABYLON {
         });
         });
         static roundSubdivisions: number;
         static roundSubdivisions: number;
         protected createModelRenderCache(modelKey: string): ModelRenderCache;
         protected createModelRenderCache(modelKey: string): ModelRenderCache;
+        protected updateTriArray(): void;
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Rectangle2DRenderCache;
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Rectangle2DRenderCache;
         protected _getInitialContentAreaToRef(primSize: Size, initialContentPosition: Vector4, initialContentArea: Size): void;
         protected _getInitialContentAreaToRef(primSize: Size, initialContentPosition: Vector4, initialContentArea: Size): void;
         protected _getActualSizeFromContentToRef(primSize: Size, newPrimSize: Size): void;
         protected _getActualSizeFromContentToRef(primSize: Size, newPrimSize: Size): void;
@@ -2873,6 +3281,9 @@ declare module BABYLON {
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -2913,6 +3324,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -2927,6 +3341,7 @@ declare module BABYLON {
             paddingBottom?: number | string;
             paddingBottom?: number | string;
             padding?: string;
             padding?: string;
         });
         });
+        protected updateTriArray(): void;
         protected createModelRenderCache(modelKey: string): ModelRenderCache;
         protected createModelRenderCache(modelKey: string): ModelRenderCache;
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Ellipse2DRenderCache;
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Ellipse2DRenderCache;
         protected createInstanceDataParts(): InstanceDataBase[];
         protected createInstanceDataParts(): InstanceDataBase[];
@@ -3003,6 +3418,9 @@ declare module BABYLON {
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -3043,6 +3461,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -3355,6 +3776,9 @@ declare module BABYLON {
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -3399,6 +3823,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -3546,6 +3973,9 @@ declare module BABYLON {
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
+         * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
+         * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
+         * - layoutData: a instance of a class implementing the ILayoutData interface that contain data to pass to the primitive parent's layout engine
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginTop: top margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginLeft: left margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - marginRight: right margin, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
@@ -3586,6 +4016,9 @@ declare module BABYLON {
             isPickable?: boolean;
             isPickable?: boolean;
             isContainer?: boolean;
             isContainer?: boolean;
             childrenFlatZOrder?: boolean;
             childrenFlatZOrder?: boolean;
+            levelCollision?: boolean;
+            deepCollision?: boolean;
+            layoutData?: ILayoutData;
             marginTop?: number | string;
             marginTop?: number | string;
             marginLeft?: number | string;
             marginLeft?: number | string;
             marginRight?: number | string;
             marginRight?: number | string;
@@ -3617,6 +4050,7 @@ declare module BABYLON {
         private _buildCap(vb, vbi, ib, ibi, pos, thickness, borderThickness, type, capDir, contour);
         private _buildCap(vb, vbi, ib, ibi, pos, thickness, borderThickness, type, capDir, contour);
         private _buildLine(vb, contour, ht, bt?);
         private _buildLine(vb, contour, ht, bt?);
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Lines2DRenderCache;
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Lines2DRenderCache;
+        protected updateTriArray(): void;
         private _computeLines2D();
         private _computeLines2D();
         readonly size: Size;
         readonly size: Size;
         protected createInstanceDataParts(): InstanceDataBase[];
         protected createInstanceDataParts(): InstanceDataBase[];
@@ -3636,11 +4070,6 @@ declare module BABYLON {
         private _borderIB;
         private _borderIB;
         private _boundingMin;
         private _boundingMin;
         private _boundingMax;
         private _boundingMax;
-        private _contour;
-        private _startCapContour;
-        private _startCapTriIndices;
-        private _endCapContour;
-        private _endCapTriIndices;
         private _closed;
         private _closed;
         private _startCap;
         private _startCap;
         private _endCap;
         private _endCap;
@@ -3701,6 +4130,9 @@ declare module BABYLON {
             isScreenSpace?: boolean;
             isScreenSpace?: boolean;
             cachingStrategy?: number;
             cachingStrategy?: number;
             enableInteraction?: boolean;
             enableInteraction?: boolean;
+            enableCollisionManager?: boolean;
+            customCollisionManager?: (owner: Canvas2D, enableBorders: boolean) => PrimitiveCollisionManagerBase;
+            collisionManagerUseBorders?: boolean;
             origin?: Vector2;
             origin?: Vector2;
             isVisible?: boolean;
             isVisible?: boolean;
             backgroundRoundRadius?: number;
             backgroundRoundRadius?: number;
@@ -3721,6 +4153,7 @@ declare module BABYLON {
         readonly updateGlobalTransformCounter: PerfCounter;
         readonly updateGlobalTransformCounter: PerfCounter;
         readonly boundingInfoRecomputeCounter: PerfCounter;
         readonly boundingInfoRecomputeCounter: PerfCounter;
         static readonly instances: Array<Canvas2D>;
         static readonly instances: Array<Canvas2D>;
+        readonly primitiveCollisionManager: PrimitiveCollisionManagerBase;
         protected _canvasPreInit(settings: any): void;
         protected _canvasPreInit(settings: any): void;
         static _zMinDelta: number;
         static _zMinDelta: number;
         private _setupInteraction(enable);
         private _setupInteraction(enable);
@@ -3774,10 +4207,6 @@ declare module BABYLON {
          */
          */
         readonly engine: Engine;
         readonly engine: Engine;
         /**
         /**
-         * return a unique identifier for the Canvas2D
-         */
-        readonly uid: string;
-        /**
          * And observable called during the Canvas rendering process.
          * And observable called during the Canvas rendering process.
          * This observable is called twice per render, each time with a different mask:
          * This observable is called twice per render, each time with a different mask:
          *  - 1: before render is executed
          *  - 1: before render is executed
@@ -3832,6 +4261,7 @@ declare module BABYLON {
         readonly fitRenderingDevice: boolean;
         readonly fitRenderingDevice: boolean;
         readonly designSize: Size;
         readonly designSize: Size;
         readonly designSizeUseHorizAxis: boolean;
         readonly designSizeUseHorizAxis: boolean;
+        designSizeUseHorizeAxis: boolean;
         /**
         /**
          * Return
          * Return
          */
          */
@@ -3860,7 +4290,6 @@ declare module BABYLON {
         addUpdatePositioningCounter(count: number): void;
         addUpdatePositioningCounter(count: number): void;
         addupdateLocalTransformCounter(count: number): void;
         addupdateLocalTransformCounter(count: number): void;
         addUpdateGlobalTransformCounter(count: number): void;
         addUpdateGlobalTransformCounter(count: number): void;
-        private _uid;
         private _renderObservable;
         private _renderObservable;
         private __engineData;
         private __engineData;
         private _interactionEnabled;
         private _interactionEnabled;
@@ -3901,6 +4330,7 @@ declare module BABYLON {
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         private _designSize;
         private _designSize;
         private _designUseHorizAxis;
         private _designUseHorizAxis;
+        _primitiveCollisionManager: PrimitiveCollisionManagerBase;
         _renderingSize: Size;
         _renderingSize: Size;
         private _drawCallsOpaqueCounter;
         private _drawCallsOpaqueCounter;
         private _drawCallsAlphaTestCounter;
         private _drawCallsAlphaTestCounter;
@@ -4049,6 +4479,9 @@ declare module BABYLON {
             padding?: string;
             padding?: string;
         });
         });
         dispose(): boolean;
         dispose(): boolean;
+        trackNode: Node;
+        trackNodeOffset: Vector3;
+        trackNodeBillboard: boolean;
         private _customWorldSpaceNode;
         private _customWorldSpaceNode;
     }
     }
     class ScreenSpaceCanvas2D extends Canvas2D {
     class ScreenSpaceCanvas2D extends Canvas2D {
@@ -4103,6 +4536,9 @@ declare module BABYLON {
             cachingStrategy?: number;
             cachingStrategy?: number;
             cacheBehavior?: number;
             cacheBehavior?: number;
             enableInteraction?: boolean;
             enableInteraction?: boolean;
+            enableCollisionManager?: boolean;
+            customCollisionManager?: (owner: Canvas2D, enableBorders: boolean) => PrimitiveCollisionManagerBase;
+            collisionManagerUseBorders?: boolean;
             isVisible?: boolean;
             isVisible?: boolean;
             backgroundRoundRadius?: number;
             backgroundRoundRadius?: number;
             backgroundFill?: IBrush2D | string;
             backgroundFill?: IBrush2D | string;

Datei-Diff unterdrückt, da er zu groß ist
+ 2366 - 238
dist/preview release/canvas2D/babylon.canvas2d.js


Datei-Diff unterdrückt, da er zu groß ist
+ 12 - 10
dist/preview release/canvas2D/babylon.canvas2d.min.js


Datei-Diff unterdrückt, da er zu groß ist
+ 3 - 3
dist/preview release/inspector/babylon.inspector.bundle.js


+ 6 - 9
dist/preview release/inspector/babylon.inspector.js

@@ -621,7 +621,8 @@ var INSPECTOR;
             var toAddDirty = [
             var toAddDirty = [
                 'actualZOffset', 'isSizeAuto', 'layoutArea', 'layoutAreaPos', 'contentArea',
                 'actualZOffset', 'isSizeAuto', 'layoutArea', 'layoutAreaPos', 'contentArea',
                 'marginOffset', 'paddingOffset', 'isPickable', 'isContainer', 'boundingInfo',
                 'marginOffset', 'paddingOffset', 'isPickable', 'isContainer', 'boundingInfo',
-                'levelBoundingInfo', 'isSizedByContent', 'isPositionAuto', 'actualScale', 'layoutBoundingInfo'
+                'levelBoundingInfo', 'isSizedByContent', 'isPositionAuto', 'actualScale', 'layoutBoundingInfo',
+                '_cachedTexture'
             ];
             ];
             for (var _i = 0, toAddDirty_1 = toAddDirty; _i < toAddDirty_1.length; _i++) {
             for (var _i = 0, toAddDirty_1 = toAddDirty; _i < toAddDirty_1.length; _i++) {
                 var dirty = toAddDirty_1[_i];
                 var dirty = toAddDirty_1[_i];
@@ -1886,7 +1887,7 @@ var INSPECTOR;
             div.style.display = 'none';
             div.style.display = 'none';
             div.appendChild(clone);
             div.appendChild(clone);
             var value = INSPECTOR.Inspector.WINDOW.getComputedStyle(clone)[cssAttribute];
             var value = INSPECTOR.Inspector.WINDOW.getComputedStyle(clone)[cssAttribute];
-            div.remove();
+            div.parentNode.removeChild(div);
             return value;
             return value;
         };
         };
         Helpers.LoadScript = function () {
         Helpers.LoadScript = function () {
@@ -2713,6 +2714,7 @@ var INSPECTOR;
             _this._bjsPanelContent.innerHTML = BABYLON.Tools.LogCache;
             _this._bjsPanelContent.innerHTML = BABYLON.Tools.LogCache;
             BABYLON.Tools.OnNewCacheEntry = function (entry) {
             BABYLON.Tools.OnNewCacheEntry = function (entry) {
                 _this._bjsPanelContent.innerHTML += entry;
                 _this._bjsPanelContent.innerHTML += entry;
+                _this._bjsPanelContent.scrollTop = _this._bjsPanelContent.scrollHeight;
             };
             };
             return _this;
             return _this;
             // Testing
             // Testing
@@ -2735,13 +2737,8 @@ var INSPECTOR;
             var callerLine = INSPECTOR.Helpers.CreateDiv('caller', this._consolePanelContent);
             var callerLine = INSPECTOR.Helpers.CreateDiv('caller', this._consolePanelContent);
             callerLine.textContent = caller;
             callerLine.textContent = caller;
             var line = INSPECTOR.Helpers.CreateDiv(type, this._consolePanelContent);
             var line = INSPECTOR.Helpers.CreateDiv(type, this._consolePanelContent);
-            if (typeof message === "string") {
-                line.textContent += message;
-            }
-            else {
-                line.textContent += JSON.stringify(message);
-                line.classList.add('object');
-            }
+            line.textContent += message;
+            this._consolePanelContent.scrollTop = this._consolePanelContent.scrollHeight;
         };
         };
         ConsoleTab.prototype._addConsoleLog = function () {
         ConsoleTab.prototype._addConsoleLog = function () {
             var params = [];
             var params = [];

Datei-Diff unterdrückt, da er zu groß ist
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
dist/preview release/proceduralTexturesLibrary/babylon.brickProceduralTexture.js


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
dist/preview release/proceduralTexturesLibrary/babylon.brickProceduralTexture.min.js


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
dist/preview release/proceduralTexturesLibrary/babylon.marbleProceduralTexture.js


Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
dist/preview release/proceduralTexturesLibrary/babylon.marbleProceduralTexture.min.js


+ 2 - 1
inspector/src/adapters/Canvas2DAdapter.ts

@@ -36,7 +36,8 @@ module INSPECTOR {
             let toAddDirty = [
             let toAddDirty = [
                 'actualZOffset', 'isSizeAuto', 'layoutArea', 'layoutAreaPos', 'contentArea', 
                 'actualZOffset', 'isSizeAuto', 'layoutArea', 'layoutAreaPos', 'contentArea', 
                 'marginOffset', 'paddingOffset', 'isPickable', 'isContainer', 'boundingInfo', 
                 'marginOffset', 'paddingOffset', 'isPickable', 'isContainer', 'boundingInfo', 
-                'levelBoundingInfo', 'isSizedByContent', 'isPositionAuto', 'actualScale', 'layoutBoundingInfo'];
+                'levelBoundingInfo', 'isSizedByContent', 'isPositionAuto', 'actualScale', 'layoutBoundingInfo', 
+                '_cachedTexture'];
             for (let dirty of toAddDirty) {
             for (let dirty of toAddDirty) {
                 let infos = new Property(dirty, this.actualObject);
                 let infos = new Property(dirty, this.actualObject);
                 propertiesLines.push(new PropertyLine(infos));
                 propertiesLines.push(new PropertyLine(infos));

+ 1 - 1
inspector/src/helpers/Helpers.ts

@@ -128,7 +128,7 @@ module INSPECTOR {
             div.style.display = 'none';
             div.style.display = 'none';
             div.appendChild(clone);
             div.appendChild(clone);
             let value = Inspector.WINDOW.getComputedStyle(clone)[cssAttribute];
             let value = Inspector.WINDOW.getComputedStyle(clone)[cssAttribute];
-            div.remove();
+            div.parentNode.removeChild(div);
             return value;
             return value;
         }
         }
         
         

+ 5 - 8
inspector/src/tabs/ConsoleTab.ts

@@ -60,6 +60,7 @@ module INSPECTOR {
             this._bjsPanelContent.innerHTML = BABYLON.Tools.LogCache;
             this._bjsPanelContent.innerHTML = BABYLON.Tools.LogCache;
             BABYLON.Tools.OnNewCacheEntry = (entry: string) => {
             BABYLON.Tools.OnNewCacheEntry = (entry: string) => {
                 this._bjsPanelContent.innerHTML += entry;
                 this._bjsPanelContent.innerHTML += entry;
+                this._bjsPanelContent.scrollTop = this._bjsPanelContent.scrollHeight; 
             };
             };
 
 
             // Testing
             // Testing
@@ -87,15 +88,11 @@ module INSPECTOR {
             let callerLine = Helpers.CreateDiv('caller', this._consolePanelContent);
             let callerLine = Helpers.CreateDiv('caller', this._consolePanelContent);
             callerLine.textContent = caller;
             callerLine.textContent = caller;
 
 
-            let line = Helpers.CreateDiv(type, this._consolePanelContent);
-            if (typeof message === "string") {
-                line.textContent += message ; 
-            } else {
-                line.textContent += JSON.stringify(message) ;
-                line.classList.add('object')
-            }
-        }
+            let line = Helpers.CreateDiv(type, this._consolePanelContent); 
+            line.textContent += message ; 
 
 
+            this._consolePanelContent.scrollTop = this._consolePanelContent.scrollHeight; 
+        }
         private _addConsoleLog(...params : any[]) {
         private _addConsoleLog(...params : any[]) {
             
             
             // Get caller name if not null
             // Get caller name if not null

+ 31 - 35
inspector/test/index.js

@@ -94,41 +94,37 @@ var Test = (function () {
      * Create the canvas2D
      * Create the canvas2D
      */
      */
     Test.prototype._createCanvas = function () {
     Test.prototype._createCanvas = function () {
-        var canvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, {
-            id: "Hello world SC",
-            size: new BABYLON.Size(300, 100),
-            backgroundFill: "#4040408F",
-            backgroundRoundRadius: 50,
-            children: [
-                new BABYLON.Text2D("Hello World!", {
-                    id: "text",
-                    marginAlignment: "h: center, v:center",
-                    fontName: "20pt Arial",
-                })
-            ]
-        });
-        var infoCanvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, { id: "PINK CUBE SC", size: new BABYLON.Size(500, 500) });
-        var text2 = new BABYLON.Text2D("UnbindTime", { parent: infoCanvas, id: "Text", marginAlignment: "h: left, v: bottom", fontName: "10pt Arial" });
-        canvas = new BABYLON.WorldSpaceCanvas2D(this.scene, new BABYLON.Size(150, 150), {
-            id: "WorldSpaceCanvas",
-            worldPosition: new BABYLON.Vector3(0, 0, 0),
-            worldRotation: BABYLON.Quaternion.RotationYawPitchRoll(Math.PI / 4, Math.PI / 4, 0),
-            enableInteraction: true,
-            backgroundFill: "#C0C0C040",
-            backgroundRoundRadius: 20,
-            children: [
-                new BABYLON.Text2D("World Space Canvas", { fontName: "8pt Arial", marginAlignment: "h: center, v: bottom", fontSuperSample: true })
-            ]
-        });
-        var rect = new BABYLON.Rectangle2D({ parent: canvas, x: 45, y: 45, width: 30, height: 30, fill: null, border: BABYLON.Canvas2D.GetGradientColorBrush(new BABYLON.Color4(0.9, 0.3, 0.9, 1), new BABYLON.Color4(1.0, 1.0, 1.0, 1)), borderThickness: 2 });
-        var buttonRect = new BABYLON.Rectangle2D({ parent: canvas, id: "button", x: 12, y: 12, width: 50, height: 15, fill: "#40C040FF", roundRadius: 2, children: [new BABYLON.Text2D("Click Me!", { fontName: "8pt Arial", marginAlignment: "h: center, v: center", fontSuperSample: true })] });
-        var button2Rect = new BABYLON.Rectangle2D({ parent: canvas, id: "button2", x: 70, y: 12, width: 40, height: 15, fill: "#4040C0FF", roundRadius: 2, isVisible: false, children: [new BABYLON.Text2D("Great!", { fontName: "8pt Arial", marginAlignment: "h: center, v: center", fontSuperSample: true })] });
-        ;
-        buttonRect.pointerEventObservable.add(function (d, s) {
-            button2Rect.levelVisible = !button2Rect.levelVisible;
-        }, BABYLON.PrimitivePointerInfo.PointerUp);
-        var insideRect = new BABYLON.Rectangle2D({ parent: rect, width: 10, height: 10, marginAlignment: "h: center, v: center", fill: "#0040F0FF" });
-        insideRect.roundRadius = 2;
+        // object hierarchy  g1 -> g2 -> rect
+            
+            // when cachingStrategy is 1 or 2 - everything is rendered
+            // when it is 3 - only direct children of g1 are rendered
+            var canvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, 
+                { id: "ScreenCanvas", 
+                cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_DONTCACHE });           // 1
+                // cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS });      // 2 
+                // cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_ALLGROUPS });           // 3
+            
+            canvas.createCanvasProfileInfoCanvas();
+
+            // parent group            
+            var g1 = new BABYLON.Group2D({parent: canvas, id: "G1",
+                x: 50, y: 50, size: new BABYLON.Size(60, 60)});
+
+            // just to see it    
+            let frame1 = new BABYLON.Rectangle2D({parent: g1, 
+                         x : 0, y: 0,  size: g1.size, border: "#FF0000FF" });
+            
+            // child group
+            let g2 = new BABYLON.Group2D({parent: g1, id: "G2",  
+                        x: 10, y: 10, size: new BABYLON.Size(40, 40)});
+
+            // just to see it
+            let frame2 = new BABYLON.Rectangle2D({parent: g2, x : 0, y: 0, size: g2.size, border: "#0000FFFF" }); 
+            
+            let rect =   new BABYLON.Rectangle2D({parent: g2, x : 10, y: 10, size: new BABYLON.Size(20, 20), 
+                                fill: "#00FF00FF" }) ;              
+                      
+            return canvas;
     };
     };
     return Test;
     return Test;
 }());
 }());

+ 3 - 3
proceduralTexturesLibrary/src/brick/brickProceduralTexture.fragment.fx

@@ -28,7 +28,7 @@ float fbm(vec2 n) {
 	return total;
 	return total;
 }
 }
 
 
-float round(float number){
+float roundF(float number){
 	return sign(number)*floor(abs(number) + 0.5);
 	return sign(number)*floor(abs(number) + 0.5);
 }
 }
 
 
@@ -40,14 +40,14 @@ void main(void)
 	float jointHPercentage = 0.05;
 	float jointHPercentage = 0.05;
 	vec3 color = brickColor;
 	vec3 color = brickColor;
 	float yi = vUV.y / brickH;
 	float yi = vUV.y / brickH;
-	float nyi = round(yi);
+	float nyi = roundF(yi);
 	float xi = vUV.x / brickW;
 	float xi = vUV.x / brickW;
 
 
 	if (mod(floor(yi), 2.0) == 0.0){
 	if (mod(floor(yi), 2.0) == 0.0){
 		xi = xi - 0.5;
 		xi = xi - 0.5;
 	}
 	}
 
 
-	float nxi = round(xi);
+	float nxi = roundF(xi);
 	vec2 brickvUV = vec2((xi - floor(xi)) / brickH, (yi - floor(yi)) /  brickW);
 	vec2 brickvUV = vec2((xi - floor(xi)) / brickH, (yi - floor(yi)) /  brickW);
 
 
 	if (yi < nyi + jointHPercentage && yi > nyi - jointHPercentage){
 	if (yi < nyi + jointHPercentage && yi > nyi - jointHPercentage){

+ 3 - 3
proceduralTexturesLibrary/src/marble/marbleProceduralTexture.fragment.fx

@@ -34,7 +34,7 @@ float turbulence(vec2 P)
 	return val;
 	return val;
 }
 }
 
 
-float round(float number){
+float roundF(float number){
 	return sign(number)*floor(abs(number) + 0.5);
 	return sign(number)*floor(abs(number) + 0.5);
 }
 }
 
 
@@ -58,14 +58,14 @@ void main()
 	float jointHPercentage = 0.01;
 	float jointHPercentage = 0.01;
 	vec3 color = marbleColor;
 	vec3 color = marbleColor;
 	float yi = vUV.y / brickH;
 	float yi = vUV.y / brickH;
-	float nyi = round(yi);
+	float nyi = roundF(yi);
 	float xi = vUV.x / brickW;
 	float xi = vUV.x / brickW;
 
 
 	if (mod(floor(yi), 2.0) == 0.0){
 	if (mod(floor(yi), 2.0) == 0.0){
 		xi = xi - 0.5;
 		xi = xi - 0.5;
 	}
 	}
 
 
-	float nxi = round(xi);
+	float nxi = roundF(xi);
 	vec2 brickvUV = vec2((xi - floor(xi)) / brickH, (yi - floor(yi)) / brickW);
 	vec2 brickvUV = vec2((xi - floor(xi)) / brickH, (yi - floor(yi)) / brickW);
 
 
 	if (yi < nyi + jointHPercentage && yi > nyi - jointHPercentage){
 	if (yi < nyi + jointHPercentage && yi > nyi - jointHPercentage){

+ 2 - 4
src/Animations/babylon.animatable.ts

@@ -107,7 +107,6 @@
                 if (idx > -1) {
                 if (idx > -1) {
 
 
                     var animations = this._animations;
                     var animations = this._animations;
-                    var numberOfAnimationsStopped = 0;
                     
                     
                     for (var index = animations.length - 1; index >= 0; index--) {
                     for (var index = animations.length - 1; index >= 0; index--) {
                         if (typeof animationName === "string" && animations[index].name != animationName) {
                         if (typeof animationName === "string" && animations[index].name != animationName) {
@@ -116,10 +115,9 @@
 
 
                         animations[index].reset();
                         animations[index].reset();
                         animations.splice(index, 1);
                         animations.splice(index, 1);
-                        numberOfAnimationsStopped++;
                     }
                     }
 
 
-                    if (animations.length == numberOfAnimationsStopped) {
+                    if (animations.length == 0) {
                         this._scene._activeAnimatables.splice(idx, 1);
                         this._scene._activeAnimatables.splice(idx, 1);
 
 
                         if (this.onAnimationEnd) {
                         if (this.onAnimationEnd) {
@@ -191,4 +189,4 @@
             return running;
             return running;
         }
         }
     }
     }
-} 
+} 

+ 2 - 2
src/Cameras/babylon.arcRotateCamera.ts

@@ -406,8 +406,8 @@
             this.rebuildAnglesAndRadius();
             this.rebuildAnglesAndRadius();
         }
         }
 
 
-        public setTarget(target: Vector3, toBoundingCenter = false): void {            
-            if (this._getTargetPosition().equals(target)) {
+        public setTarget(target: Vector3, toBoundingCenter = false, allowSamePosition = false): void {            
+            if (!allowSamePosition && this._getTargetPosition().equals(target)) {
                 return;
                 return;
             }
             }
             
             

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

@@ -403,7 +403,7 @@
                 this._markSyncedWithParent();
                 this._markSyncedWithParent();
             }
             }
 
 
-            if (this._cameraRigParams.vrPreViewMatrix) {
+            if (this._cameraRigParams && this._cameraRigParams.vrPreViewMatrix) {
                 this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
                 this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
             }
             }
 
 

+ 5 - 4
src/Cameras/babylon.followCamera.ts

@@ -41,11 +41,12 @@
                 yRotation = cameraTarget.rotation.y;
                 yRotation = cameraTarget.rotation.y;
             }
             }
             var radians = this.getRadians(this.rotationOffset) + yRotation;
             var radians = this.getRadians(this.rotationOffset) + yRotation;
-            var targetX: number = cameraTarget.position.x + Math.sin(radians) * this.radius;
+            var targetPosition = cameraTarget.getAbsolutePosition();
+            var targetX: number = targetPosition.x + Math.sin(radians) * this.radius;
 
 
-            var targetZ: number = cameraTarget.position.z + Math.cos(radians) * this.radius;
+            var targetZ: number = targetPosition.z + Math.cos(radians) * this.radius;
             var dx: number = targetX - this.position.x;
             var dx: number = targetX - this.position.x;
-            var dy: number = (cameraTarget.position.y + this.heightOffset) - this.position.y;
+            var dy: number = (targetPosition.y + this.heightOffset) - this.position.y;
             var dz: number = (targetZ) - this.position.z;
             var dz: number = (targetZ) - this.position.z;
             var vx: number = dx * this.cameraAcceleration * 2;//this is set to .05
             var vx: number = dx * this.cameraAcceleration * 2;//this is set to .05
             var vy: number = dy * this.cameraAcceleration;
             var vy: number = dy * this.cameraAcceleration;
@@ -64,7 +65,7 @@
             }
             }
 
 
             this.position = new Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
             this.position = new Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
-            this.setTarget(cameraTarget.position);
+            this.setTarget(targetPosition);
         }
         }
 
 
         public _checkInputs(): void {
         public _checkInputs(): void {

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

@@ -319,7 +319,7 @@
             }
             }
         }
         }
 
 
-        public _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: number[] | Int32Array, indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean): void {
+        public _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: IndicesArray, indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean): void {
             for (var i = indexStart; i < indexEnd; i += 3) {
             for (var i = indexStart; i < indexEnd; i += 3) {
                 var p1 = pts[indices[i] - decal];
                 var p1 = pts[indices[i] - decal];
                 var p2 = pts[indices[i + 1] - decal];
                 var p2 = pts[indices[i + 1] - decal];

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

@@ -47,7 +47,7 @@ module BABYLON {
     export interface SerializedGeometry {
     export interface SerializedGeometry {
         id: string;
         id: string;
         positions: Float32Array;
         positions: Float32Array;
-        indices: Int32Array;
+        indices: Uint32Array;
         normals: Float32Array;
         normals: Float32Array;
         //uvs?: Float32Array;
         //uvs?: Float32Array;
     }
     }
@@ -181,7 +181,7 @@ module BABYLON {
                 id: geometry.id,
                 id: geometry.id,
                 positions: new Float32Array(geometry.getVerticesData(VertexBuffer.PositionKind) || []),
                 positions: new Float32Array(geometry.getVerticesData(VertexBuffer.PositionKind) || []),
                 normals: new Float32Array(geometry.getVerticesData(VertexBuffer.NormalKind) || []),
                 normals: new Float32Array(geometry.getVerticesData(VertexBuffer.NormalKind) || []),
-                indices: new Int32Array(geometry.getIndices() || []),
+                indices: new Uint32Array(geometry.getIndices() || []),
                 //uvs: new Float32Array(geometry.getVerticesData(VertexBuffer.UVKind) || [])
                 //uvs: new Float32Array(geometry.getVerticesData(VertexBuffer.UVKind) || [])
             }
             }
         }
         }

+ 6 - 3
src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts

@@ -289,13 +289,13 @@
                 this._effect.setMatrix(name, this._matrices[name]);
                 this._effect.setMatrix(name, this._matrices[name]);
             }            
             }            
 
 
-            // VBOs
-            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
-
             if (this.isCube) {
             if (this.isCube) {
                 for (var face = 0; face < 6; face++) {
                 for (var face = 0; face < 6; face++) {
                     engine.bindFramebuffer(this._texture, face);
                     engine.bindFramebuffer(this._texture, face);
 
 
+                    // VBOs
+                    engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
+
                     this._effect.setFloat("face", face);
                     this._effect.setFloat("face", face);
 
 
                     // Clear
                     // Clear
@@ -312,6 +312,9 @@
             } else {
             } else {
                 engine.bindFramebuffer(this._texture);
                 engine.bindFramebuffer(this._texture);
 
 
+                // VBOs
+                engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
+
                 // Clear
                 // Clear
                 engine.clear(scene.clearColor, true, true, true);
                 engine.clear(scene.clearColor, true, true, true);
 
 

+ 4 - 3
src/Math/babylon.math.ts

@@ -1196,8 +1196,8 @@
             Matrix.FromValuesToRef(
             Matrix.FromValuesToRef(
                 cw / 2.0, 0, 0, 0,
                 cw / 2.0, 0, 0, 0,
                 0, -ch / 2.0, 0, 0,
                 0, -ch / 2.0, 0, 0,
-                0, 0, 1, 0,
-                cx + cw / 2.0, ch / 2.0 + cy, 0, 1, viewportMatrix);
+                0, 0, 0.5, 0,
+                cx + cw / 2.0, ch / 2.0 + cy, 0.5, 1, viewportMatrix);
 
 
             var matrix = Vector3._matrixCache ? Vector3._matrixCache : (Vector3._matrixCache = new Matrix());
             var matrix = Vector3._matrixCache ? Vector3._matrixCache : (Vector3._matrixCache = new Matrix());
             world.multiplyToRef(transform, matrix);
             world.multiplyToRef(transform, matrix);
@@ -4076,7 +4076,8 @@
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
             Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough
             Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough
         public static Vector4: Vector4[] = [Vector4.Zero(), Vector4.Zero(), Vector4.Zero()];  // 3 temp Vector4 at once should be enough
         public static Vector4: Vector4[] = [Vector4.Zero(), Vector4.Zero(), Vector4.Zero()];  // 3 temp Vector4 at once should be enough
-        public static Quaternion: Quaternion[] = [new Quaternion(0, 0, 0, 0)];                // 1 temp Quaternion at once should be enough
+        public static Quaternion: Quaternion[] = [new Quaternion(0.0, 0.0, 0.0, 0.0), 
+            new Quaternion(0.0, 0.0, 0.0, 0.0)];                // 2 temp Quaternion at once should be enough
         public static Matrix: Matrix[] = [Matrix.Zero(), Matrix.Zero(),
         public static Matrix: Matrix[] = [Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero(),

+ 449 - 10
src/Mesh/babylon.abstractMesh.ts

@@ -27,6 +27,55 @@
             return AbstractMesh._BILLBOARDMODE_ALL;
             return AbstractMesh._BILLBOARDMODE_ALL;
         }
         }
 
 
+        // facetData private properties
+        private _facetPositions: Vector3[];             // facet local positions
+        private _facetNormals: Vector3[];               // facet local normals
+        private _facetPartitioning: number[][];           // partitioning array of facet index arrays
+        private _facetNb: number = 0;                   // facet number
+        private _partitioningSubdivisions: number = 10; // number of subdivisions per axis in the partioning space  
+        private _partitioningBBoxRatio: number = 1.01;  // the partioning array space is by default 1% bigger than the bounding box
+        private _facetDataEnabled: boolean = false;     // is the facet data feature enabled on this mesh ?
+        private _facetParameters: any = {};                  // keep a reference to the object parameters to avoid memory re-allocation
+        private _bbSize: Vector3 = Vector3.Zero();      // bbox size approximated for facet data
+        private _subDiv = {                         // actual number of subdivisions per axis for ComputeNormals()
+            max: 1,
+            X: 1,
+            Y: 1,
+            Z: 1
+        };
+        /**
+         * Read-only : the number of facets in the mesh
+         */
+        public get facetNb(): number {
+            return this._facetNb;
+        }
+        /**
+         * The number of subdivisions per axis in the partioning space
+         */
+        public get partitioningSubdivisions(): number {
+            return this._partitioningSubdivisions;
+        }
+        public set partitioningSubdivisions(nb: number) {
+            this._partitioningSubdivisions = nb;
+        } 
+        /**
+         * The ratio to apply to the bouding box size to set to the partioning space.  
+         * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box.
+         */
+        public get partitioningBBoxRatio(): number {
+            return this._partitioningBBoxRatio;
+        }
+        public set partitioningBBoxRatio(ratio: number) {
+            this._partitioningBBoxRatio = ratio;
+        }
+        /**
+         * Read-only : is the feature facetData enabled ?
+         */
+        public get isFacetDataEnabled(): boolean {
+            return this._facetDataEnabled;
+        }
+
+
         // Events
         // Events
 
 
         /**
         /**
@@ -65,10 +114,10 @@
 
 
         // Properties
         // Properties
         public definedFacingForward = true; // orientation for POV movement & rotation
         public definedFacingForward = true; // orientation for POV movement & rotation
-        public position = new Vector3(0, 0, 0);
-        private _rotation = new Vector3(0, 0, 0);
+        public position = new Vector3(0.0, 0.0, 0.0);
+        private _rotation = new Vector3(0.0, 0.0, 0.0);
         public _rotationQuaternion: Quaternion;
         public _rotationQuaternion: Quaternion;
-        private _scaling = new Vector3(1, 1, 1);
+        private _scaling = new Vector3(1.0, 1.0, 1.0);
         public billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
         public billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
         public visibility = 1.0;
         public visibility = 1.0;
         public alphaIndex = Number.MAX_VALUE;
         public alphaIndex = Number.MAX_VALUE;
@@ -99,7 +148,9 @@
         public useOctreeForCollisions = true;
         public useOctreeForCollisions = true;
 
 
         public layerMask: number = 0x0FFFFFFF;
         public layerMask: number = 0x0FFFFFFF;
-
+        /**
+         * True if the mesh must be rendered in any case.  
+         */
         public alwaysSelectAsActiveMesh = false;
         public alwaysSelectAsActiveMesh = false;
 
 
         /**
         /**
@@ -192,6 +243,9 @@
             scene.addMesh(this);
             scene.addMesh(this);
         }
         }
 
 
+        /**
+         * Returns the string "AbstractMesh"
+         */
         public getClassName(): string {
         public getClassName(): string {
             return "AbstractMesh";
             return "AbstractMesh";
         }
         }
@@ -213,8 +267,9 @@
         }
         }
 
 
         /**
         /**
-         * Getting the rotation object. 
-         * If rotation quaternion is set, this vector will (almost always) be the Zero vector!
+         * Ratation property : a Vector3 depicting the rotation value in radians around each local axis X, Y, Z. 
+         * If rotation quaternion is set, this Vector3 will (almost always) be the Zero vector!
+         * Default : (0.0, 0.0, 0.0)
          */
          */
         public get rotation(): Vector3 {
         public get rotation(): Vector3 {
             return this._rotation;
             return this._rotation;
@@ -224,6 +279,10 @@
             this._rotation = newRotation;
             this._rotation = newRotation;
         }
         }
 
 
+        /**
+         * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
+         * Default : (1.0, 1.0, 1.0)
+         */
         public get scaling(): Vector3 {
         public get scaling(): Vector3 {
             return this._scaling;
             return this._scaling;
         }
         }
@@ -235,6 +294,11 @@
             }
             }
         }
         }
 
 
+        /**
+         * Rotation Quaternion property : this a Quaternion object depicting the mesh rotation by using a unit quaternion. 
+         * It's null by default.  
+         * If set, only the rotationQuaternion is then used to compute the mesh rotation and its property `.rotation\ is then ignored and set to (0.0, 0.0, 0.0)
+         */
         public get rotationQuaternion() {
         public get rotationQuaternion() {
             return this._rotationQuaternion;
             return this._rotationQuaternion;
         }
         }
@@ -243,7 +307,7 @@
             this._rotationQuaternion = quaternion;
             this._rotationQuaternion = quaternion;
             //reset the rotation vector. 
             //reset the rotation vector. 
             if (quaternion && this.rotation.length()) {
             if (quaternion && this.rotation.length()) {
-                this.rotation.copyFromFloats(0, 0, 0);
+                this.rotation.copyFromFloats(0.0, 0.0, 0.0);
             }
             }
         }
         }
 
 
@@ -272,26 +336,49 @@
             return false;
             return false;
         }
         }
 
 
+        /**
+         * Returns this by default, used by the class Mesh.  
+         * Returned type : AbstractMesh
+         */
         public getLOD(camera: Camera): AbstractMesh {
         public getLOD(camera: Camera): AbstractMesh {
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Returns 0 by default, used by the class Mesh.  
+         * Returns an integer.  
+         */
         public getTotalVertices(): number {
         public getTotalVertices(): number {
             return 0;
             return 0;
         }
         }
 
 
-        public getIndices(): number[] | Int32Array {
+        /**
+         * Returns null by default, used by the class Mesh. 
+         * Returned type : integer array 
+         */
+        public getIndices(): IndicesArray {
             return null;
             return null;
         }
         }
 
 
+        /**
+         * Returns the array of the requested vertex data kind. Used by the class Mesh. Returns null here. 
+         * Returned type : float array or Float32Array 
+         */
         public getVerticesData(kind: string): number[] | Float32Array {
         public getVerticesData(kind: string): number[] | Float32Array {
             return null;
             return null;
         }
         }
 
 
+        /** Returns false by default, used by the class Mesh.  
+         *  Returns a boolean
+        */
         public isVerticesDataPresent(kind: string): boolean {
         public isVerticesDataPresent(kind: string): boolean {
             return false;
             return false;
         }
         }
 
 
+        /**
+         * Returns the mesh BoundingInfo object or creates a new one and returns it if undefined.
+         * Returns a BoundingInfo
+         */
         public getBoundingInfo(): BoundingInfo {
         public getBoundingInfo(): BoundingInfo {
             if (this._masterMesh) {
             if (this._masterMesh) {
                 return this._masterMesh.getBoundingInfo();
                 return this._masterMesh.getBoundingInfo();
@@ -303,6 +390,10 @@
             return this._boundingInfo;
             return this._boundingInfo;
         }
         }
 
 
+        /**
+         * Sets a mesh new object BoundingInfo.
+         * Returns nothing.  
+         */
         public setBoundingInfo(boundingInfo: BoundingInfo): void {
         public setBoundingInfo(boundingInfo: BoundingInfo): void {
             this._boundingInfo = boundingInfo;
             this._boundingInfo = boundingInfo;
         }
         }
@@ -321,6 +412,10 @@
             this._renderId = renderId;
             this._renderId = renderId;
         }
         }
 
 
+        /**
+         * Returns the last update of the World matrix
+         * Returns a Matrix.  
+         */
         public getWorldMatrix(): Matrix {
         public getWorldMatrix(): Matrix {
             if (this._masterMesh) {
             if (this._masterMesh) {
                 return this._masterMesh.getWorldMatrix();
                 return this._masterMesh.getWorldMatrix();
@@ -332,30 +427,53 @@
             return this._worldMatrix;
             return this._worldMatrix;
         }
         }
 
 
+        /**
+         * Returns directly the last state of the mesh World matrix. 
+         * A Matrix is returned.    
+         */
         public get worldMatrixFromCache(): Matrix {
         public get worldMatrixFromCache(): Matrix {
             return this._worldMatrix;
             return this._worldMatrix;
         }
         }
 
 
+        /**
+         * Returns the current mesh absolute position.
+         * Retuns a Vector3
+         */
         public get absolutePosition(): Vector3 {
         public get absolutePosition(): Vector3 {
             return this._absolutePosition;
             return this._absolutePosition;
         }
         }
-
+        /**
+         * Prevents the World matrix to be computed any longer.
+         * Returns nothing.  
+         */
         public freezeWorldMatrix() {
         public freezeWorldMatrix() {
             this._isWorldMatrixFrozen = false;  // no guarantee world is not already frozen, switch off temporarily
             this._isWorldMatrixFrozen = false;  // no guarantee world is not already frozen, switch off temporarily
             this.computeWorldMatrix(true);
             this.computeWorldMatrix(true);
             this._isWorldMatrixFrozen = true;
             this._isWorldMatrixFrozen = true;
         }
         }
 
 
+        /**
+         * Allows back the World matrix computation. 
+         * Returns nothing.  
+         */
         public unfreezeWorldMatrix() {
         public unfreezeWorldMatrix() {
             this._isWorldMatrixFrozen = false;
             this._isWorldMatrixFrozen = false;
             this.computeWorldMatrix(true);
             this.computeWorldMatrix(true);
         }
         }
 
 
+        /**
+         * True if the World matrix has been frozen.  
+         * Returns a boolean.  
+         */
         public get isWorldMatrixFrozen(): boolean {
         public get isWorldMatrixFrozen(): boolean {
             return this._isWorldMatrixFrozen;
             return this._isWorldMatrixFrozen;
         }
         }
 
 
         private static _rotationAxisCache = new Quaternion();
         private static _rotationAxisCache = new Quaternion();
+        /**
+         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in the given space.  
+         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD
+         */
         public rotate(axis: Vector3, amount: number, space?: Space): void {
         public rotate(axis: Vector3, amount: number, space?: Space): void {
             axis.normalize();
             axis.normalize();
 
 
@@ -380,6 +498,10 @@
             }
             }
         }
         }
 
 
+        /**
+         * Translates the mesh along the axis vector for the passed distance in the given space.  
+         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD
+         */
         public translate(axis: Vector3, distance: number, space?: Space): void {
         public translate(axis: Vector3, distance: number, space?: Space): void {
             var displacementVector = axis.scale(distance);
             var displacementVector = axis.scale(distance);
 
 
@@ -392,6 +514,36 @@
             }
             }
         }
         }
 
 
+        /**
+         * Adds a rotation step to the mesh current rotation.  
+         * x, y, z are Euler angles expressed in radians.  
+         * This methods updates the current mesh rotation, either mesh.rotation, either mesh.rotationQuaternion if it's set.  
+         * It's useful to set a custom rotation order different from the BJS standard one YXZ.  
+         * Example : this rotates the mesh first around its local X axis, then around its local Z axis, finally around its local Y axis.  
+         * ```javascript
+         * mesh.addRotation(x1, 0, 0).addRotation(0, 0, z2).addRotation(0, 0, y3);
+         * ```
+         * Note that `addRotation()` accumulates the passed rotation values to the current ones and computes the .rotation or .rotationQuaternion updated values.  
+         * Under the hood, only quaternions are used. So it's a little faster is you use .rotationQuaternion because it doesn't need to translate them back to Euler angles.  
+         */
+        public addRotation(x: number, y: number, z: number): AbstractMesh {
+            var rotationQuaternion;
+            if (this.rotationQuaternion) {
+                rotationQuaternion = this.rotationQuaternion;
+            }
+            else {
+                rotationQuaternion = Tmp.Quaternion[1];
+                Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, rotationQuaternion);
+            }
+            var accumulation = BABYLON.Tmp.Quaternion[0];
+            Quaternion.RotationYawPitchRollToRef(y, x, z, accumulation);
+            rotationQuaternion.multiplyInPlace(accumulation);
+            if (!this.rotationQuaternion) {
+                rotationQuaternion.toEulerAnglesToRef(this.rotation);
+            }
+            return this;
+        }
+
         public getAbsolutePosition(): Vector3 {
         public getAbsolutePosition(): Vector3 {
             this.computeWorldMatrix();
             this.computeWorldMatrix();
             return this._absolutePosition;
             return this._absolutePosition;
@@ -488,11 +640,20 @@
             return new Vector3(flipBack * defForwardMult, twirlClockwise, tiltRight * defForwardMult);
             return new Vector3(flipBack * defForwardMult, twirlClockwise, tiltRight * defForwardMult);
         }
         }
 
 
+        /**
+         * Sets a new pivot matrix to the mesh.  
+         * Returns nothing.
+         */
         public setPivotMatrix(matrix: Matrix): void {
         public setPivotMatrix(matrix: Matrix): void {
             this._pivotMatrix = matrix;
             this._pivotMatrix = matrix;
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
         }
         }
 
 
+        /**
+         * Returns the mesh pivot matrix.
+         * Default : Identity.  
+         * A Matrix is returned.  
+         */
         public getPivotMatrix(): Matrix {
         public getPivotMatrix(): Matrix {
             return this._pivotMatrix;
             return this._pivotMatrix;
         }
         }
@@ -571,6 +732,13 @@
             }
             }
         }
         }
 
 
+        /**
+         * Computes the mesh World matrix and returns it.  
+         * If the mesh world matrix is frozen, this computation does nothing more than returning the last frozen values.  
+         * If the parameter `force` is let to `false` (default), the current cached World matrix is returned. 
+         * If the parameter `force`is set to `true`, the actual computation is done.  
+         * Returns the mesh World Matrix.
+         */
         public computeWorldMatrix(force?: boolean): Matrix {
         public computeWorldMatrix(force?: boolean): Matrix {
             if (this._isWorldMatrixFrozen) {
             if (this._isWorldMatrixFrozen) {
                 return this._worldMatrix;
                 return this._worldMatrix;
@@ -708,13 +876,16 @@
         }
         }
 
 
         /**
         /**
-        * If you'd like to be callbacked after the mesh position, rotation or scaling has been updated
+        * If you'd like to be called back after the mesh position, rotation or scaling has been updated
         * @param func: callback function to add
         * @param func: callback function to add
         */
         */
         public registerAfterWorldMatrixUpdate(func: (mesh: AbstractMesh) => void): void {
         public registerAfterWorldMatrixUpdate(func: (mesh: AbstractMesh) => void): void {
             this.onAfterWorldMatrixUpdateObservable.add(func);
             this.onAfterWorldMatrixUpdateObservable.add(func);
         }
         }
 
 
+        /**
+         * Removes a registered callback function
+         */
         public unregisterAfterWorldMatrixUpdate(func: (mesh: AbstractMesh) => void): void {
         public unregisterAfterWorldMatrixUpdate(func: (mesh: AbstractMesh) => void): void {
             this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
             this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
         }
         }
@@ -725,6 +896,10 @@
             this.position = Vector3.TransformNormal(vector3, this._localWorld);
             this.position = Vector3.TransformNormal(vector3, this._localWorld);
         }
         }
 
 
+        /**
+         * Returns the mesh position in the local space from the current World matrix values.
+         * Returns a new Vector3.
+         */
         public getPositionExpressedInLocalSpace(): Vector3 {
         public getPositionExpressedInLocalSpace(): Vector3 {
             this.computeWorldMatrix();
             this.computeWorldMatrix();
             var invLocalWorldMatrix = this._localWorld.clone();
             var invLocalWorldMatrix = this._localWorld.clone();
@@ -784,6 +959,11 @@
             return this._boundingInfo.isCompletelyInFrustum(frustumPlanes);;
             return this._boundingInfo.isCompletelyInFrustum(frustumPlanes);;
         }
         }
 
 
+        /** 
+         * True if the mesh intersects another mesh or a SolidParticle object.  
+         * Unless the parameter `precise` is set to `true` the intersection is computed according to Axis Aligned Bounding Boxes (AABB), else according to OBB (Oriented BBoxes)
+         * Returns a boolean.  
+         */
         public intersectsMesh(mesh: AbstractMesh | SolidParticle, precise?: boolean): boolean {
         public intersectsMesh(mesh: AbstractMesh | SolidParticle, precise?: boolean): boolean {
             if (!this._boundingInfo || !mesh._boundingInfo) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;
                 return false;
@@ -792,6 +972,10 @@
             return this._boundingInfo.intersects(mesh._boundingInfo, precise);
             return this._boundingInfo.intersects(mesh._boundingInfo, precise);
         }
         }
 
 
+        /**
+         * Returns true if the passed point (Vector3) is inside the mesh bounding box.  
+         * Returns a boolean.  
+         */
         public intersectsPoint(point: Vector3): boolean {
         public intersectsPoint(point: Vector3): boolean {
             if (!this._boundingInfo) {
             if (!this._boundingInfo) {
                 return false;
                 return false;
@@ -1107,6 +1291,12 @@
             }
             }
         }
         }
 
 
+        /**
+         * Disposes the AbstractMesh.  
+         * Some internal references are kept for further use.  
+         * By default, all the mesh children are also disposed unless the parameter `doNotRecurse` is set to `true`.  
+         * Returns nothing.  
+         */
         public dispose(doNotRecurse?: boolean): void {
         public dispose(doNotRecurse?: boolean): void {
             var index: number;
             var index: number;
 
 
@@ -1204,6 +1394,11 @@
                 }
                 }
             }
             }
 
 
+            // facet data
+            if (this._facetDataEnabled) {
+                this.disableFacetData();
+            }
+
             this.onAfterWorldMatrixUpdateObservable.clear();
             this.onAfterWorldMatrixUpdateObservable.clear();
             this.onCollideObservable.clear();
             this.onCollideObservable.clear();
             this.onCollisionPositionChangeObservable.clear();
             this.onCollisionPositionChangeObservable.clear();
@@ -1375,5 +1570,249 @@
 
 
         }
         }
 
 
+       // Facet data
+        /** 
+         *  Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning
+         */
+        private _initFacetData(): AbstractMesh {
+            if (!this._facetNormals) {
+                this._facetNormals = new Array<Vector3>();
+            }
+            if (!this._facetPositions) {
+                this._facetPositions = new Array<Vector3>();
+            }
+            if (!this._facetPartitioning) {
+                this._facetPartitioning = new Array<number[]>();
+            }
+            this._facetNb = this.getIndices().length / 3;
+            this._partitioningSubdivisions = (this._partitioningSubdivisions) ? this._partitioningSubdivisions : 10;   // default nb of partitioning subdivisions = 10
+            this._partitioningBBoxRatio = (this._partitioningBBoxRatio) ? this._partitioningBBoxRatio : 1.01;          // default ratio 1.01 = the partitioning is 1% bigger than the bounding box
+            for (var f = 0; f < this._facetNb; f++) {
+                this._facetNormals[f] = Vector3.Zero();
+                this._facetPositions[f] = Vector3.Zero();
+            }
+            this._facetDataEnabled = true;           
+            return this;
+        }
+
+        /**
+         * Updates the mesh facetData arrays and the internal partitioning when the mesh is morphed or updated.  
+         * This method can be called within the render loop.  
+         * You don't need to call this method by yourself in the render loop when you update/morph a mesh with the methods CreateXXX() as they automatically manage this computation.  
+         */
+        public updateFacetData(): AbstractMesh {
+            if (!this._facetDataEnabled) {
+                this._initFacetData();
+            }
+            var positions = this.getVerticesData(VertexBuffer.PositionKind);
+            var indices = this.getIndices();
+            var normals = this.getVerticesData(VertexBuffer.NormalKind);
+            var bInfo = this.getBoundingInfo();
+            this._bbSize.x = (bInfo.maximum.x - bInfo.minimum.x > Epsilon) ? bInfo.maximum.x - bInfo.minimum.x : Epsilon;
+            this._bbSize.y = (bInfo.maximum.y - bInfo.minimum.y > Epsilon) ? bInfo.maximum.y - bInfo.minimum.y : Epsilon;
+            this._bbSize.z = (bInfo.maximum.z - bInfo.minimum.z > Epsilon) ? bInfo.maximum.z - bInfo.minimum.z : Epsilon;
+            var bbSizeMax = (this._bbSize.x > this._bbSize.y) ? this._bbSize.x : this._bbSize.y;
+            bbSizeMax = (bbSizeMax > this._bbSize.z) ? bbSizeMax : this._bbSize.z;
+            this._subDiv.max = this._partitioningSubdivisions;
+            this._subDiv.X = Math.floor(this._subDiv.max * this._bbSize.x / bbSizeMax);   // adjust the number of subdivisions per axis
+            this._subDiv.Y = Math.floor(this._subDiv.max * this._bbSize.y / bbSizeMax);   // according to each bbox size per axis
+            this._subDiv.Z = Math.floor(this._subDiv.max * this._bbSize.z / bbSizeMax);
+            this._subDiv.X = this._subDiv.X < 1 ? 1 : this._subDiv.X;                     // at least one subdivision
+            this._subDiv.Y = this._subDiv.Y < 1 ? 1 : this._subDiv.Y;
+            this._subDiv.Z = this._subDiv.Z < 1 ? 1 : this._subDiv.Z;
+            // set the parameters for ComputeNormals()
+            this._facetParameters.facetNormals = this.getFacetLocalNormals(); 
+            this._facetParameters.facetPositions = this.getFacetLocalPositions();
+            this._facetParameters.facetPartitioning = this.getFacetLocalPartitioning();
+            this._facetParameters.bInfo = bInfo;
+            this._facetParameters.bbSize = this._bbSize;
+            this._facetParameters.subDiv = this._subDiv;
+            this._facetParameters.ratio = this.partitioningBBoxRatio;
+            VertexData.ComputeNormals(positions, indices, normals, this._facetParameters);
+            return this;
+        }
+        /**
+         * Returns the facetLocalNormals array.  
+         * The normals are expressed in the mesh local space.  
+         */
+        public getFacetLocalNormals(): Vector3[] {
+            if (!this._facetNormals) {
+                this.updateFacetData();
+            }
+            return this._facetNormals;
+        }
+        /**
+         * Returns the facetLocalPositions array.  
+         * The facet positions are expressed in the mesh local space.  
+         */
+        public getFacetLocalPositions(): Vector3[] {
+            if (!this._facetPositions) {
+                this.updateFacetData();
+            }
+            return this._facetPositions;           
+        }
+        /**
+         * Returns the facetLocalPartioning array
+         */
+        public getFacetLocalPartitioning(): number[][] {
+            if (!this._facetPartitioning) {
+                this.updateFacetData();
+            }
+            return this._facetPartitioning;
+        }
+        /**
+         * Returns the i-th facet position in the world system.  
+         * This method allocates a new Vector3 per call.  
+         */
+        public getFacetPosition(i: number): Vector3 {
+            var pos = Vector3.Zero();
+            this.getFacetPositionToRef(i, pos);
+            return pos;
+        }
+        /**
+         * Sets the reference Vector3 with the i-th facet position in the world system.  
+         * Returns the mesh.  
+         */
+        public getFacetPositionToRef(i: number, ref: Vector3): AbstractMesh {
+            var localPos = (this.getFacetLocalPositions())[i];
+            var world = this.getWorldMatrix();
+            Vector3.TransformCoordinatesToRef(localPos, world, ref);
+            return this;
+        }
+        /**
+         * Returns the i-th facet normal in the world system.  
+         * This method allocates a new Vector3 per call.  
+         */
+        public getFacetNormal(i: number): Vector3 {
+            var norm = Vector3.Zero();
+            this.getFacetNormalToRef(i, norm);
+            return norm;
+        }
+        /**
+         * Sets the reference Vector3 with the i-th facet normal in the world system.  
+         * Returns the mesh.  
+         */
+        public getFacetNormalToRef(i: number, ref: Vector3) {
+            var localNorm = (this.getFacetLocalNormals())[i];
+            Vector3.TransformNormalToRef(localNorm, this.getWorldMatrix(), ref);
+            return this;
+        }
+        /** 
+         * Returns the facets (in an array) in the same partitioning block than the one the passed coordinates are located (expressed in the mesh local system).
+         */
+        public getFacetsAtLocalCoordinates(x: number, y: number, z: number): number[] {
+            var bInfo = this.getBoundingInfo();
+            var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) * this._subDiv.X * this._partitioningBBoxRatio / this._bbSize.x);
+            var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) * this._subDiv.Y * this._partitioningBBoxRatio / this._bbSize.y);
+            var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) * this._subDiv.Z * this._partitioningBBoxRatio / this._bbSize.z);
+            if (ox < 0 || ox > this._subDiv.max || oy < 0 || oy > this._subDiv.max || oz < 0 || oz > this._subDiv.max) {
+                return null;
+            }
+            return this._facetPartitioning[ox + this._subDiv.max * oy + this._subDiv.max * this._subDiv.max * oz];
+        }
+        /** 
+         * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found.  
+         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) World projection on the facet.  
+         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
+         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
+         * If facing si false and checkFace is true, only the facet "turning their backs" to (x, y, z) are returned : negative dot (x, y, z) * facet position. 
+         */
+        public getClosestFacetAtCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): number {
+            var world = this.getWorldMatrix();
+            var invMat = Tmp.Matrix[5];
+            world.invertToRef(invMat);
+            var invVect = Tmp.Vector3[8];
+            var closest = null;
+            Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect);  // transform (x,y,z) to coordinates in the mesh local space
+            closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, checkFace, facing);
+            if (projected) {
+                // tranform the local computed projected vector to world coordinates
+                Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
+            }
+            return closest;
+        }
+        /** 
+         * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found.   
+         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) local projection on the facet.  
+         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
+         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
+         * If facing si false and checkFace is true, only the facet "turning their backs"  to (x, y, z) are returned : negative dot (x, y, z) * facet position.
+         */
+        public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): number {
+            var closest = null;
+            var tmpx = 0.0;         
+            var tmpy = 0.0;
+            var tmpz = 0.0;
+            var d = 0.0;            // tmp dot facet normal * facet position
+            var t0 = 0.0;
+            var projx = 0.0;
+            var projy = 0.0;
+            var projz = 0.0;
+            // Get all the facets in the same partitioning block than (x, y, z)
+            var facetPositions = this.getFacetLocalPositions();
+            var facetNormals = this.getFacetLocalNormals();
+            var facetsInBlock = this.getFacetsAtLocalCoordinates(x, y, z);
+            if (!facetsInBlock) {
+                return null;
+            }
+            // Get the closest facet to (x, y, z)
+            var shortest = Number.MAX_VALUE;            // init distance vars
+            var tmpDistance = shortest;
+            var fib;                                    // current facet in the block
+            var norm;                                   // current facet normal
+            var p0;                                     // current facet barycenter position
+            // loop on all the facets in the current partitioning block
+            for (var idx = 0; idx < facetsInBlock.length; idx++) {
+                fib = facetsInBlock[idx];           
+                norm = facetNormals[fib];
+                p0 = facetPositions[fib];
+
+                d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
+                if ( !checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0) ) {
+                    // compute (x,y,z) projection on the facet = (projx, projy, projz)
+                    d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z; 
+                    t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
+                    projx = x + norm.x * t0;
+                    projy = y + norm.y * t0;
+                    projz = z + norm.z * t0;
+
+                    tmpx = projx - x;
+                    tmpy = projy - y;
+                    tmpz = projz - z;
+                    tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz;             // compute length between (x, y, z) and its projection on the facet
+                    if (tmpDistance < shortest) {                                      // just keep the closest facet to (x, y, z)
+                        shortest = tmpDistance;
+                        closest = fib; 
+                        if (projected) {
+                            projected.x = projx;
+                            projected.y = projy;
+                            projected.z = projz;
+                        }
+                    }
+                }
+            }
+            return closest;
+        }
+        /**
+         * Returns the object "parameter" set with all the expected parameters for facetData computation by ComputeNormals()  
+         */
+        public getFacetDataParameters(): any {
+            return this._facetParameters;
+        }
+        /** 
+         * Disables the feature FacetData and frees the related memory.  
+         * Returns the mesh.  
+         */
+        public disableFacetData(): AbstractMesh {
+            if (this._facetDataEnabled) {
+                this._facetDataEnabled = false;
+                this._facetPositions = null;
+                this._facetNormals = null;
+                this._facetPartitioning = null;
+                this._facetParameters = null;
+            }
+            return this;
+        } 
+
     }
     }
 }
 }

+ 4 - 4
src/Mesh/babylon.geometry.ts

@@ -11,7 +11,7 @@
         private _engine: Engine;
         private _engine: Engine;
         private _meshes: Mesh[];
         private _meshes: Mesh[];
         private _totalVertices = 0;
         private _totalVertices = 0;
-        private _indices: number[] | Int32Array;
+        private _indices: IndicesArray;
         private _vertexBuffers: { [key: string]: VertexBuffer; };
         private _vertexBuffers: { [key: string]: VertexBuffer; };
         private _isDisposed = false;
         private _isDisposed = false;
         private _extend: { minimum: Vector3, maximum: Vector3 };
         private _extend: { minimum: Vector3, maximum: Vector3 };
@@ -285,7 +285,7 @@
             return result;
             return result;
         }
         }
 
 
-        public setIndices(indices: number[] | Int32Array, totalVertices?: number): void {
+        public setIndices(indices: IndicesArray, totalVertices?: number): void {
             if (this._indexBuffer) {
             if (this._indexBuffer) {
                 this._engine._releaseBuffer(this._indexBuffer);
                 this._engine._releaseBuffer(this._indexBuffer);
             }
             }
@@ -317,7 +317,7 @@
             return this._indices.length;
             return this._indices.length;
         }
         }
 
 
-        public getIndices(copyWhenShared?: boolean): number[] | Int32Array {
+        public getIndices(copyWhenShared?: boolean): IndicesArray {
             if (!this.isReady()) {
             if (!this.isReady()) {
                 return null;
                 return null;
             }
             }
@@ -1017,7 +1017,7 @@
                 super.setAllVerticesData(vertexData, false);
                 super.setAllVerticesData(vertexData, false);
             }
             }
 
 
-            public setVerticesData(kind: string, data: number[] | Int32Array | Float32Array, updatable?: boolean): void {
+            public setVerticesData(kind: string, data: number[] | Float32Array, updatable?: boolean): void {
                 if (!this._beingRegenerated) {
                 if (!this._beingRegenerated) {
                     return;
                     return;
                 }
                 }

+ 1 - 1
src/Mesh/babylon.instancedMesh.ts

@@ -71,7 +71,7 @@
             return this._sourceMesh.isVerticesDataPresent(kind);
             return this._sourceMesh.isVerticesDataPresent(kind);
         }
         }
 
 
-        public getIndices(): number[] | Int32Array {
+        public getIndices(): IndicesArray {
             return this._sourceMesh.getIndices();
             return this._sourceMesh.getIndices();
         }
         }
 
 

+ 4 - 301
src/Mesh/babylon.mesh.ts

@@ -121,53 +121,6 @@
         private _sourcePositions: Float32Array; // Will be used to save original positions when using software skinning
         private _sourcePositions: Float32Array; // Will be used to save original positions when using software skinning
         private _sourceNormals: Float32Array;   // Will be used to save original normals when using software skinning
         private _sourceNormals: Float32Array;   // Will be used to save original normals when using software skinning
 
 
-        private _facetPositions: Vector3[];             // facet local positions
-        private _facetNormals: Vector3[];               // facet local normals
-        private _facetPartitioning: number[][];           // partitioning array of facet index arrays
-        private _facetNb: number = 0;                   // facet number
-        private _partitioningSubdivisions: number = 10; // number of subdivisions per axis in the partioning space  
-        private _partitioningBBoxRatio: number = 1.01;  // the partioning array space is by default 1% bigger than the bounding box
-        private _facetDataEnabled: boolean = false;     // is the facet data feature enabled on this mesh ?
-        private _facetParameters: any = {};                  // keep a reference to the object parameters to avoid memory re-allocation
-        private _bbSize: Vector3 = Vector3.Zero();      // bbox size approximated for facet data
-        private _subDiv = {                         // actual number of subdivisions per axis for ComputeNormals()
-            max: 1,
-            X: 1,
-            Y: 1,
-            Z: 1
-        };
-        /**
-         * Read-only : the number of facets in the mesh
-         */
-        public get facetNb(): number {
-            return this._facetNb;
-        }
-        /**
-         * The number of subdivisions per axis in the partioning space
-         */
-        public get partitioningSubdivisions(): number {
-            return this._partitioningSubdivisions;
-        }
-        public set partitioningSubdivisions(nb: number) {
-            this._partitioningSubdivisions = nb;
-        } 
-        /**
-         * The ratio to apply to the bouding box size to set to the partioning space.  
-         * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box.
-         */
-        public get partitioningBBoxRatio(): number {
-            return this._partitioningBBoxRatio;
-        }
-        public set partitioningBBoxRatio(ratio: number) {
-            this._partitioningBBoxRatio = ratio;
-        }
-        /**
-         * Read-only : is the feature facetData enabled ?
-         */
-        public get isFacetDataEnabled(): boolean {
-            return this._facetDataEnabled;
-        }
-
         // Will be used to save a source mesh reference, If any
         // Will be used to save a source mesh reference, If any
         private _source: BABYLON.Mesh = null; 
         private _source: BABYLON.Mesh = null; 
         public get source(): BABYLON.Mesh {
         public get source(): BABYLON.Mesh {
@@ -524,11 +477,11 @@
         }
         }
 
 
         /**
         /**
-         * Returns an array of integers or a Int32Array populated with the mesh indices.  
+         * Returns an array of integers or a typed array (Int32Array, Uint32Array, Uint16Array) populated with the mesh indices.  
          * If the parameter `copyWhenShared` is true (default false) and and if the mesh geometry is shared among some other meshes, the returned array is a copy of the internal one.
          * If the parameter `copyWhenShared` is true (default false) and and if the mesh geometry is shared among some other meshes, the returned array is a copy of the internal one.
          * Returns an empty array if the mesh has no geometry.
          * Returns an empty array if the mesh has no geometry.
          */
          */
-        public getIndices(copyWhenShared?: boolean): number[] | Int32Array {
+        public getIndices(copyWhenShared?: boolean): IndicesArray {
 
 
             if (!this._geometry) {
             if (!this._geometry) {
                 return [];
                 return [];
@@ -829,11 +782,11 @@
 
 
         /**
         /**
          * Sets the mesh indices.  
          * Sets the mesh indices.  
-         * Expects an array populated with integers or a Int32Array.
+         * Expects an array populated with integers or a typed array (Int32Array, Uint32Array, Uint16Array).
          * If the mesh has no geometry, a new Geometry object is created and set to the mesh. 
          * If the mesh has no geometry, a new Geometry object is created and set to the mesh. 
          * This method creates a new index buffer each call.
          * This method creates a new index buffer each call.
          */
          */
-        public setIndices(indices: number[] | Int32Array, totalVertices?: number): void {
+        public setIndices(indices: IndicesArray, totalVertices?: number): void {
             if (!this._geometry) {
             if (!this._geometry) {
                 var vertexData = new VertexData();
                 var vertexData = new VertexData();
                 vertexData.indices = indices;
                 vertexData.indices = indices;
@@ -1423,12 +1376,6 @@
                     highlightLayer.removeExcludedMesh(this);
                     highlightLayer.removeExcludedMesh(this);
                 }
                 }
             }
             }
-
-            // facet data
-            if (this._facetDataEnabled) {
-                this.disableFacetData();
-            }
-
             super.dispose(doNotRecurse);
             super.dispose(doNotRecurse);
         }
         }
 
 
@@ -1813,250 +1760,6 @@
             });
             });
         }
         }
 
 
-        // Facet data
-        /** 
-         *  Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning
-         */
-        private _initFacetData(): Mesh {
-            if (!this._facetNormals) {
-                this._facetNormals = new Array<Vector3>();
-            }
-            if (!this._facetPositions) {
-                this._facetPositions = new Array<Vector3>();
-            }
-            if (!this._facetPartitioning) {
-                this._facetPartitioning = new Array<number[]>();
-            }
-            this._facetNb = this.getIndices().length / 3;
-            this._partitioningSubdivisions = (this._partitioningSubdivisions) ? this._partitioningSubdivisions : 10;   // default nb of partitioning subdivisions = 10
-            this._partitioningBBoxRatio = (this._partitioningBBoxRatio) ? this._partitioningBBoxRatio : 1.01;          // default ratio 1.01 = the partitioning is 1% bigger than the bounding box
-            for (var f = 0; f < this._facetNb; f++) {
-                this._facetNormals[f] = Vector3.Zero();
-                this._facetPositions[f] = Vector3.Zero();
-            }
-            this._facetDataEnabled = true;           
-            return this;
-        }
-
-        /**
-         * Updates the mesh facetData arrays and the internal partitioning when the mesh is morphed or updated.  
-         * This method can be called within the render loop.  
-         * You don't need to call this method by yourself in the render loop when you update/morph a mesh with the methods CreateXXX() as they automatically manage this computation.  
-         */
-        public updateFacetData(): Mesh {
-            if (!this._facetDataEnabled) {
-                this._initFacetData();
-            }
-            var positions = this.getVerticesData(VertexBuffer.PositionKind);
-            var indices = this.getIndices();
-            var normals = this.getVerticesData(VertexBuffer.NormalKind);
-            var bInfo = this.getBoundingInfo();
-            this._bbSize.x = (bInfo.maximum.x - bInfo.minimum.x > Epsilon) ? bInfo.maximum.x - bInfo.minimum.x : Epsilon;
-            this._bbSize.y = (bInfo.maximum.y - bInfo.minimum.y > Epsilon) ? bInfo.maximum.y - bInfo.minimum.y : Epsilon;
-            this._bbSize.z = (bInfo.maximum.z - bInfo.minimum.z > Epsilon) ? bInfo.maximum.z - bInfo.minimum.z : Epsilon;
-            var bbSizeMax = (this._bbSize.x > this._bbSize.y) ? this._bbSize.x : this._bbSize.y;
-            bbSizeMax = (bbSizeMax > this._bbSize.z) ? bbSizeMax : this._bbSize.z;
-            this._subDiv.max = this._partitioningSubdivisions;
-            this._subDiv.X = Math.floor(this._subDiv.max * this._bbSize.x / bbSizeMax);   // adjust the number of subdivisions per axis
-            this._subDiv.Y = Math.floor(this._subDiv.max * this._bbSize.y / bbSizeMax);   // according to each bbox size per axis
-            this._subDiv.Z = Math.floor(this._subDiv.max * this._bbSize.z / bbSizeMax);
-            this._subDiv.X = this._subDiv.X < 1 ? 1 : this._subDiv.X;                     // at least one subdivision
-            this._subDiv.Y = this._subDiv.Y < 1 ? 1 : this._subDiv.Y;
-            this._subDiv.Z = this._subDiv.Z < 1 ? 1 : this._subDiv.Z;
-            // set the parameters for ComputeNormals()
-            this._facetParameters.facetNormals = this.getFacetLocalNormals(); 
-            this._facetParameters.facetPositions = this.getFacetLocalPositions();
-            this._facetParameters.facetPartitioning = this.getFacetLocalPartitioning();
-            this._facetParameters.bInfo = bInfo;
-            this._facetParameters.bbSize = this._bbSize;
-            this._facetParameters.subDiv = this._subDiv;
-            this._facetParameters.ratio = this.partitioningBBoxRatio;
-            VertexData.ComputeNormals(positions, indices, normals, this._facetParameters);
-            return this;
-        }
-        /**
-         * Returns the facetLocalNormals array.  
-         * The normals are expressed in the mesh local space.  
-         */
-        public getFacetLocalNormals(): Vector3[] {
-            if (!this._facetNormals) {
-                this.updateFacetData();
-            }
-            return this._facetNormals;
-        }
-        /**
-         * Returns the facetLocalPositions array.  
-         * The facet positions are expressed in the mesh local space.  
-         */
-        public getFacetLocalPositions(): Vector3[] {
-            if (!this._facetPositions) {
-                this.updateFacetData();
-            }
-            return this._facetPositions;           
-        }
-        /**
-         * Returns the facetLocalPartioning array
-         */
-        public getFacetLocalPartitioning(): number[][] {
-            if (!this._facetPartitioning) {
-                this.updateFacetData();
-            }
-            return this._facetPartitioning;
-        }
-        /**
-         * Returns the i-th facet position in the world system.  
-         * This method allocates a new Vector3 per call.  
-         */
-        public getFacetPosition(i: number): Vector3 {
-            var pos = Vector3.Zero();
-            this.getFacetPositionToRef(i, pos);
-            return pos;
-        }
-        /**
-         * Sets the reference Vector3 with the i-th facet position in the world system.  
-         * Returns the mesh.  
-         */
-        public getFacetPositionToRef(i: number, ref: Vector3): Mesh {
-            var localPos = (this.getFacetLocalPositions())[i];
-            var world = this.getWorldMatrix();
-            Vector3.TransformCoordinatesToRef(localPos, world, ref);
-            return this;
-        }
-        /**
-         * Returns the i-th facet normal in the world system.  
-         * This method allocates a new Vector3 per call.  
-         */
-        public getFacetNormal(i: number): Vector3 {
-            var norm = Vector3.Zero();
-            this.getFacetNormalToRef(i, norm);
-            return norm;
-        }
-        /**
-         * Sets the reference Vector3 with the i-th facet normal in the world system.  
-         * Returns the mesh.  
-         */
-        public getFacetNormalToRef(i: number, ref: Vector3) {
-            var localNorm = (this.getFacetLocalNormals())[i];
-            Vector3.TransformNormalToRef(localNorm, this.getWorldMatrix(), ref);
-            return this;
-        }
-        /** 
-         * Returns the facets (in an array) in the same partitioning block than the one the passed coordinates are located (expressed in the mesh local system).
-         */
-        public getFacetsAtLocalCoordinates(x: number, y: number, z: number): number[] {
-            var bInfo = this.getBoundingInfo();
-            var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) * this._subDiv.X * this._partitioningBBoxRatio / this._bbSize.x);
-            var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) * this._subDiv.Y * this._partitioningBBoxRatio / this._bbSize.y);
-            var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) * this._subDiv.Z * this._partitioningBBoxRatio / this._bbSize.z);
-            if (ox < 0 || ox > this._subDiv.max || oy < 0 || oy > this._subDiv.max || oz < 0 || oz > this._subDiv.max) {
-                return null;
-            }
-            return this._facetPartitioning[ox + this._subDiv.max * oy + this._subDiv.max * this._subDiv.max * oz];
-        }
-        /** 
-         * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found.  
-         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) World projection on the facet.  
-         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
-         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
-         * If facing si false and checkFace is true, only the facet "turning their backs" to (x, y, z) are returned : negative dot (x, y, z) * facet position. 
-         */
-        public getClosestFacetAtCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): number {
-            var world = this.getWorldMatrix();
-            var invMat = Tmp.Matrix[5];
-            world.invertToRef(invMat);
-            var invVect = Tmp.Vector3[8];
-            var closest = null;
-            Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect);  // transform (x,y,z) to coordinates in the mesh local space
-            closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, checkFace, facing);
-            if (projected) {
-                // tranform the local computed projected vector to world coordinates
-                Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
-            }
-            return closest;
-        }
-        /** 
-         * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found.   
-         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) local projection on the facet.  
-         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
-         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
-         * If facing si false and checkFace is true, only the facet "turning their backs"  to (x, y, z) are returned : negative dot (x, y, z) * facet position.
-         */
-        public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true) {
-            var closest = null;
-            var tmpx = 0.0;         
-            var tmpy = 0.0;
-            var tmpz = 0.0;
-            var d = 0.0;            // tmp dot facet normal * facet position
-            var t0 = 0.0;
-            var projx = 0.0;
-            var projy = 0.0;
-            var projz = 0.0;
-            // Get all the facets in the same partitioning block than (x, y, z)
-            var facetPositions = this.getFacetLocalPositions();
-            var facetNormals = this.getFacetLocalNormals();
-            var facetsInBlock = this.getFacetsAtLocalCoordinates(x, y, z);
-            if (!facetsInBlock) {
-                return null;
-            }
-            // Get the closest facet to (x, y, z)
-            var shortest = Number.MAX_VALUE;            // init distance vars
-            var tmpDistance = shortest;
-            var fib;                                    // current facet in the block
-            var norm;                                   // current facet normal
-            var p0;                                     // current facet barycenter position
-            // loop on all the facets in the current partitioning block
-            for (var idx = 0; idx < facetsInBlock.length; idx++) {
-                fib = facetsInBlock[idx];           
-                norm = facetNormals[fib];
-                p0 = facetPositions[fib];
-
-                d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
-                if ( !checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0) ) {
-                    // compute (x,y,z) projection on the facet = (projx, projy, projz)
-                    d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z; 
-                    t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
-                    projx = x + norm.x * t0;
-                    projy = y + norm.y * t0;
-                    projz = z + norm.z * t0;
-
-                    tmpx = projx - x;
-                    tmpy = projy - y;
-                    tmpz = projz - z;
-                    tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz;             // compute length between (x, y, z) and its projection on the facet
-                    if (tmpDistance < shortest) {                                      // just keep the closest facet to (x, y, z)
-                        shortest = tmpDistance;
-                        closest = fib; 
-                        if (projected) {
-                            projected.x = projx;
-                            projected.y = projy;
-                            projected.z = projz;
-                        }
-                    }
-                }
-            }
-            return closest;
-        }
-        /**
-         * Returns the object "parameter" set with all the expected parameters for facetData computation by ComputeNormals()  
-         */
-        public getFacetDataParameters(): any {
-            return this._facetParameters;
-        }
-        /** 
-         * Disables the feature FacetData and frees the related memory.  
-         * Returns the mesh.  
-         */
-        public disableFacetData(): Mesh {
-            if (this._facetDataEnabled) {
-                this._facetDataEnabled = false;
-                this._facetPositions = null;
-                this._facetNormals = null;
-                this._facetPartitioning = null;
-                this._facetParameters = null;
-            }
-            return this;
-        }
-
         // Statics
         // Statics
         /**
         /**
          * Returns a new Mesh object what is a deep copy of the passed mesh.   
          * Returns a new Mesh object what is a deep copy of the passed mesh.   

+ 9 - 6
src/Mesh/babylon.mesh.vertexData.ts

@@ -1,11 +1,14 @@
 module BABYLON {
 module BABYLON {
-    export interface IGetSetVerticesData {
+    export type IndicesArray = number[] | Int32Array | Uint32Array | Uint16Array;
+
+    export interface IGetSetVerticesData
+    {
         isVerticesDataPresent(kind: string): boolean;
         isVerticesDataPresent(kind: string): boolean;
-        getVerticesData(kind: string, copyWhenShared?: boolean): number[] | Int32Array | Float32Array;
-        getIndices(copyWhenShared?: boolean): number[] | Int32Array;
+        getVerticesData(kind: string, copyWhenShared?: boolean): number[] | Float32Array;
+        getIndices(copyWhenShared?: boolean): IndicesArray;
         setVerticesData(kind: string, data: number[] | Float32Array, updatable?: boolean): void;
         setVerticesData(kind: string, data: number[] | Float32Array, updatable?: boolean): void;
         updateVerticesData(kind: string, data: number[] | Float32Array, updateExtends?: boolean, makeItUnique?: boolean): void;
         updateVerticesData(kind: string, data: number[] | Float32Array, updateExtends?: boolean, makeItUnique?: boolean): void;
-        setIndices(indices: number[] | Int32Array): void;
+        setIndices(indices: IndicesArray): void;
     }
     }
 
 
     export class VertexData {
     export class VertexData {
@@ -22,7 +25,7 @@
         public matricesWeights: number[] | Float32Array;
         public matricesWeights: number[] | Float32Array;
         public matricesIndicesExtra: number[] | Float32Array;
         public matricesIndicesExtra: number[] | Float32Array;
         public matricesWeightsExtra: number[] | Float32Array;
         public matricesWeightsExtra: number[] | Float32Array;
-        public indices: number[] | Int32Array;
+        public indices: IndicesArray;
 
 
         public set(data: number[] | Float32Array, kind: string) {
         public set(data: number[] | Float32Array, kind: string) {
             switch (kind) {
             switch (kind) {
@@ -238,7 +241,7 @@
 
 
                 var offset = this.positions ? this.positions.length / 3 : 0;
                 var offset = this.positions ? this.positions.length / 3 : 0;
                 for (var index = 0; index < other.indices.length; index++) {
                 for (var index = 0; index < other.indices.length; index++) {
-                    //TODO check type - if Int32Array!
+                    //TODO check type - if Int32Array | Uint32Array | Uint16Array!
                     (<number[]>this.indices).push(other.indices[index] + offset);
                     (<number[]>this.indices).push(other.indices[index] + offset);
                 }
                 }
             }
             }

+ 1 - 1
src/Mesh/babylon.meshBuilder.ts

@@ -1093,7 +1093,7 @@
                 for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
                 for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
                     var vertex = faceVertices[vIndex];
                     var vertex = faceVertices[vIndex];
 
 
-                    //TODO check for Int32Array
+                    //TODO check for Int32Array | Uint32Array | Uint16Array
                     (<number[]>vertexData.indices).push(currentVertexDataIndex);
                     (<number[]>vertexData.indices).push(currentVertexDataIndex);
                     vertex.position.toArray(vertexData.positions, currentVertexDataIndex * 3);
                     vertex.position.toArray(vertexData.positions, currentVertexDataIndex * 3);
                     vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
                     vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);

+ 2 - 2
src/Mesh/babylon.subMesh.ts

@@ -119,7 +119,7 @@
             this._renderingMesh.render(this, enableAlphaMode);
             this._renderingMesh.render(this, enableAlphaMode);
         }
         }
 
 
-        public getLinesIndexBuffer(indices: number[] | Int32Array, engine: Engine): WebGLBuffer {
+        public getLinesIndexBuffer(indices: IndicesArray, engine: Engine): WebGLBuffer {
             if (!this._linesIndexBuffer) {
             if (!this._linesIndexBuffer) {
                 var linesIndices = [];
                 var linesIndices = [];
 
 
@@ -139,7 +139,7 @@
             return ray.intersectsBox(this.getBoundingInfo().boundingBox);
             return ray.intersectsBox(this.getBoundingInfo().boundingBox);
         }
         }
 
 
-        public intersects(ray: Ray, positions: Vector3[], indices: number[] | Int32Array, fastCheck?: boolean): IntersectionInfo {
+        public intersects(ray: Ray, positions: Vector3[], indices: IndicesArray, fastCheck?: boolean): IntersectionInfo {
             var intersectInfo: IntersectionInfo = null;
             var intersectInfo: IntersectionInfo = null;
 
 
             // LineMesh first as it's also a Mesh...
             // LineMesh first as it's also a Mesh...

+ 1 - 1
src/Physics/babylon.physicsImpostor.ts

@@ -17,7 +17,7 @@ module BABYLON {
         computeWorldMatrix?(force: boolean): void;
         computeWorldMatrix?(force: boolean): void;
         getChildMeshes?(): Array<AbstractMesh>;
         getChildMeshes?(): Array<AbstractMesh>;
         getVerticesData?(kind: string): Array<number> | Float32Array;
         getVerticesData?(kind: string): Array<number> | Float32Array;
-        getIndices?(): Array<number> | Int32Array;
+        getIndices?(): IndicesArray;
         getScene?(): Scene;
         getScene?(): Scene;
     }
     }
 
 

+ 1 - 3
src/Tools/babylon.tools.ts

@@ -135,7 +135,7 @@
             return "data:image/png;base64," + output;
             return "data:image/png;base64," + output;
         }
         }
 
 
-        public static ExtractMinAndMaxIndexed(positions: number[] | Float32Array, indices: number[] | Int32Array, indexStart: number, indexCount: number, bias: Vector2 = null): { minimum: Vector3; maximum: Vector3 } {
+        public static ExtractMinAndMaxIndexed(positions: number[] | Float32Array, indices: IndicesArray, indexStart: number, indexCount: number, bias: Vector2 = null): { minimum: Vector3; maximum: Vector3 } {
             var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
             var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
 
 
@@ -740,8 +740,6 @@
             var offsetX = Math.max(0, width - newWidth) / 2;
             var offsetX = Math.max(0, width - newWidth) / 2;
             var offsetY = Math.max(0, height - newHeight) / 2;
             var offsetY = Math.max(0, height - newHeight) / 2;
 
 
-            renderContext.fillStyle = camera.getScene().clearColor.toHexString();
-            renderContext.fillRect(0, 0, width, height);
             renderContext.drawImage(engine.getRenderingCanvas(), offsetX, offsetY, newWidth, newHeight);
             renderContext.drawImage(engine.getRenderingCanvas(), offsetX, offsetY, newWidth, newHeight);
 
 
             Tools.EncodeScreenshotCanvasData(successCallback, mimeType);
             Tools.EncodeScreenshotCanvasData(successCallback, mimeType);

+ 7 - 7
src/Tools/babylon.virtualJoystick.ts

@@ -230,15 +230,15 @@ module BABYLON {
 
 
         private _onPointerUp(e: PointerEvent) {
         private _onPointerUp(e: PointerEvent) {
             if (this._joystickPointerID == e.pointerId) {
             if (this._joystickPointerID == e.pointerId) {
-                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);
-                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
+                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 64, this._joystickPointerStartPos.y - 64, 128, 128);
+                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 42, this._joystickPreviousPointerPos.y - 42, 84, 84);
                 this._joystickPointerID = -1;
                 this._joystickPointerID = -1;
                 this.pressed = false;
                 this.pressed = false;
             }
             }
             else {
             else {
                 var touch = <{ x: number, y: number, prevX: number, prevY: number }>this._touches.get(e.pointerId.toString());
                 var touch = <{ x: number, y: number, prevX: number, prevY: number }>this._touches.get(e.pointerId.toString());
                 if (touch) {
                 if (touch) {
-                    VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
+                    VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 44, touch.prevY - 44, 88, 88);
                 }
                 }
             }
             }
             this._deltaJoystickVector.x = 0;
             this._deltaJoystickVector.x = 0;
@@ -300,8 +300,8 @@ module BABYLON {
             if (this.pressed) {
             if (this.pressed) {
                 this._touches.forEach((key, touch) => {
                 this._touches.forEach((key, touch) => {
                     if ((<PointerEvent>touch).pointerId === this._joystickPointerID) {
                     if ((<PointerEvent>touch).pointerId === this._joystickPointerID) {
-                        VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);                       
-                        VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
+                        VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 64, this._joystickPointerStartPos.y - 64, 128, 128);                       
+                        VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 42, this._joystickPreviousPointerPos.y - 42, 84, 84);
                         VirtualJoystick.vjCanvasContext.beginPath();
                         VirtualJoystick.vjCanvasContext.beginPath();
                         VirtualJoystick.vjCanvasContext.lineWidth = 6;
                         VirtualJoystick.vjCanvasContext.lineWidth = 6;
                         VirtualJoystick.vjCanvasContext.strokeStyle = this._joystickColor;
                         VirtualJoystick.vjCanvasContext.strokeStyle = this._joystickColor;
@@ -318,11 +318,11 @@ module BABYLON {
                         VirtualJoystick.vjCanvasContext.strokeStyle = this._joystickColor;
                         VirtualJoystick.vjCanvasContext.strokeStyle = this._joystickColor;
                         VirtualJoystick.vjCanvasContext.arc(this._joystickPointerPos.x, this._joystickPointerPos.y, 40, 0, Math.PI * 2, true);
                         VirtualJoystick.vjCanvasContext.arc(this._joystickPointerPos.x, this._joystickPointerPos.y, 40, 0, Math.PI * 2, true);
                         VirtualJoystick.vjCanvasContext.stroke();
                         VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
+                        VirtualJoystick.vjCanvasContext.closePath(); 
                         this._joystickPreviousPointerPos = this._joystickPointerPos.clone();
                         this._joystickPreviousPointerPos = this._joystickPointerPos.clone();
                     }
                     }
                     else {
                     else {
-                        VirtualJoystick.vjCanvasContext.clearRect((<any>touch).prevX - 43, (<any>touch).prevY - 43, 86, 86);
+                        VirtualJoystick.vjCanvasContext.clearRect((<any>touch).prevX - 44, (<any>touch).prevY - 44, 88, 88);
                         VirtualJoystick.vjCanvasContext.beginPath();
                         VirtualJoystick.vjCanvasContext.beginPath();
                         VirtualJoystick.vjCanvasContext.fillStyle = "white";
                         VirtualJoystick.vjCanvasContext.fillStyle = "white";
                         VirtualJoystick.vjCanvasContext.beginPath();
                         VirtualJoystick.vjCanvasContext.beginPath();

+ 21 - 10
src/babylon.engine.ts

@@ -1308,7 +1308,7 @@
             this._cachedIndexBuffer = null;
             this._cachedIndexBuffer = null;
         }
         }
 
 
-        public createIndexBuffer(indices: number[] | Int32Array): WebGLBuffer {
+        public createIndexBuffer(indices: IndicesArray): WebGLBuffer {
             var vbo = this._gl.createBuffer();
             var vbo = this._gl.createBuffer();
             this.bindIndexBuffer(vbo);
             this.bindIndexBuffer(vbo);
 
 
@@ -1316,18 +1316,29 @@
             var arrayBuffer;
             var arrayBuffer;
             var need32Bits = false;
             var need32Bits = false;
 
 
-            if (this._caps.uintIndices) {
-
-                for (var index = 0; index < indices.length; index++) {
-                    if (indices[index] > 65535) {
+            if (indices instanceof Uint16Array) {
+                arrayBuffer = indices;
+            } else {
+                //check 32 bit support
+                if (this._caps.uintIndices) {
+                    if (indices instanceof Uint32Array) {
+                        arrayBuffer = indices;
                         need32Bits = true;
                         need32Bits = true;
-                        break;
+                    } else {
+                        //number[] or Int32Array, check if 32 bit is necessary
+                        for (var index = 0; index < indices.length; index++) {
+                            if (indices[index] > 65535) {
+                                need32Bits = true;
+                                break;
+                            }
+                        }
+
+                        arrayBuffer = need32Bits ? new Uint32Array(indices) : new Uint16Array(indices);
                     }
                     }
+                } else {
+                    //no 32 bit support, force conversion to 16 bit (values greater 16 bit are lost)
+                    arrayBuffer = new Uint16Array(indices);
                 }
                 }
-
-                arrayBuffer = need32Bits ? new Uint32Array(indices) : new Uint16Array(indices);
-            } else {
-                arrayBuffer = new Uint16Array(indices);
             }
             }
 
 
             this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER, arrayBuffer, this._gl.STATIC_DRAW);
             this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER, arrayBuffer, this._gl.STATIC_DRAW);

+ 2 - 0
tests/Canvas2d/Jasmine/chutzpah.json

@@ -93,6 +93,7 @@
         { "Path": "../../../src/Materials/babylon.multiMaterial.js" },
         { "Path": "../../../src/Materials/babylon.multiMaterial.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.fontTexture.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.fontTexture.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.bounding2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.bounding2d.js" },
+        { "Path": "../../../canvas2d/src/Engine/babylon.primitiveCollisionManager.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.canvas2dLayoutEngine.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.canvas2dLayoutEngine.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.brushes2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.brushes2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.smartPropertyPrim.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.smartPropertyPrim.js" },
@@ -101,6 +102,7 @@
         { "Path": "../../../canvas2d/src/Engine/babylon.renderablePrim2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.renderablePrim2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.shape2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.shape2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.group2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.group2d.js" },
+        { "Path": "../../../canvas2d/src/Engine/babylon.wireFrame2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.rectangle2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.rectangle2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.sprite2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.sprite2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.text2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.text2d.js" },

+ 2 - 0
tests/Tools/Jasmine/chutzpah.json

@@ -93,6 +93,7 @@
         { "Path": "../../../src/Materials/babylon.multiMaterial.js" },
         { "Path": "../../../src/Materials/babylon.multiMaterial.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.fontTexture.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.fontTexture.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.bounding2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.bounding2d.js" },
+        { "Path": "../../../canvas2d/src/Engine/babylon.primitiveCollisionManager.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.canvas2dLayoutEngine.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.canvas2dLayoutEngine.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.brushes2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.brushes2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.smartPropertyPrim.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.smartPropertyPrim.js" },
@@ -101,6 +102,7 @@
         { "Path": "../../../canvas2d/src/Engine/babylon.renderablePrim2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.renderablePrim2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.shape2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.shape2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.group2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.group2d.js" },
+        { "Path": "../../../canvas2d/src/Engine/babylon.wireFrame2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.rectangle2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.rectangle2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.sprite2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.sprite2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.text2d.js" },
         { "Path": "../../../canvas2d/src/Engine/babylon.text2d.js" },