소스 검색

update from upstream

nockawa 9 년 전
부모
커밋
5f2a905d35
100개의 변경된 파일12205개의 추가작업 그리고 5994개의 파일을 삭제
  1. BIN
      Exporters/3ds Max/Max2Babylon-0.4.8.zip
  2. BIN
      Exporters/3ds Max/Max2Babylon-0.4.9.zip
  3. 12 11
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs
  4. 8 2
      Exporters/Blender/io_export_babylon.py
  5. 2 0
      Exporters/Unity 5/Unity3D2Babylon/ExportationOptions.cs
  6. 1 1
      Exporters/Unity 5/Unity3D2Babylon/ExporterWindow.cs
  7. 25 23
      Tools/Gulp/config.json
  8. 21 10
      Tools/Gulp/gulpfile.js
  9. 2 1
      Tools/Gulp/package.json
  10. 25 25
      dist/preview release/babylon.core.js
  11. 5121 4501
      dist/preview release/babylon.d.ts
  12. 38 37
      dist/preview release/babylon.js
  13. 2871 499
      dist/preview release/babylon.max.js
  14. 38 37
      dist/preview release/babylon.noworker.js
  15. 29 12
      dist/preview release/what's new.md
  16. 4 0
      src/Bones/babylon.skeleton.js
  17. 20 16
      src/Bones/babylon.skeleton.ts
  18. 10 7
      src/Cameras/VR/babylon.webVRCamera.js
  19. 12 9
      src/Cameras/VR/babylon.webVRCamera.ts
  20. 3 0
      src/Cameras/babylon.camera.js
  21. 37 38
      src/Cameras/babylon.camera.ts
  22. 49 37
      src/Canvas2d/babylon.canvas2d.js
  23. 12 2
      src/Canvas2d/babylon.canvas2d.ts
  24. 306 0
      src/Canvas2d/babylon.ellipse2d.js
  25. 2 2
      src/Canvas2d/babylon.ellipse2d.ts
  26. 17 7
      src/Canvas2d/babylon.group2d.js
  27. 991 0
      src/Canvas2d/babylon.lines2d.js
  28. 2 2
      src/Canvas2d/babylon.lines2d.ts
  29. 2 2
      src/Canvas2d/babylon.prim2dBase.js
  30. 31 19
      src/Canvas2d/babylon.rectangle2d.js
  31. 2 2
      src/Canvas2d/babylon.rectangle2d.ts
  32. 42 6
      src/Canvas2d/babylon.renderablePrim2d.js
  33. 2 2
      src/Canvas2d/babylon.shape2d.js
  34. 13 0
      src/Canvas2d/babylon.smartPropertyPrim.js
  35. 27 11
      src/Canvas2d/babylon.sprite2d.js
  36. 1 1
      src/Canvas2d/babylon.sprite2d.ts
  37. 22 9
      src/Canvas2d/babylon.text2d.js
  38. 1 1
      src/Canvas2d/babylon.text2d.ts
  39. 6 6
      src/Canvas2d/babylon.worldSpaceCanvas2d.js
  40. 12 10
      src/Layer/babylon.layer.js
  41. 15 13
      src/Layer/babylon.layer.ts
  42. 10 9
      src/LensFlare/babylon.lensFlareSystem.js
  43. 12 11
      src/LensFlare/babylon.lensFlareSystem.ts
  44. 16 8
      src/Materials/Textures/Procedurals/babylon.proceduralTexture.js
  45. 21 11
      src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts
  46. 11 3
      src/Materials/Textures/babylon.renderTargetTexture.js
  47. 13 3
      src/Materials/Textures/babylon.renderTargetTexture.ts
  48. 545 0
      src/Materials/babylon.colorCurves.js
  49. 535 0
      src/Materials/babylon.colorCurves.ts
  50. 18 0
      src/Materials/babylon.pbrMaterial.js
  51. 19 0
      src/Materials/babylon.pbrMaterial.ts
  52. 48 0
      src/Math/babylon.math.js
  53. 29 5
      src/Math/babylon.math.ts
  54. 6 1
      src/Mesh/babylon.abstractMesh.ts
  55. 82 0
      src/Mesh/babylon.buffer.js
  56. 102 0
      src/Mesh/babylon.buffer.ts
  57. 45 8
      src/Mesh/babylon.geometry.js
  58. 56 12
      src/Mesh/babylon.geometry.ts
  59. 5 3
      src/Mesh/babylon.linesMesh.js
  60. 7 3
      src/Mesh/babylon.linesMesh.ts
  61. 125 83
      src/Mesh/babylon.mesh.js
  62. 133 87
      src/Mesh/babylon.mesh.ts
  63. 28 19
      src/Mesh/babylon.meshBuilder.js
  64. 28 19
      src/Mesh/babylon.meshBuilder.ts
  65. 63 74
      src/Mesh/babylon.vertexBuffer.js
  66. 74 84
      src/Mesh/babylon.vertexBuffer.ts
  67. 26 22
      src/Particles/babylon.particleSystem.js
  68. 31 25
      src/Particles/babylon.particleSystem.ts
  69. 6 6
      src/Particles/babylon.solidParticleSystem.js
  70. 6 6
      src/Particles/babylon.solidParticleSystem.ts
  71. 2 2
      src/PostProcess/babylon.anaglyphPostProcess.js
  72. 2 2
      src/PostProcess/babylon.anaglyphPostProcess.ts
  73. 2 2
      src/PostProcess/babylon.blackAndWhitePostProcess.js
  74. 2 2
      src/PostProcess/babylon.blackAndWhitePostProcess.ts
  75. 2 2
      src/PostProcess/babylon.blurPostProcess.js
  76. 2 2
      src/PostProcess/babylon.blurPostProcess.ts
  77. 2 2
      src/PostProcess/babylon.colorCorrectionPostProcess.js
  78. 2 2
      src/PostProcess/babylon.colorCorrectionPostProcess.ts
  79. 2 2
      src/PostProcess/babylon.convolutionPostProcess.js
  80. 2 2
      src/PostProcess/babylon.convolutionPostProcess.ts
  81. 2 2
      src/PostProcess/babylon.displayPassPostProcess.js
  82. 2 2
      src/PostProcess/babylon.displayPassPostProcess.ts
  83. 2 2
      src/PostProcess/babylon.filterPostProcess.js
  84. 2 2
      src/PostProcess/babylon.filterPostProcess.ts
  85. 2 2
      src/PostProcess/babylon.fxaaPostProcess.js
  86. 2 2
      src/PostProcess/babylon.fxaaPostProcess.ts
  87. 2 2
      src/PostProcess/babylon.passPostProcess.js
  88. 2 2
      src/PostProcess/babylon.passPostProcess.ts
  89. 8 8
      src/PostProcess/babylon.postProcess.js
  90. 13 10
      src/PostProcess/babylon.postProcess.ts
  91. 9 9
      src/PostProcess/babylon.postProcessManager.js
  92. 11 11
      src/PostProcess/babylon.postProcessManager.ts
  93. 2 2
      src/PostProcess/babylon.refractionPostProcess.js
  94. 2 2
      src/PostProcess/babylon.refractionPostProcess.ts
  95. 78 14
      src/PostProcess/babylon.ssaoRenderingPipeline.js
  96. 74 17
      src/PostProcess/babylon.ssaoRenderingPipeline.ts
  97. 11 6
      src/Rendering/babylon.boundingBoxRenderer.js
  98. 13 8
      src/Rendering/babylon.boundingBoxRenderer.ts
  99. 14 8
      src/Rendering/babylon.edgesRenderer.js
  100. 0 0
      src/Rendering/babylon.edgesRenderer.ts

BIN
Exporters/3ds Max/Max2Babylon-0.4.8.zip


BIN
Exporters/3ds Max/Max2Babylon-0.4.9.zip


+ 12 - 11
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs

@@ -192,7 +192,7 @@ namespace Max2Babylon
             var unskinnedMesh = gameMesh;
             IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
             List<int> boneIds = null;
-            int nbBones = 0;
+            int maxNbBones = 0;
             if (isSkinned)
             {
                 bonesCount = skin.TotalSkinBoneCount;
@@ -255,7 +255,7 @@ namespace Max2Babylon
                 {
                     for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++)
                     {
-                        nbBones = Math.Max(nbBones, skin.GetNumberOfBones(vertexIndex));
+                        maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex));
                     }
                 }
 
@@ -355,7 +355,7 @@ namespace Max2Babylon
                         for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                         {
                             var face = unskinnedMesh.GetFace(j);
-                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds, nbBones);
+                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                         }
                     }
                     else
@@ -373,7 +373,7 @@ namespace Max2Babylon
 #if !MAX2017
                             Marshal.FreeHGlobal(faceIndexer);
 #endif
-                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds, nbBones);
+                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
                         }
                     }
 
@@ -419,8 +419,8 @@ namespace Max2Babylon
                     babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                     babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();
 
-                    babylonMesh.numBoneInfluencers = nbBones;
-                    if (nbBones > 4)
+                    babylonMesh.numBoneInfluencers = maxNbBones;
+                    if (maxNbBones > 4)
                     {
                         babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra.ToArray()).ToArray();
                         babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray();
@@ -523,11 +523,11 @@ namespace Max2Babylon
             babylonScene.MeshesList.Add(babylonMesh);
         }
 
-        private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds, int nbBones)
+        private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds)
         {
-            var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds, nbBones);
-            var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds, nbBones);
-            var c = CreateGlobalVertex(unskinnedMesh, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds, nbBones);
+            var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
+            var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
+            var c = CreateGlobalVertex(unskinnedMesh, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
             indices.Add(a);
             indices.Add(b);
             indices.Add(c);
@@ -625,7 +625,7 @@ namespace Max2Babylon
         }
 
 
-        int CreateGlobalVertex(IIGameMesh mesh, IFaceEx face, int facePart, List<GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List<int> boneIds, int nbBones)
+        int CreateGlobalVertex(IIGameMesh mesh, IFaceEx face, int facePart, List<GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List<int> boneIds)
         {
             var vertexIndex = (int)face.Vert[facePart];
 
@@ -697,6 +697,7 @@ namespace Max2Babylon
                 int bone1 = bonesCount;
                 int bone2 = bonesCount;
                 int bone3 = bonesCount;
+                var nbBones = skin.GetNumberOfBones(vertexIndex);
 
                 if (nbBones > 0)
                 {

+ 8 - 2
Exporters/Blender/io_export_babylon.py

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

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

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

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

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

+ 25 - 23
Tools/Gulp/config.json

@@ -14,7 +14,7 @@
       "!../../src/**/*.d.ts",
       "../../external references/**/*.d.ts"
     ],
-   "files": [
+    "files": [
       "../../src/Math/babylon.math.js",
       "../../src/Tools/babylon.decorators.js",
       "../../src/Tools/babylon.observable.js",
@@ -43,18 +43,18 @@
       "../../src/Collisions/babylon.collider.js",
       "../../src/Collisions/babylon.collisionCoordinator.js",
       "../../src/Cameras/babylon.camera.js",
-      "../../src/Cameras/babylon.camerainputsmanager.js",
-      "../../src/Cameras/inputs/babylon.freecamera.input.mouse.js",
-      "../../src/Cameras/inputs/babylon.freecamera.input.keyboard.js",
-      "../../src/Cameras/inputs/babylon.freecamera.input.touch.js",
-      "../../src/Cameras/inputs/babylon.freecamera.input.deviceorientation.js",
-      "../../src/Cameras/inputs/babylon.freecamera.input.vrdeviceorientation.js",
-      "../../src/Cameras/inputs/babylon.freecamera.input.gamepad.js",
-      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.keyboard.js",
-      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.mousewheel.js",
-      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.pointers.js",
-      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.gamepad.js",
-      "../../src/Cameras/inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js",
+      "../../src/Cameras/babylon.cameraInputsManager.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.mouse.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.keyboard.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.touch.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.deviceorientation.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.gamepad.js",
+      "../../src/Cameras/Inputs/babylon.arcrotatecamera.input.keyboard.js",
+      "../../src/Cameras/Inputs/babylon.arcrotatecamera.input.mousewheel.js",
+      "../../src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js",
+      "../../src/Cameras/Inputs/babylon.arcrotatecamera.input.gamepad.js",
+      "../../src/Cameras/Inputs/babylon.arcrotatecamera.input.vrdeviceorientation.js",
       "../../src/Cameras/babylon.targetCamera.js",
       "../../src/Cameras/babylon.freeCamera.js",
       "../../src/Cameras/babylon.freeCameraInputsManager.js",
@@ -65,6 +65,7 @@
       "../../src/Rendering/babylon.renderingManager.js",
       "../../src/Rendering/babylon.renderingGroup.js",
       "../../src/babylon.scene.js",
+      "../../src/Mesh/babylon.buffer.js",
       "../../src/Mesh/babylon.vertexBuffer.js",
       "../../src/Mesh/babylon.instancedMesh.js",
       "../../src/Mesh/babylon.mesh.js",
@@ -171,7 +172,7 @@
       "../../src/Canvas2d/babylon.text2d.js",
       "../../src/Canvas2d/babylon.lines2d.js",
       "../../src/Canvas2d/babylon.canvas2d.js",
-      "../../src/Canvas2d/babylon.worldspacecanvas2d.js",
+      "../../src/Canvas2d/babylon.worldSpaceCanvas2d.js",
       "../../src/Materials/babylon.shaderMaterial.js",
       "../../src/Tools/babylon.tools.dds.js",
       "../../src/Physics/Plugins/babylon.cannonJSPlugin.js",
@@ -183,11 +184,11 @@
       "../../src/PostProcess/babylon.vrDistortionCorrectionPostProcess.js",
       "../../src/Tools/babylon.virtualJoystick.js",
       "../../src/Cameras/babylon.virtualJoysticksCamera.js",      
-      "../../src/cameras/inputs/babylon.freecamera.input.virtualjoystick.js",
+      "../../src/Cameras/Inputs/babylon.freecamera.input.virtualjoystick.js",
       "../../src/PostProcess/babylon.anaglyphPostProcess.js",
       "../../src/Rendering/babylon.outlineRenderer.js",
       "../../src/Tools/babylon.assetsManager.js",
-      "../../src/cameras/vr/babylon.vrCameraMetrics.js",
+      "../../src/Cameras/VR/babylon.vrCameraMetrics.js",
       "../../src/Cameras/VR/babylon.vrDeviceOrientationCamera.js",
       "../../src/Cameras/VR/babylon.webVRCamera.js",
       "../../src/Tools/babylon.sceneOptimizer.js",
@@ -222,14 +223,15 @@
       "../../src/Probes/babylon.reflectionProbe.js",
       "../../src/Particles/babylon.solidParticle.js",
       "../../src/Particles/babylon.solidParticleSystem.js",
-      "../../src/tools/hdr/babylon.tools.cubemaptosphericalpolynomial.js",
-      "../../src/tools/hdr/babylon.tools.panoramatocubemap.js",
-      "../../src/tools/hdr/babylon.tools.hdr.js",
-      "../../src/tools/hdr/babylon.tools.pmremGenerator.js",
-      "../../src/materials/textures/babylon.hdrcubetexture.js",
-      "../../src/debug/babylon.skeletonViewer.js",
+      "../../src/Tools/HDR/babylon.tools.cubemapToSphericalPolynomial.js",
+      "../../src/Tools/HDR/babylon.tools.panoramaToCubemap.js",
+      "../../src/Tools/HDR/babylon.tools.hdr.js",
+      "../../src/Tools/HDR/babylon.tools.pmremgenerator.js",
+      "../../src/Materials/Textures/babylon.hdrCubeTexture.js",
+      "../../src/Debug/babylon.skeletonViewer.js",
       "../../src/Materials/Textures/babylon.colorGradingTexture.js",
-      "../../src/materials/babylon.pbrmaterial.js",      
+      "../../src/Materials/babylon.colorCurves.js",
+      "../../src/Materials/babylon.pbrMaterial.js",      
       "../../src/Debug/babylon.debugLayer.js"
     ]
   }

+ 21 - 10
Tools/Gulp/gulpfile.js

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

+ 2 - 1
Tools/Gulp/package.json

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 25 - 25
dist/preview release/babylon.core.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 5121 - 4501
dist/preview release/babylon.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 38 - 37
dist/preview release/babylon.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2871 - 499
dist/preview release/babylon.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 38 - 37
dist/preview release/babylon.noworker.js


+ 29 - 12
dist/preview release/what's new.md

@@ -1,25 +1,28 @@
 - 2.4.0:
   - **Major updates**    
     - New refraction channel for Standard material (including fresnel support). Refraction texture can be provided by a reflection probe or a refraction texture. [See demo here](http://www.babylonjs.com/Demos/refraction/) ([deltakosh](https://github.com/deltakosh))
-    - Added support for HDR cubemaps ([sebavan](https://github.com/sebavan))
+    - Added support for HDR cubemaps. [demo here](http://www.babylonjs-playground.com/#19JGPR#4) ([sebavan](https://github.com/sebavan))
     - Support for shaders includes ([deltakosh](https://github.com/deltakosh))
-    - New mesh type : `LineSystem` ([jerome](https://github.com/jbousquie))
+    - New mesh type : `LineSystem`. [Demo here](http://www.babylonjs-playground.com/#2K1IS4#5) ([jerome](https://github.com/jbousquie))
     - SerializationHelper for complex classes using TypeScript decorators ([deltakosh](https://github.com/deltakosh))
     - StandardMaterial now supports Parallax and Parallax Occlusion Mapping ([tutorial](http://doc.babylonjs.com/tutorials/Using_parallax_mapping)) ([nockawa](https://github.com/nockawa))
     - Animations blending. See [demo here](http://www.babylonjs-playground.com/#2BLI9T#3). More [info here](http://doc.babylonjs.com/tutorials/Animations#animation-blending) ([deltakosh](https://github.com/deltakosh))
     - New debuger tool: SkeletonViewer. See [demo here](http://www.babylonjs-playground.com/#1BZJVJ#8) (Adam & [deltakosh](https://github.com/deltakosh))
-    - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance ([gleborgne](https://github.com/gleborgne))
-    - Introduced new observable system to handle events ([quick doc](http://www.html5gamedevs.com/topic/22655-big-brother-is-back-observable-everywhere/)) ([nockawa](https://github.com/nockawa), [deltakosh](https://github.com/deltakosh))
+    - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance. [Documentation here](http://doc.babylonjs.com/tutorials/Customizing_Camera_Inputs) ([gleborgne](https://github.com/gleborgne))
+    - Introduced new observable system to handle events. [Documentation here](http://doc.babylonjs.com/overviews/Observables) ([nockawa](https://github.com/nockawa), [deltakosh](https://github.com/deltakosh))
     - Added a new VR camera : VRDeviceOrientationArcRotateCamera ([temechon](https://github.com/Temechon))
-    - Unity3D exporter: Added support for lightmaps ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
-    - Unity3D exporter: Added support for export and run (local webserver) ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Moved PBR Material to core ([deltakosh](https://github.com/deltakosh))
-    - StandardMaterial.maxSimultaneousLights can define how many dynamic lights the material can handle ([deltakosh](https://github.com/deltakosh))
+    - StandardMaterial.maxSimultaneousLights can define how many dynamic lights the material can handle. [Demo here](http://www.babylonjs-playground.com/#IRVAX#10) ([deltakosh](https://github.com/deltakosh))
     - Introduced Canvas2D feature: a 2D engine to render primitives, sprites in 2D, text. Canvas2D can be displayed in Screen Space (above the 3D scene) or in World Space to be a part of the Scene. [overview](http://doc.babylonjs.com/overviews/Using_The_Canvas2D), [tutorial](http://doc.babylonjs.com/tutorials/Using_the_Canvas2D) ([nockawa](https://github.com/nockawa))
     - Added two new types of Texture: FontTexture and MapTexture ([quick doc](http://www.html5gamedevs.com/topic/22565-two-new-texture-types-fonttexture-and-maptexture/)) ([nockawa](https://github.com/nockawa))
     - Added a dynamic [2D Bin Packing Algorithm](http://stackoverflow.com/questions/8762569/how-is-2d-bin-packing-achieved-programmatically), ([more info here](http://www.html5gamedevs.com/topic/22565-two-new-texture-types-fonttexture-and-maptexture/)) ([nockawa](https://github.com/nockawa))
-    - Introduced Canvas2D feature: a 2D engine to render primitives, sprites in 2D, text. Canvas2D can be displayed in Screen Space (above the 3D scene) or in World Space to be a part of the Scene. [overview](http://doc.babylonjs.com/overviews/Using_The_Canvas2D), [tutorial](http://doc.babylonjs.com/tutorials/Using_the_Canvas2D) ([nockawa](https://github.com/nockawa))	
+    - Physics engine was completely rewritten, including both plugins for Oimo.js and Cannon.js. [overview](http://doc.babylonjs.com/overviews/Using_The_Physics_Engine) ([RaananW](https://github.com/RaananW))
+	- Interleaved buffers are now directly supported. Create a `Buffer` object and then use `buffer.createVertexBuffer` to specify the vertex buffers ([benaadams](https://github.com/benaadams)) 
+	- Vertex buffers can be marked as instanced to allow custom instancing attributes ([benaadams](https://github.com/benaadams)) 
+	- Mesh can have `overridenInstanceCount` set to specify the number of meshes to draw when custom instancing is used ([benaadams](https://github.com/benaadams)) 
   - **Updates**
+    - Added `renderTargetTexture.useCameraPostProcesses` to control postprocesses for render targets ([deltakosh](https://github.com/deltakosh))
+    - Added `mesh.toLefthanded()` to convert a mesh from right handed system ([kesshi](https://github.com/Kesshi))
     - Renderlists can now also be defined using predicates ([deltakosh](https://github.com/deltakosh))
     - Added support for various normal maps conventions ([deltakosh](https://github.com/deltakosh))
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
@@ -40,10 +43,17 @@
     - LinesMesh class now supports Intersection. Added the intersectionThreshold property to set a tolerance margin during intersection with wire lines. ([nockawa](https://github.com/nockawa))
     - Geometry.boundingBias property to enlarge the boundingInfo objects ([nockawa](https://github.com/nockawa))
     - Tools.ExtractMinAndMax & ExtractMinAndMaxIndexed now supports an optional Bias for Extent computation.
-	  - Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([quick doc](http://www.html5gamedevs.com/topic/22566-be-efficient-my-friend-use-stringdictionary/)) ([nockawa](https://github.com/nockawa))
-	  - Added RectanglePackingMap class to fit several rectangles in a big map in the most optimal way, dynamically. ([nockawa](https://github.com/nockawa))
-	  - Added DynamicFloatArray class to store float32 based elements of a given size (stride) into one big Float32Array, with allocation/free/pack operations to then access an optimal buffer that can be used to update a WebGLBuffer dynamically.([quick doc](http://www.html5gamedevs.com/topic/22567-dynamicfloatarray-to-the-rescue-for-efficient-instanced-array/)) ([nockawa](https://github.com/nockawa))
-	  - Scene.onPointerObservable property added to enable a unique Observable event for user input (see ArcRotateCamera inputs for examples) ([nockawa](https://github.com/nockawa))
+    - Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([quick doc](http://www.html5gamedevs.com/topic/22566-be-efficient-my-friend-use-stringdictionary/)) ([nockawa](https://github.com/nockawa))
+    - Added RectanglePackingMap class to fit several rectangles in a big map in the most optimal way, dynamically. ([nockawa](https://github.com/nockawa))
+    - Added DynamicFloatArray class to store float32 based elements of a given size (stride) into one big Float32Array, with allocation/free/pack operations to then access an optimal buffer that can be used to update a WebGLBuffer dynamically.([quick doc](http://www.html5gamedevs.com/topic/22567-dynamicfloatarray-to-the-rescue-for-efficient-instanced-array/)) ([nockawa](https://github.com/nockawa))
+    - Scene.onPointerObservable property added to enable a unique Observable event for user input (see ArcRotateCamera inputs for examples) ([nockawa](https://github.com/nockawa))
+    - Oimo.js updated to the latest version ([RaananW](https://github.com/RaananW))
+    - Added PhysicsImpostor and PhysicsJoint classes  ([RaananW](https://github.com/RaananW))
+    - LensFlareSystem now has both ID and name  ([RaananW](https://github.com/RaananW))
+    - TargetCamera has now a rotationQuaternion variable to can be used to set the camera's rotation  ([RaananW](https://github.com/RaananW))
+    - SSAORenderingPipeline now uses bilateral blur post-processes instead of standard blur post-process, in order to remove more efficiently the "textile effect"
+    - `Engine.updateDynamicVertexBuffer` now has optional count as well as offset to allow partial updates ([benaadams](https://github.com/benaadams)) 
+    - vertex attributes are only disabled if they aren't going to be reeabled by the next draw, to reduce gpu state changes ([benaadams](https://github.com/benaadams)) 
   - **Exporters**
     - Unity3D exporter: Added support for lightmaps ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Unity3D exporter: Added support for export and run (local webserver) ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
@@ -59,7 +69,14 @@
     - Fixed bug with billboards and parenting ([deltakosh](https://github.com/deltakosh))
     - Fixed bug with ArcRotateCamera.setTarget ([deltakosh](https://github.com/deltakosh))
     - Fixed bug with OBJ Loader - All meshes were concatenated with the previous one ([Temechon](https://github.com/Temechon))
+    - Fixed the device orientation cameras (both VR and non-VR cameras)  ([RaananW](https://github.com/RaananW))
+    - Fixed the WebVR implementation  ([RaananW](https://github.com/RaananW))
   - **Breaking changes**
     - `VertexData.CreateLines()` removed as `MeshBuilder.CreateLines()` now calls `MeshBuilder.CreateLineSystem()`
     - `scene.onNewXXXAdded` and `scene.onXXXRemoved` callbacks were removed and replaced by `scene.onNewXXXAddedObservable` and `scene.onXXXRemovedObservable`
     - `Material.dispose` does not dispose textures by default. You have to call `material.dispose(false, true)` to get the previous behavior.
+    - `SSAORenderingPipeline.getBlurHPostProcess` and `SSAORenderingPipeline.getBlurVPostProcess`. The SSAO rendering pipeline doesn't use standard blur post-process anymore. A bilateral blur post-process is used instead.
+    - `Engine.bindBuffers` is now `Engine.bindBuffersDirectly` ([benaadams](https://github.com/benaadams))
+    - `Engine.bindMultiBuffers` is now `Engine.bindBuffers` and strongly typed `{ [key: string]: VertexBuffer; }` of buffers ([benaadams](https://github.com/benaadams))
+    - `Engine.createDynamicVertexBuffer` takes vertices rather than capacity, creating and initalizing in one gpu instruction ([benaadams](https://github.com/benaadams)) 
+    - Internally new `Engine.bindBuffer` is used rather than `gl.bindBuffer` which only binds when the bound buffer is changing ([benaadams](https://github.com/benaadams)) 

+ 4 - 0
src/Bones/babylon.skeleton.js

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

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

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

+ 10 - 7
src/Cameras/VR/babylon.webVRCamera.js

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

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

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

+ 3 - 0
src/Cameras/babylon.camera.js

@@ -247,6 +247,9 @@ var BABYLON;
                     cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
                     rigPostProcess.markTextureDirty();
                 }
+                else {
+                    cam._postProcesses = this._postProcesses.slice(0);
+                }
             }
         };
         Camera.prototype.attachPostProcess = function (postProcess, insertAt) {

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

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

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

@@ -36,64 +36,62 @@ var BABYLON;
             this._mapCounter = 0;
         }
         /**
-         * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the top/left corner of the screen.
+         * Create a new 2D ScreenSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a position relative to the bottom/left corner of the screen.
          * ScreenSpace Canvas will be drawn in the Viewport as a 2D Layer lying to the top of the 3D Scene. Typically used for traditional UI.
          * All caching strategies will be available.
          * PLEASE NOTE: the origin of a Screen Space Canvas is set to [0;0] (bottom/left) which is different than the default origin of a Primitive which is centered [0.5;0.5]
          * @param scene the Scene that owns the Canvas
-         * @param name the name of the Canvas, for information purpose only
-         * @param pos the position of the canvas, relative from the bottom/left of the scene's viewport
-         * @param size the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
-         * @param cachingStrategy either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information.
+         * Options:
+         *  - id: a text identifier, for information purpose only
+         *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport
+         *  - size: the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
+         *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS
+         *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property.
          */
-        Canvas2D.CreateScreenSpace = function (scene, name, pos, size, cachingStrategy, enableInteraction) {
-            if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
-            if (enableInteraction === void 0) { enableInteraction = true; }
+        Canvas2D.CreateScreenSpace = function (scene, options) {
             var c = new Canvas2D();
-            c.setupCanvas(scene, name, size, true, cachingStrategy, enableInteraction);
-            c.position = pos;
-            c.origin = BABYLON.Vector2.Zero();
+            c.setupCanvas(scene, options && options.id || null, options && options.size || null, true, options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, options && options.enableInteraction || true);
+            c.position = options && options.pos || BABYLON.Vector2.Zero();
+            c.origin = options && options.origin || BABYLON.Vector2.Zero();
             return c;
         };
         /**
          * Create a new 2D WorldSpace Rendering Canvas, it is a 2D rectangle that has a size (width/height) and a world transformation information to place it in the world space.
          * This kind of canvas can't have its Primitives directly drawn in the Viewport, they need to be cached in a bitmap at some point, as a consequence the DONT_CACHE strategy is unavailable. For now only CACHESTRATEGY_CANVAS is supported, but the remaining strategies will be soon.
          * @param scene the Scene that owns the Canvas
-         * @param name the name of the Canvas, for information purpose only
-         * @param position the position of the Canvas in World Space
-         * @param rotation the rotation of the Canvas in World Space
          * @param size the dimension of the Canvas in World Space
-         * @param renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality.
+         * Options:
+         *  - id: a text identifier, for information purpose only, default is null.
+         *  - position the position of the Canvas in World Space, default is [0,0,0]
+         *  - rotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         *  - renderScaleFactor A scale factor applied to create the rendering texture that will be mapped in the Scene Rectangle. If you set 2 for instance the texture will be twice large in width and height. A greater value will allow to achieve a better rendering quality. Default value is 1.
          * BE AWARE that the Canvas true dimension will be size*renderScaleFactor, then all coordinates and size will have to be express regarding this size.
          * TIPS: if you want a renderScaleFactor independent reference of frame, create a child Group2D in the Canvas with position 0,0 and size set to null, then set its scale property to the same amount than the renderScaleFactor, put all your primitive inside using coordinates regarding the size property you pick for the Canvas and you'll be fine.
-         * @param sideOrientation Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one.
-         * @param cachingStrategy Must be CACHESTRATEGY_CANVAS for now
+         * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
+         * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
          */
-        Canvas2D.CreateWorldSpace = function (scene, name, position, rotation, size, renderScaleFactor, sideOrientation, cachingStrategy, enableInteraction) {
-            if (renderScaleFactor === void 0) { renderScaleFactor = 1; }
-            if (cachingStrategy === void 0) { cachingStrategy = Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS; }
-            if (enableInteraction === void 0) { enableInteraction = true; }
-            if (cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
+        Canvas2D.CreateWorldSpace = function (scene, size, options) {
+            var cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
+            if (cs !== Canvas2D.CACHESTRATEGY_CANVAS) {
                 throw new Error("Right now only the CACHESTRATEGY_CANVAS cache Strategy is supported for WorldSpace Canvas. More will come soon!");
             }
             //if (cachingStrategy === Canvas2D.CACHESTRATEGY_DONTCACHE) {
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
-            if (!sideOrientation) {
-                sideOrientation = BABYLON.Mesh.DEFAULTSIDE;
-            }
+            var id = options && options.id || null;
+            var rsf = options && options.renderScaleFactor || 1;
             var c = new Canvas2D();
-            c.setupCanvas(scene, name, new BABYLON.Size(size.width * renderScaleFactor, size.height * renderScaleFactor), false, cachingStrategy, enableInteraction);
-            var plane = new BABYLON.WorldSpaceCanvas2d(name, scene, c);
-            var vertexData = BABYLON.VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: sideOrientation });
-            var mtl = new BABYLON.StandardMaterial(name + "_Material", scene);
+            c.setupCanvas(scene, id, new BABYLON.Size(size.width * rsf, size.height * rsf), false, cs, options && options.enableInteraction || true);
+            var plane = new BABYLON.WorldSpaceCanvas2D(id, scene, c);
+            var vertexData = BABYLON.VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: options && options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE });
+            var mtl = new BABYLON.StandardMaterial(id + "_Material", scene);
             c.applyCachedTexture(vertexData, mtl);
             vertexData.applyToMesh(plane, false);
             mtl.specularColor = new BABYLON.Color3(0, 0, 0);
             mtl.disableLighting = true;
             mtl.useAlphaFromDiffuseTexture = true;
-            plane.position = position;
-            plane.rotationQuaternion = rotation;
+            plane.position = options && options.position || BABYLON.Vector3.Zero();
+            plane.rotationQuaternion = options && options.rotation || BABYLON.Quaternion.Identity();
             plane.material = mtl;
             c._worldSpaceNode = plane;
             return c;
@@ -110,7 +108,7 @@ var BABYLON;
             this._primPointerInfo = new BABYLON.PrimitivePointerInfo();
             this._capturedPointers = new BABYLON.StringDictionary();
             this._pickStartingPosition = BABYLON.Vector2.Zero();
-            this.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), size, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
+            this.setupGroup2D(this, null, name, BABYLON.Vector2.Zero(), null, size, this._cachingStrategy === Canvas2D.CACHESTRATEGY_ALLGROUPS ? BABYLON.Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : BABYLON.Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
             this._hierarchyLevelMaxSiblingCount = 100;
             this._hierarchyDepthOffset = 0;
             this._siblingDepthOffset = 1 / this._hierarchyLevelMaxSiblingCount;
@@ -122,7 +120,7 @@ var BABYLON;
                 _this.dispose();
             });
             if (cachingstrategy !== Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) {
-                this._background = BABYLON.Rectangle2D.Create(this, "###CANVAS BACKGROUND###", 0, 0, size.width, size.height);
+                this._background = BABYLON.Rectangle2D.Create(this, { id: "###CANVAS BACKGROUND###", width: size.width, height: size.height });
                 this._background.isPickable = false;
                 this._background.origin = BABYLON.Vector2.Zero();
                 this._background.levelVisible = false;
@@ -222,6 +220,10 @@ var BABYLON;
             return this._capturedPointers.get(pointerId.toString());
         };
         Canvas2D.prototype._handlePointerEventForInteraction = function (eventData, eventState) {
+            // Dispose check
+            if (this.isDisposed) {
+                return;
+            }
             // Update the this._primPointerInfo structure we'll send to observers using the PointerEvent data
             this._updatePointerInfo(eventData);
             var capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
@@ -257,8 +259,15 @@ var BABYLON;
             if (!pii.canvasPointerPos) {
                 pii.canvasPointerPos = BABYLON.Vector2.Zero();
             }
-            pii.canvasPointerPos.x = eventData.localPosition.x - this.position.x;
-            pii.canvasPointerPos.y = (this.engine.getRenderHeight() - eventData.localPosition.y) - this.position.y;
+            var camera = this._scene.activeCamera;
+            var engine = this._scene.getEngine();
+            var cameraViewport = camera.viewport;
+            var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+            // Moving coordinates to local viewport world
+            var x = eventData.localPosition.x / engine.getHardwareScalingLevel() - viewport.x;
+            var y = eventData.localPosition.y / engine.getHardwareScalingLevel() - viewport.y;
+            pii.canvasPointerPos.x = x - this.position.x;
+            pii.canvasPointerPos.y = engine.getRenderHeight() - y - this.position.y;
             pii.mouseWheelDelta = 0;
             if (eventData.type === BABYLON.PointerEventTypes.POINTERWHEEL) {
                 var event = eventData.event;
@@ -668,6 +677,9 @@ var BABYLON;
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
             }
         };
+        Canvas2D.prototype.onPrimBecomesDirty = function () {
+            this._addPrimToDirtyList(this);
+        };
         Canvas2D.prototype._updateCanvasState = function () {
             // Check if the update has already been made for this render Frame
             if (this.scene.getRenderId() === this._updateRenderId) {
@@ -752,12 +764,12 @@ var BABYLON;
                 // Special case if the canvas is entirely cached: create a group that will have a single sprite it will be rendered specifically at the very end of the rendering process
                 if (this._cachingStrategy === Canvas2D.CACHESTRATEGY_CANVAS) {
                     this._cachedCanvasGroup = BABYLON.Group2D._createCachedCanvasGroup(this);
-                    var sprite = BABYLON.Sprite2D.Create(this._cachedCanvasGroup, "__cachedCanvasSprite__", 0, 0, map, node.contentSize, node.pos);
+                    var sprite = BABYLON.Sprite2D.Create(this._cachedCanvasGroup, map, { id: "__cachedCanvasSprite__", spriteSize: node.contentSize, spriteLocation: node.pos });
                     sprite.zOrder = 1;
                     sprite.origin = BABYLON.Vector2.Zero();
                 }
                 else {
-                    var sprite = BABYLON.Sprite2D.Create(parent, "__cachedSpriteOfGroup__" + group.id, group.position.x, group.position.y, map, node.contentSize, node.pos, false);
+                    var sprite = BABYLON.Sprite2D.Create(parent, map, { id: "__cachedSpriteOfGroup__" + group.id, x: group.position.x, y: group.position.y, spriteSize: node.contentSize, spriteLocation: node.pos });
                     sprite.origin = group.origin.clone();
                     res.sprite = sprite;
                 }

+ 12 - 2
src/Canvas2d/babylon.canvas2d.ts

@@ -321,8 +321,18 @@
             if (!pii.canvasPointerPos) {
                 pii.canvasPointerPos = Vector2.Zero();
             }
-            pii.canvasPointerPos.x = eventData.localPosition.x - this.position.x;
-            pii.canvasPointerPos.y = (this.engine.getRenderHeight() - eventData.localPosition.y) - this.position.y;
+            var camera = this._scene.activeCamera;
+            var engine = this._scene.getEngine();
+
+            var cameraViewport = camera.viewport;
+            var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+
+            // Moving coordinates to local viewport world
+            var x = eventData.localPosition.x / engine.getHardwareScalingLevel() - viewport.x;
+            var y = eventData.localPosition.y / engine.getHardwareScalingLevel() - viewport.y;
+
+            pii.canvasPointerPos.x = x - this.position.x;
+            pii.canvasPointerPos.y = engine.getRenderHeight() -y - this.position.y;
             pii.mouseWheelDelta = 0;
 
             if (eventData.type === PointerEventTypes.POINTERWHEEL) {

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

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

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

@@ -50,7 +50,7 @@
                 let effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
 
                 engine.enableEffect(effect);
-                engine.bindBuffers(this.fillVB, this.fillIB, [1], 4, effect);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, effect);
                 if (context.useInstancing) {
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
@@ -78,7 +78,7 @@
                 let effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
 
                 engine.enableEffect(effect);
-                engine.bindBuffers(this.borderVB, this.borderIB, [1], 4, effect);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, effect);
                 if (context.useInstancing) {
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);

+ 17 - 7
src/Canvas2d/babylon.group2d.js

@@ -22,16 +22,25 @@ var BABYLON;
             this._childrenRenderableGroups = new Array();
             this._renderGroupInstancesInfo = new BABYLON.StringDictionary();
         }
-        Group2D.CreateGroup2D = function (parent, id, position, size, cacheBehabior) {
-            if (cacheBehabior === void 0) { cacheBehabior = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY; }
+        /**
+         * Create an Logical or Renderable Group.
+         * @param parent the parent primitive, must be a valid primitive (or the Canvas)
+         * options:
+         *  - id a text identifier, for information purpose
+         *  - position: the X & Y positions relative to its parent, default is [0;0]
+         *  - origin: define the normalized origin point location, default [0.5;0.5]
+         *  - size: the size of the group, if null the size will be computed from its content, default is null.
+         *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
+         */
+        Group2D.CreateGroup2D = function (parent, options) {
             BABYLON.Prim2DBase.CheckParent(parent);
             var g = new Group2D();
-            g.setupGroup2D(parent.owner, parent, id, position, size, cacheBehabior);
+            g.setupGroup2D(parent.owner, parent, options && options.id || null, options && options.position || BABYLON.Vector2.Zero(), options && options.origin || null, options && options.size || null, options && options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
             return g;
         };
         Group2D._createCachedCanvasGroup = function (owner) {
             var g = new Group2D();
-            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero());
+            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", BABYLON.Vector2.Zero(), null);
             g.origin = BABYLON.Vector2.Zero();
             return g;
         };
@@ -76,10 +85,10 @@ var BABYLON;
             }
             return true;
         };
-        Group2D.prototype.setupGroup2D = function (owner, parent, id, position, size, cacheBehavior) {
+        Group2D.prototype.setupGroup2D = function (owner, parent, id, position, origin, size, cacheBehavior) {
             if (cacheBehavior === void 0) { cacheBehavior = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY; }
             this._cacheBehavior = cacheBehavior;
-            this.setupPrim2DBase(owner, parent, id, position);
+            this.setupPrim2DBase(owner, parent, id, position, origin);
             this.size = size;
             this._viewportPosition = BABYLON.Vector2.Zero();
         };
@@ -247,6 +256,7 @@ var BABYLON;
                         }
                     });
                     // Everything is updated, clear the dirty list
+                    this._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
                     this._primDirtyList.splice(0);
                 }
             }
@@ -300,9 +310,9 @@ var BABYLON;
                                 // Update the WebGL buffer to match the new content of the instances data
                                 engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
                                 engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData_1);
-                                v._dirtyInstancesData = false;
                             }
                         }
+                        v._dirtyInstancesData = false;
                     }
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
                     if (!_this.owner.supportInstancedArray || totalRenderCount > 0) {

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

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

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

@@ -50,7 +50,7 @@
                 let effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
 
                 engine.enableEffect(effect);
-                engine.bindBuffers(this.fillVB, this.fillIB, [2], 2*4, effect);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [2], 2*4, effect);
                 if (context.useInstancing) {
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
@@ -78,7 +78,7 @@
                 let effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
 
                 engine.enableEffect(effect);
-                engine.bindBuffers(this.borderVB, this.borderIB, [2], 2 * 4, effect);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [2], 2 * 4, effect);
                 if (context.useInstancing) {
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);

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

@@ -230,7 +230,7 @@ var BABYLON;
         function Prim2DBase() {
             _super.apply(this, arguments);
         }
-        Prim2DBase.prototype.setupPrim2DBase = function (owner, parent, id, position, isVisible) {
+        Prim2DBase.prototype.setupPrim2DBase = function (owner, parent, id, position, origin, isVisible) {
             if (isVisible === void 0) { isVisible = true; }
             if (!(this instanceof BABYLON.Group2D) && !(this instanceof BABYLON.Sprite2D && id !== null && id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
                 throw new Error("Can't create a primitive with the canvas as direct parent when the caching strategy is TOPLEVELGROUPS. You need to create a Group below the canvas and use it as the parent for the primitive");
@@ -265,7 +265,7 @@ var BABYLON;
             this.rotation = 0;
             this.scale = 1;
             this.levelVisible = isVisible;
-            this.origin = new BABYLON.Vector2(0.5, 0.5);
+            this.origin = origin || new BABYLON.Vector2(0.5, 0.5);
         };
         Object.defineProperty(Prim2DBase.prototype, "actionManager", {
             get: function () {

+ 31 - 19
src/Canvas2d/babylon.rectangle2d.js

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

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

@@ -51,7 +51,7 @@
                 let effect = context.useInstancing ? this.effectFillInstanced : this.effectFill;
 
                 engine.enableEffect(effect);
-                engine.bindBuffers(this.fillVB, this.fillIB, [1], 4, effect);
+                engine.bindBuffersDirectly(this.fillVB, this.fillIB, [1], 4, effect);
                 if (context.useInstancing) {
                     if (!this.instancingFillAttributes) {
                         this.instancingFillAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_FILLPARTID, effect);
@@ -79,7 +79,7 @@
                 let effect = context.useInstancing ? this.effectBorderInstanced : this.effectBorder;
 
                 engine.enableEffect(effect);
-                engine.bindBuffers(this.borderVB, this.borderIB, [1], 4, effect);
+                engine.bindBuffersDirectly(this.borderVB, this.borderIB, [1], 4, effect);
                 if (context.useInstancing) {
                     if (!this.instancingBorderAttributes) {
                         this.instancingBorderAttributes = this.loadInstancingAttributes(Shape2D.SHAPE2D_BORDERPARTID, effect);

+ 42 - 6
src/Canvas2d/babylon.renderablePrim2d.js

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

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

@@ -48,9 +48,9 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Shape2D.prototype.setupShape2D = function (owner, parent, id, position, isVisible, fill, border, borderThickness) {
+        Shape2D.prototype.setupShape2D = function (owner, parent, id, position, origin, isVisible, fill, border, borderThickness) {
             if (borderThickness === void 0) { borderThickness = 1.0; }
-            this.setupRenderablePrim2D(owner, parent, id, position, isVisible);
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible);
             this.border = border;
             this.fill = fill;
             this.borderThickness = borderThickness;

+ 13 - 0
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -190,6 +190,16 @@ var BABYLON;
                 propDic.forEach(function (k, v) {
                     if (v.kind === Prim2DPropInfo.PROPKIND_MODEL) {
                         var propVal = _this[v.name];
+                        // Special case, array, this WON'T WORK IN ALL CASES, all entries have to be of the same type and it must be a BJS well known one
+                        if (propVal && propVal.constructor === Array) {
+                            var firstVal = propVal[0];
+                            if (!firstVal) {
+                                propVal = 0;
+                            }
+                            else {
+                                propVal = BABYLON.Tools.hashCodeFromStream(BABYLON.Tools.arrayOrStringFeeder(propVal));
+                            }
+                        }
                         modelKey += v.name + ":" + ((propVal != null) ? ((v.typeLevelCompare) ? BABYLON.Tools.getClassName(propVal) : propVal.toString()) : "[null]") + ";";
                     }
                 });
@@ -350,6 +360,9 @@ var BABYLON;
             this._instanceDirtyFlags &= ~flags;
             return this._instanceDirtyFlags;
         };
+        SmartPropertyPrim.prototype._resetPropertiesDirty = function () {
+            this._instanceDirtyFlags = 0;
+        };
         Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
             /**
              * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children

+ 27 - 11
src/Canvas2d/babylon.sprite2d.js

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

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

@@ -24,7 +24,7 @@
 
             engine.enableEffect(effect);
             effect.setTexture("diffuseSampler", this.texture);
-            engine.bindBuffers(this.vb, this.ib, [1], 4, effect);
+            engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
 
             var cur = engine.getAlphaMode();
 

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 22 - 9
src/Canvas2d/babylon.text2d.js


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

@@ -25,7 +25,7 @@
 
             engine.enableEffect(effect);
             effect.setTexture("diffuseSampler", this.fontTexture);
-            engine.bindBuffers(this.vb, this.ib, [1], 4, effect);
+            engine.bindBuffersDirectly(this.vb, this.ib, [1], 4, effect);
 
             var curAlphaMode = engine.getAlphaMode();
 

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

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

+ 12 - 10
src/Layer/babylon.layer.js

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

+ 15 - 13
src/Layer/babylon.layer.ts

@@ -9,9 +9,7 @@
         public alphaTest: boolean;
 
         private _scene: Scene;
-        private _vertexDeclaration = [2];
-        private _vertexStrideSize = 2 * 4;
-        private _vertexBuffer: WebGLBuffer;
+        private _vertexBuffers: { [key: string]: VertexBuffer } = {};
         private _indexBuffer: WebGLBuffer;
         private _effect: Effect;
         private _alphaTestEffect: Effect;
@@ -69,6 +67,8 @@
             this._scene = scene;
             this._scene.layers.push(this);
 
+            var engine = scene.getEngine();
+
             // VBO
             var vertices = [];
             vertices.push(1, 1);
@@ -76,7 +76,8 @@
             vertices.push(-1, -1);
             vertices.push(1, -1);
 
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            var vertexBuffer = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
+            this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
 
             // Indices
             var indices = [];
@@ -88,16 +89,16 @@
             indices.push(2);
             indices.push(3);
 
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = engine.createIndexBuffer(indices);
 
             // Effects
-            this._effect = this._scene.getEngine().createEffect("layer",
-                ["position"],
+            this._effect = engine.createEffect("layer",
+                [VertexBuffer.PositionKind],
                 ["textureMatrix", "color", "scale", "offset"],
                 ["textureSampler"], "");
 
-            this._alphaTestEffect = this._scene.getEngine().createEffect("layer",
-                ["position"],
+            this._alphaTestEffect = engine.createEffect("layer",
+                [VertexBuffer.PositionKind],
                 ["textureMatrix", "color", "scale", "offset"],
                 ["textureSampler"], "#define ALPHATEST");
         }
@@ -130,7 +131,7 @@
             currentEffect.setVector2("scale", this.scale);
 
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, currentEffect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
 
             // Draw order
             if (!this._alphaTestEffect) {
@@ -146,9 +147,10 @@
         }
 
         public dispose(): void {
-            if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
-                this._vertexBuffer = null;
+            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
             }
 
             if (this._indexBuffer) {

+ 10 - 9
src/LensFlare/babylon.lensFlareSystem.js

@@ -6,21 +6,21 @@ var BABYLON;
             this.lensFlares = new Array();
             this.borderLimit = 300;
             this.layerMask = 0x0FFFFFFF;
-            this._vertexDeclaration = [2];
-            this._vertexStrideSize = 2 * 4;
+            this._vertexBuffers = {};
             this._isEnabled = true;
             this._scene = scene;
             this._emitter = emitter;
             this.id = name;
             scene.lensFlareSystems.push(this);
             this.meshesSelectionPredicate = function (m) { return m.material && m.isVisible && m.isEnabled() && m.isBlocker && ((m.layerMask & scene.activeCamera.layerMask) != 0); };
+            var engine = scene.getEngine();
             // VBO
             var vertices = [];
             vertices.push(1, 1);
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = new BABYLON.VertexBuffer(engine, vertices, BABYLON.VertexBuffer.PositionKind, false, false, 2);
             // Indices
             var indices = [];
             indices.push(0);
@@ -29,9 +29,9 @@ var BABYLON;
             indices.push(0);
             indices.push(2);
             indices.push(3);
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = engine.createIndexBuffer(indices);
             // Effects
-            this._effect = this._scene.getEngine().createEffect("lensFlare", ["position"], ["color", "viewportMatrix"], ["textureSampler"], "");
+            this._effect = engine.createEffect("lensFlare", [BABYLON.VertexBuffer.PositionKind], ["color", "viewportMatrix"], ["textureSampler"], "");
         }
         Object.defineProperty(LensFlareSystem.prototype, "isEnabled", {
             get: function () {
@@ -138,7 +138,7 @@ var BABYLON;
             engine.setDepthBuffer(false);
             engine.setAlphaMode(BABYLON.Engine.ALPHA_ONEONE);
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
             // Flares
             for (var index = 0; index < this.lensFlares.length; index++) {
                 var flare = this.lensFlares[index];
@@ -162,9 +162,10 @@ var BABYLON;
             return true;
         };
         LensFlareSystem.prototype.dispose = function () {
-            if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
-                this._vertexBuffer = null;
+            var vertexBuffer = this._vertexBuffers[BABYLON.VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = null;
             }
             if (this._indexBuffer) {
                 this._scene.getEngine()._releaseBuffer(this._indexBuffer);

+ 12 - 11
src/LensFlare/babylon.lensFlareSystem.ts

@@ -8,9 +8,7 @@
 
         private _scene: Scene;
         private _emitter: any;
-        private _vertexDeclaration = [2];
-        private _vertexStrideSize = 2 * 4;
-        private _vertexBuffer: WebGLBuffer;
+        private _vertexBuffers: { [key: string]: VertexBuffer } = {};
         private _indexBuffer: WebGLBuffer;
         private _effect: Effect;
         private _positionX: number;
@@ -26,6 +24,8 @@
 
             this.meshesSelectionPredicate = m => m.material && m.isVisible && m.isEnabled() && m.isBlocker && ((m.layerMask & scene.activeCamera.layerMask) != 0);
 
+            var engine = scene.getEngine();
+
             // VBO
             var vertices = [];
             vertices.push(1, 1);
@@ -33,7 +33,7 @@
             vertices.push(-1, -1);
             vertices.push(1, -1);
 
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
 
             // Indices
             var indices = [];
@@ -45,11 +45,11 @@
             indices.push(2);
             indices.push(3);
 
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = engine.createIndexBuffer(indices);
 
             // Effects
-            this._effect = this._scene.getEngine().createEffect("lensFlare",
-                ["position"],
+            this._effect = engine.createEffect("lensFlare",
+                [VertexBuffer.PositionKind],
                 ["color", "viewportMatrix"],
                 ["textureSampler"], "");
         }
@@ -179,7 +179,7 @@
             engine.setAlphaMode(Engine.ALPHA_ONEONE);
 
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
 
             // Flares
             for (var index = 0; index < this.lensFlares.length; index++) {
@@ -217,9 +217,10 @@
         }
 
         public dispose(): void {
-            if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
-                this._vertexBuffer = null;
+            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
             }
 
             if (this._indexBuffer) {

+ 16 - 8
src/Materials/Textures/Procedurals/babylon.proceduralTexture.js

@@ -15,8 +15,7 @@ var BABYLON;
             this.isEnabled = true;
             this._currentRefreshId = -1;
             this._refreshRate = 1;
-            this._vertexDeclaration = [2];
-            this._vertexStrideSize = 2 * 4;
+            this._vertexBuffers = {};
             this._uniforms = new Array();
             this._samplers = new Array();
             this._textures = new Array();
@@ -35,12 +34,13 @@ var BABYLON;
             this._generateMipMaps = generateMipMaps;
             this.setFragment(fragment);
             this._fallbackTexture = fallbackTexture;
+            var engine = scene.getEngine();
             if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
+                this._texture = engine.createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
                 this.setFloat("face", 0);
             }
             else {
-                this._texture = scene.getEngine().createRenderTargetTexture(size, generateMipMaps);
+                this._texture = engine.createRenderTargetTexture(size, generateMipMaps);
             }
             // VBO
             var vertices = [];
@@ -48,7 +48,7 @@ var BABYLON;
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = new BABYLON.VertexBuffer(engine, vertices, BABYLON.VertexBuffer.PositionKind, false, false, 2);
             // Indices
             var indices = [];
             indices.push(0);
@@ -57,7 +57,7 @@ var BABYLON;
             indices.push(0);
             indices.push(2);
             indices.push(3);
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = engine.createIndexBuffer(indices);
         }
         ProceduralTexture.prototype.reset = function () {
             if (this._effect === undefined) {
@@ -82,7 +82,7 @@ var BABYLON;
             else {
                 shaders = { vertex: "procedural", fragment: this._fragment };
             }
-            this._effect = engine.createEffect(shaders, ["position"], this._uniforms, this._samplers, "", null, null, function () {
+            this._effect = engine.createEffect(shaders, [BABYLON.VertexBuffer.PositionKind], this._uniforms, this._samplers, "", null, null, function () {
                 _this.releaseInternalTexture();
                 if (_this._fallbackTexture) {
                     _this._texture = _this._fallbackTexture._texture;
@@ -225,7 +225,7 @@ var BABYLON;
                 this._effect.setMatrix(name, this._matrices[name]);
             }
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
             if (this.isCube) {
                 for (var face = 0; face < 6; face++) {
                     engine.bindFramebuffer(this._texture, face);
@@ -268,6 +268,14 @@ var BABYLON;
             if (index >= 0) {
                 this.getScene()._proceduralTextures.splice(index, 1);
             }
+            var vertexBuffer = this._vertexBuffers[BABYLON.VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = null;
+            }
+            if (this._indexBuffer && this.getScene().getEngine()._releaseBuffer(this._indexBuffer)) {
+                this._indexBuffer = null;
+            }
             _super.prototype.dispose.call(this);
         };
         return ProceduralTexture;

+ 21 - 11
src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts

@@ -9,13 +9,10 @@
 
         public onGenerated: () => void;
 
-        private _vertexBuffer: WebGLBuffer;
+        private _vertexBuffers: { [key: string]: VertexBuffer } = {};
         private _indexBuffer: WebGLBuffer;
         private _effect: Effect;
 
-        private _vertexDeclaration = [2];
-        private _vertexStrideSize = 2 * 4;
-
         private _uniforms = new Array<string>();
         private _samplers = new Array<string>();
         private _fragment: any;
@@ -47,12 +44,14 @@
 
             this._fallbackTexture = fallbackTexture;
 
+            var engine = scene.getEngine();
+
             if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
+                this._texture = engine.createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
                 this.setFloat("face", 0);
             }
             else {
-                this._texture = scene.getEngine().createRenderTargetTexture(size, generateMipMaps);
+                this._texture = engine.createRenderTargetTexture(size, generateMipMaps);
             }
 
             // VBO
@@ -62,7 +61,7 @@
             vertices.push(-1, -1);
             vertices.push(1, -1);
 
-            this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
 
             // Indices
             var indices = [];
@@ -74,7 +73,7 @@
             indices.push(2);
             indices.push(3);
 
-            this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+            this._indexBuffer = engine.createIndexBuffer(indices);
         }
 
         public reset(): void {
@@ -106,7 +105,7 @@
             }
 
             this._effect = engine.createEffect(shaders,
-                ["position"],
+                [VertexBuffer.PositionKind],
                 this._uniforms,
                 this._samplers,
                 "", null, null, () => {
@@ -288,10 +287,10 @@
             // Matrix      
             for (name in this._matrices) {
                 this._effect.setMatrix(name, this._matrices[name]);
-            }
+            }            
 
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._effect);
 
             if (this.isCube) {
                 for (var face = 0; face < 6; face++) {
@@ -348,6 +347,17 @@
             if (index >= 0) {
                 this.getScene()._proceduralTextures.splice(index, 1);
             }
+
+            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
+            }
+
+            if (this._indexBuffer && this.getScene().getEngine()._releaseBuffer(this._indexBuffer)) {
+                this._indexBuffer = null;
+            }
+
             super.dispose();
         }
     }

+ 11 - 3
src/Materials/Textures/babylon.renderTargetTexture.js

@@ -7,10 +7,11 @@ var BABYLON;
 (function (BABYLON) {
     var RenderTargetTexture = (function (_super) {
         __extends(RenderTargetTexture, _super);
-        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube) {
+        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode) {
             if (doNotChangeAspectRatio === void 0) { doNotChangeAspectRatio = true; }
             if (type === void 0) { type = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT; }
             if (isCube === void 0) { isCube = false; }
+            if (samplingMode === void 0) { samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE; }
             _super.call(this, null, scene, !generateMipMaps);
             this.isCube = isCube;
             /**
@@ -48,13 +49,17 @@ var BABYLON;
             this._size = size;
             this._generateMipMaps = generateMipMaps;
             this._doNotChangeAspectRatio = doNotChangeAspectRatio;
+            if (samplingMode === BABYLON.Texture.NEAREST_SAMPLINGMODE) {
+                this.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
+                this.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
+            }
             if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
+                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps, samplingMode: samplingMode });
                 this.coordinatesMode = BABYLON.Texture.INVCUBIC_MODE;
                 this._textureMatrix = BABYLON.Matrix.Identity();
             }
             else {
-                this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, type: type });
+                this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, type: type, samplingMode: samplingMode });
             }
             // Rendering groups
             this._renderingManager = new BABYLON.RenderingManager(scene);
@@ -184,6 +189,9 @@ var BABYLON;
         };
         RenderTargetTexture.prototype.render = function (useCameraPostProcess, dumpForDebug) {
             var scene = this.getScene();
+            if (this.useCameraPostProcesses !== undefined) {
+                useCameraPostProcess = this.useCameraPostProcesses;
+            }
             if (this.activeCamera && this.activeCamera !== scene.activeCamera) {
                 scene.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix(true));
             }

+ 13 - 3
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -31,6 +31,7 @@
         public coordinatesMode = Texture.PROJECTION_MODE;
         public activeCamera: Camera;
         public customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, beforeTransparents?: () => void) => void;
+        public useCameraPostProcesses: boolean;
 
         // Events
 
@@ -99,7 +100,7 @@
         private _refreshRate = 1;
         private _textureMatrix: Matrix;
 
-        constructor(name: string, size: any, scene: Scene, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false) {
+        constructor(name: string, size: any, scene: Scene, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE) {
             super(null, scene, !generateMipMaps);
 
             this.name = name;
@@ -108,12 +109,17 @@
             this._generateMipMaps = generateMipMaps;
             this._doNotChangeAspectRatio = doNotChangeAspectRatio;
 
+            if (samplingMode === Texture.NEAREST_SAMPLINGMODE) {
+                this.wrapU = Texture.CLAMP_ADDRESSMODE;
+                this.wrapV = Texture.CLAMP_ADDRESSMODE;
+            }
+
             if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
+                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps, samplingMode: samplingMode });
                 this.coordinatesMode = Texture.INVCUBIC_MODE;
                 this._textureMatrix = Matrix.Identity();
             } else {
-                this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, type: type });
+                this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, type: type, samplingMode: samplingMode });
             }
 
             // Rendering groups
@@ -190,6 +196,10 @@
         public render(useCameraPostProcess?: boolean, dumpForDebug?: boolean) {
             var scene = this.getScene();
 
+            if (this.useCameraPostProcesses !== undefined) {
+                useCameraPostProcess = this.useCameraPostProcesses;
+            }
+
             if (this.activeCamera && this.activeCamera !== scene.activeCamera) {
                 scene.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix(true));
             }

+ 545 - 0
src/Materials/babylon.colorCurves.js

@@ -0,0 +1,545 @@
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT).
+     * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects.
+     * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image;
+     * corresponding to low luminance, medium luminance, and high luminance areas respectively.
+     */
+    var ColorCurves = (function () {
+        function ColorCurves() {
+            this._dirty = true;
+            this._tempColor = new BABYLON.Color4(0, 0, 0, 0);
+            this._globalCurve = new BABYLON.Color4(0, 0, 0, 0);
+            this._highlightsCurve = new BABYLON.Color4(0, 0, 0, 0);
+            this._midtonesCurve = new BABYLON.Color4(0, 0, 0, 0);
+            this._shadowsCurve = new BABYLON.Color4(0, 0, 0, 0);
+            this._positiveCurve = new BABYLON.Color4(0, 0, 0, 0);
+            this._negativeCurve = new BABYLON.Color4(0, 0, 0, 0);
+            this._globalHue = 30;
+            this._globalDensity = 0;
+            this._globalSaturation = 0;
+            this._globalExposure = 0;
+            this._highlightsHue = 30;
+            this._highlightsDensity = 0;
+            this._highlightsSaturation = 0;
+            this._highlightsExposure = 0;
+            this._midtonesHue = 30;
+            this._midtonesDensity = 0;
+            this._midtonesSaturation = 0;
+            this._midtonesExposure = 0;
+            this._shadowsHue = 30;
+            this._shadowsDensity = 0;
+            this._shadowsSaturation = 0;
+            this._shadowsExposure = 0;
+        }
+        Object.defineProperty(ColorCurves.prototype, "GlobalHue", {
+            /**
+             * Gets the global Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            get: function () {
+                return this._globalHue;
+            },
+            /**
+             * Sets the global Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            set: function (value) {
+                this._globalHue = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "GlobalDensity", {
+            /**
+             * Gets the global Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            get: function () {
+                return this._globalDensity;
+            },
+            /**
+             * Sets the global Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            set: function (value) {
+                this._globalDensity = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "GlobalSaturation", {
+            /**
+             * Gets the global Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            get: function () {
+                return this._globalSaturation;
+            },
+            /**
+             * Sets the global Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            set: function (value) {
+                this._globalSaturation = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "HighlightsHue", {
+            /**
+             * Gets the highlights Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            get: function () {
+                return this._highlightsHue;
+            },
+            /**
+             * Sets the highlights Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            set: function (value) {
+                this._highlightsHue = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "HighlightsDensity", {
+            /**
+             * Gets the highlights Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            get: function () {
+                return this._highlightsDensity;
+            },
+            /**
+             * Sets the highlights Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            set: function (value) {
+                this._highlightsDensity = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "HighlightsSaturation", {
+            /**
+             * Gets the highlights Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            get: function () {
+                return this._highlightsSaturation;
+            },
+            /**
+             * Sets the highlights Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            set: function (value) {
+                this._highlightsSaturation = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "HighlightsExposure", {
+            /**
+             * Gets the highlights Exposure value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+             */
+            get: function () {
+                return this._highlightsExposure;
+            },
+            /**
+             * Sets the highlights Exposure value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+             */
+            set: function (value) {
+                this._highlightsExposure = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "MidtonesHue", {
+            /**
+             * Gets the midtones Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            get: function () {
+                return this._midtonesHue;
+            },
+            /**
+             * Sets the midtones Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            set: function (value) {
+                this._midtonesHue = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "MidtonesDensity", {
+            /**
+             * Gets the midtones Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            get: function () {
+                return this._midtonesDensity;
+            },
+            /**
+             * Sets the midtones Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            set: function (value) {
+                this._midtonesDensity = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "MidtonesSaturation", {
+            /**
+             * Gets the midtones Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            get: function () {
+                return this._midtonesSaturation;
+            },
+            /**
+             * Sets the midtones Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            set: function (value) {
+                this._midtonesSaturation = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "MidtonesExposure", {
+            /**
+             * Gets the midtones Exposure value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+             */
+            get: function () {
+                return this._midtonesExposure;
+            },
+            /**
+             * Sets the midtones Exposure value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+             */
+            set: function (value) {
+                this._midtonesExposure = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "ShadowsHue", {
+            /**
+             * Gets the shadows Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            get: function () {
+                return this._shadowsHue;
+            },
+            /**
+             * Sets the shadows Hue value.
+             * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+             */
+            set: function (value) {
+                this._shadowsHue = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "ShadowsDensity", {
+            /**
+             * Gets the shadows Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            get: function () {
+                return this._shadowsDensity;
+            },
+            /**
+             * Sets the shadows Density value.
+             * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect.
+             * Values less than zero provide a filter of opposite hue.
+             */
+            set: function (value) {
+                this._shadowsDensity = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "ShadowsSaturation", {
+            /**
+             * Gets the shadows Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            get: function () {
+                return this._shadowsSaturation;
+            },
+            /**
+             * Sets the shadows Saturation value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+             */
+            set: function (value) {
+                this._shadowsSaturation = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ColorCurves.prototype, "ShadowsExposure", {
+            /**
+             * Gets the shadows Exposure value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+             */
+            get: function () {
+                return this._shadowsExposure;
+            },
+            /**
+             * Sets the shadows Exposure value.
+             * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+             */
+            set: function (value) {
+                this._shadowsExposure = value;
+                this._dirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Binds the color curves to the shader.
+         * @param colorCurves The color curve to bind
+         * @param effect The effect to bind to
+         */
+        ColorCurves.Bind = function (colorCurves, effect) {
+            if (colorCurves._dirty) {
+                colorCurves._dirty = false;
+                // Fill in global info.
+                colorCurves.getColorGradingDataToRef(colorCurves._globalHue, colorCurves._globalDensity, colorCurves._globalSaturation, colorCurves._globalExposure, colorCurves._globalCurve);
+                // Compute highlights info.
+                colorCurves.getColorGradingDataToRef(colorCurves._highlightsHue, colorCurves._highlightsDensity, colorCurves._highlightsSaturation, colorCurves._highlightsExposure, colorCurves._tempColor);
+                colorCurves._tempColor.multiplyToRef(colorCurves._globalCurve, colorCurves._highlightsCurve);
+                // Compute midtones info.
+                colorCurves.getColorGradingDataToRef(colorCurves._midtonesHue, colorCurves._midtonesDensity, colorCurves._midtonesSaturation, colorCurves._midtonesExposure, colorCurves._tempColor);
+                colorCurves._tempColor.multiplyToRef(colorCurves._globalCurve, colorCurves._midtonesCurve);
+                // Compute shadows info.
+                colorCurves.getColorGradingDataToRef(colorCurves._shadowsHue, colorCurves._shadowsDensity, colorCurves._shadowsSaturation, colorCurves._shadowsExposure, colorCurves._tempColor);
+                colorCurves._tempColor.multiplyToRef(colorCurves._globalCurve, colorCurves._shadowsCurve);
+                // Compute deltas (neutral is midtones).
+                colorCurves._highlightsCurve.subtractToRef(colorCurves._midtonesCurve, colorCurves._positiveCurve);
+                colorCurves._midtonesCurve.subtractToRef(colorCurves._shadowsCurve, colorCurves._negativeCurve);
+            }
+            effect.setFloat4("vCameraColorCurvePositive", colorCurves._positiveCurve.r, colorCurves._positiveCurve.g, colorCurves._positiveCurve.b, colorCurves._positiveCurve.a);
+            effect.setFloat4("vCameraColorCurveNeutral", colorCurves._midtonesCurve.r, colorCurves._midtonesCurve.g, colorCurves._midtonesCurve.b, colorCurves._midtonesCurve.a);
+            effect.setFloat4("vCameraColorCurveNegative", colorCurves._negativeCurve.r, colorCurves._negativeCurve.g, colorCurves._negativeCurve.b, colorCurves._negativeCurve.a);
+        };
+        /**
+         * Prepare the list of uniforms associated with the ColorCurves effects.
+         * @param uniformsList The list of uniforms used in the effect
+         */
+        ColorCurves.PrepareUniforms = function (uniformsList) {
+            uniformsList.push("vCameraColorCurveNeutral", "vCameraColorCurvePositive", "vCameraColorCurveNegative");
+        };
+        /**
+         * Returns color grading data based on a hue, density, saturation and exposure value.
+         * @param filterHue The hue of the color filter.
+         * @param filterDensity The density of the color filter.
+         * @param saturation The saturation.
+         * @param exposure The exposure.
+         * @param result The result data container.
+         */
+        ColorCurves.prototype.getColorGradingDataToRef = function (hue, density, saturation, exposure, result) {
+            if (hue == null) {
+                return;
+            }
+            hue = ColorCurves.clamp(hue, 0, 360);
+            density = ColorCurves.clamp(density, -100, 100);
+            saturation = ColorCurves.clamp(saturation, -100, 100);
+            exposure = ColorCurves.clamp(exposure, -100, 100);
+            // Remap the slider/config filter density with non-linear mapping and also scale by half
+            // so that the maximum filter density is only 50% control. This provides fine control 
+            // for small values and reasonable range.
+            density = ColorCurves.applyColorGradingSliderNonlinear(density);
+            density *= 0.5;
+            exposure = ColorCurves.applyColorGradingSliderNonlinear(exposure);
+            if (density < 0) {
+                density *= -1;
+                hue = (hue + 180) % 360;
+            }
+            ColorCurves.fromHSBToRef(hue, density, 50 + 0.25 * exposure, result);
+            result.scaleToRef(2, result);
+            result.a = 1 + 0.01 * saturation;
+        };
+        /**
+         * Takes an input slider value and returns an adjusted value that provides extra control near the centre.
+         * @param value The input slider value in range [-100,100].
+         * @returns Adjusted value.
+         */
+        ColorCurves.applyColorGradingSliderNonlinear = function (value) {
+            value /= 100;
+            var x = Math.abs(value);
+            x = Math.pow(x, 2);
+            if (value < 0) {
+                x *= -1;
+            }
+            x *= 100;
+            return x;
+        };
+        /**
+         * Returns an RGBA Color4 based on Hue, Saturation and Brightness (also referred to as value, HSV).
+         * @param hue The hue (H) input.
+         * @param saturation The saturation (S) input.
+         * @param brightness The brightness (B) input.
+         * @result An RGBA color represented as Vector4.
+         */
+        ColorCurves.fromHSBToRef = function (hue, saturation, brightness, result) {
+            var h = ColorCurves.clamp(hue, 0, 360);
+            var s = ColorCurves.clamp(saturation / 100, 0, 1);
+            var v = ColorCurves.clamp(brightness / 100, 0, 1);
+            if (s === 0) {
+                result.r = v;
+                result.g = v;
+                result.b = v;
+            }
+            else {
+                // sector 0 to 5
+                h /= 60;
+                var i = Math.floor(h);
+                // fractional part of h
+                var f = h - i;
+                var p = v * (1 - s);
+                var q = v * (1 - s * f);
+                var t = v * (1 - s * (1 - f));
+                switch (i) {
+                    case 0:
+                        result.r = v;
+                        result.g = t;
+                        result.b = p;
+                        break;
+                    case 1:
+                        result.r = q;
+                        result.g = v;
+                        result.b = p;
+                        break;
+                    case 2:
+                        result.r = p;
+                        result.g = v;
+                        result.b = t;
+                        break;
+                    case 3:
+                        result.r = p;
+                        result.g = q;
+                        result.b = v;
+                        break;
+                    case 4:
+                        result.r = t;
+                        result.g = p;
+                        result.b = v;
+                        break;
+                    default:
+                        result.r = v;
+                        result.g = p;
+                        result.b = q;
+                        break;
+                }
+            }
+            result.a = 1;
+        };
+        /**
+         * Returns a value clamped between min and max
+         * @param value The value to clamp
+         * @param min The minimum of value
+         * @param max The maximum of value
+         * @returns The clamped value.
+         */
+        ColorCurves.clamp = function (value, min, max) {
+            return Math.min(Math.max(value, min), max);
+        };
+        /**
+         * Clones the current color curve instance.
+         * @return The cloned curves
+         */
+        ColorCurves.prototype.clone = function () {
+            return BABYLON.SerializationHelper.Clone(function () { return new ColorCurves(); }, this);
+        };
+        /**
+         * Serializes the current color curve instance to a json representation.
+         * @return a JSON representation
+         */
+        ColorCurves.prototype.serialize = function () {
+            return BABYLON.SerializationHelper.Serialize(this);
+        };
+        /**
+         * Parses the color curve from a json representation.
+         * @param source the JSON source to parse
+         * @return The parsed curves
+         */
+        ColorCurves.Parse = function (source) {
+            return BABYLON.SerializationHelper.Parse(function () { return new ColorCurves(); }, source, null, null);
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_globalHue", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_globalDensity", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_globalSaturation", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_globalExposure", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_highlightsHue", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_highlightsDensity", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_highlightsSaturation", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_highlightsExposure", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_midtonesHue", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_midtonesDensity", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_midtonesSaturation", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ColorCurves.prototype, "_midtonesExposure", void 0);
+        return ColorCurves;
+    })();
+    BABYLON.ColorCurves = ColorCurves;
+})(BABYLON || (BABYLON = {}));

+ 535 - 0
src/Materials/babylon.colorCurves.ts

@@ -0,0 +1,535 @@
+module BABYLON {
+    
+    /**
+     * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT). 
+     * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects.
+     * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; 
+     * corresponding to low luminance, medium luminance, and high luminance areas respectively.
+     */
+    export class ColorCurves {
+        
+        private _dirty = true;
+        
+        private _tempColor = new Color4(0, 0, 0, 0);
+        
+        private _globalCurve = new Color4(0, 0, 0, 0);
+        private _highlightsCurve = new Color4(0, 0, 0, 0);
+        private _midtonesCurve = new Color4(0, 0, 0, 0);
+        private _shadowsCurve = new Color4(0, 0, 0, 0);
+        
+        private _positiveCurve = new Color4(0, 0, 0, 0);
+        private _negativeCurve = new Color4(0, 0, 0, 0);
+        
+        @serialize()
+        private _globalHue = 30;
+        
+        @serialize()
+        private _globalDensity = 0;
+        
+        @serialize()
+        private _globalSaturation = 0;
+        
+        @serialize()
+        private _globalExposure = 0;
+        
+        /**
+         * Gets the global Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public get GlobalHue(): number {
+            return this._globalHue;
+        }
+        /**
+         * Sets the global Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public set GlobalHue(value: number) {
+            this._globalHue = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the global Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public get GlobalDensity(): number {
+            return this._globalDensity;
+        }
+        /**
+         * Sets the global Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public set GlobalDensity(value: number) {
+            this._globalDensity = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the global Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public get GlobalSaturation(): number {
+            return this._globalSaturation;
+        }
+        /**
+         * Sets the global Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public set GlobalSaturation(value: number) {
+            this._globalSaturation = value;
+            this._dirty = true;
+        }
+        
+        @serialize()
+        private _highlightsHue = 30;
+        
+        @serialize()
+        private _highlightsDensity = 0;
+        
+        @serialize()
+        private _highlightsSaturation = 0;
+        
+        @serialize()
+        private _highlightsExposure = 0;
+        
+        /**
+         * Gets the highlights Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public get HighlightsHue(): number {
+            return this._highlightsHue;
+        }
+        /**
+         * Sets the highlights Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public set HighlightsHue(value: number) {
+            this._highlightsHue = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the highlights Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public get HighlightsDensity(): number {
+            return this._highlightsDensity;
+        }
+        /**
+         * Sets the highlights Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public set HighlightsDensity(value: number) {
+            this._highlightsDensity = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the highlights Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public get HighlightsSaturation(): number {
+            return this._highlightsSaturation;
+        }
+        /**
+         * Sets the highlights Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public set HighlightsSaturation(value: number) {
+            this._highlightsSaturation = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the highlights Exposure value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+         */
+        public get HighlightsExposure(): number {
+            return this._highlightsExposure;
+        }
+        /**
+         * Sets the highlights Exposure value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+         */
+        public set HighlightsExposure(value: number) {
+            this._highlightsExposure = value;
+            this._dirty = true;
+        }
+        
+        @serialize()
+        private _midtonesHue = 30;
+        
+        @serialize()
+        private _midtonesDensity = 0;
+        
+        @serialize()
+        private _midtonesSaturation = 0;
+        
+        @serialize()
+        private _midtonesExposure = 0;
+        
+        /**
+         * Gets the midtones Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public get MidtonesHue(): number {
+            return this._midtonesHue;
+        }
+        /**
+         * Sets the midtones Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public set MidtonesHue(value: number) {
+            this._midtonesHue = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the midtones Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public get MidtonesDensity(): number {
+            return this._midtonesDensity;
+        }
+        /**
+         * Sets the midtones Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public set MidtonesDensity(value: number) {
+            this._midtonesDensity = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the midtones Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public get MidtonesSaturation(): number {
+            return this._midtonesSaturation;
+        }
+        /**
+         * Sets the midtones Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public set MidtonesSaturation(value: number) {
+            this._midtonesSaturation = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the midtones Exposure value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+         */
+        public get MidtonesExposure(): number {
+            return this._midtonesExposure;
+        }
+        /**
+         * Sets the midtones Exposure value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+         */
+        public set MidtonesExposure(value: number) {
+            this._midtonesExposure = value;
+            this._dirty = true;
+        }
+        
+        private _shadowsHue = 30;
+        private _shadowsDensity = 0;
+        private _shadowsSaturation = 0;
+        private _shadowsExposure = 0;
+        
+        /**
+         * Gets the shadows Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public get ShadowsHue(): number {
+            return this._shadowsHue;
+        }
+        /**
+         * Sets the shadows Hue value.
+         * The hue value is a standard HSB hue in the range [0,360] where 0=red, 120=green and 240=blue. The default value is 30 degrees (orange).
+         */
+        public set ShadowsHue(value: number) {
+            this._shadowsHue = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the shadows Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public get ShadowsDensity(): number {
+            return this._shadowsDensity;
+        }
+        /**
+         * Sets the shadows Density value.
+         * The density value is in range [-100,+100] where 0 means the color filter has no effect and +100 means the color filter has maximum effect. 
+         * Values less than zero provide a filter of opposite hue.
+         */
+        public set ShadowsDensity(value: number) {
+            this._shadowsDensity = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the shadows Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public get ShadowsSaturation(): number {
+            return this._shadowsSaturation;
+        }
+        /**
+         * Sets the shadows Saturation value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase saturation and negative values decrease saturation.
+         */
+        public set ShadowsSaturation(value: number) {
+            this._shadowsSaturation = value;
+            this._dirty = true;
+        }
+        /**
+         * Gets the shadows Exposure value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+         */
+        public get ShadowsExposure(): number {
+            return this._shadowsExposure;
+        }
+        /**
+         * Sets the shadows Exposure value.
+         * This is an adjustment value in the range [-100,+100], where the default value of 0.0 makes no adjustment, positive values increase exposure and negative values decrease exposure.
+         */
+        public set ShadowsExposure(value: number) {
+            this._shadowsExposure = value;
+            this._dirty = true;
+        }
+        
+        /**
+         * Binds the color curves to the shader.
+         * @param colorCurves The color curve to bind
+         * @param effect The effect to bind to
+         */
+        public static Bind(colorCurves: ColorCurves, effect: Effect) : void {
+            if (colorCurves._dirty) {
+                colorCurves._dirty = false;
+                            
+                // Fill in global info.
+                colorCurves.getColorGradingDataToRef(
+                    colorCurves._globalHue,
+                    colorCurves._globalDensity,
+                    colorCurves._globalSaturation,
+                    colorCurves._globalExposure,
+                    colorCurves._globalCurve);
+
+                // Compute highlights info.
+                colorCurves.getColorGradingDataToRef(
+                    colorCurves._highlightsHue,
+                    colorCurves._highlightsDensity,
+                    colorCurves._highlightsSaturation,
+                    colorCurves._highlightsExposure,
+                    colorCurves._tempColor);
+                colorCurves._tempColor.multiplyToRef(colorCurves._globalCurve, colorCurves._highlightsCurve);
+
+                // Compute midtones info.
+                colorCurves.getColorGradingDataToRef(
+                    colorCurves._midtonesHue,
+                    colorCurves._midtonesDensity,
+                    colorCurves._midtonesSaturation,
+                    colorCurves._midtonesExposure,
+                    colorCurves._tempColor);
+                colorCurves._tempColor.multiplyToRef(colorCurves._globalCurve, colorCurves._midtonesCurve);
+                
+                // Compute shadows info.
+                colorCurves.getColorGradingDataToRef(
+                    colorCurves._shadowsHue,
+                    colorCurves._shadowsDensity,
+                    colorCurves._shadowsSaturation,
+                    colorCurves._shadowsExposure,
+                    colorCurves._tempColor);
+                colorCurves._tempColor.multiplyToRef(colorCurves._globalCurve, colorCurves._shadowsCurve);
+                
+                // Compute deltas (neutral is midtones).
+                colorCurves._highlightsCurve.subtractToRef(colorCurves._midtonesCurve, colorCurves._positiveCurve);
+                colorCurves._midtonesCurve.subtractToRef(colorCurves._shadowsCurve, colorCurves._negativeCurve);            
+            }
+            
+            effect.setFloat4("vCameraColorCurvePositive", 
+                colorCurves._positiveCurve.r,
+                colorCurves._positiveCurve.g,
+                colorCurves._positiveCurve.b,
+                colorCurves._positiveCurve.a);
+            effect.setFloat4("vCameraColorCurveNeutral", 
+                colorCurves._midtonesCurve.r,
+                colorCurves._midtonesCurve.g,
+                colorCurves._midtonesCurve.b,
+                colorCurves._midtonesCurve.a);
+            effect.setFloat4("vCameraColorCurveNegative", 
+                colorCurves._negativeCurve.r,
+                colorCurves._negativeCurve.g,
+                colorCurves._negativeCurve.b,
+                colorCurves._negativeCurve.a);
+        }
+        
+        /**
+         * Prepare the list of uniforms associated with the ColorCurves effects.
+         * @param uniformsList The list of uniforms used in the effect
+         */
+        public static PrepareUniforms(uniformsList: string[]): void {
+            uniformsList.push(
+                "vCameraColorCurveNeutral", 
+                "vCameraColorCurvePositive", 
+                "vCameraColorCurveNegative"
+            );
+        }
+        
+        /**
+         * Returns color grading data based on a hue, density, saturation and exposure value.
+         * @param filterHue The hue of the color filter.
+         * @param filterDensity The density of the color filter.
+         * @param saturation The saturation.
+         * @param exposure The exposure.
+         * @param result The result data container.
+         */
+        private getColorGradingDataToRef(hue: number, density: number, saturation: number, exposure: number, result: Color4) : void {
+            if (hue == null) {
+                return;
+            }
+
+            hue = ColorCurves.clamp(hue, 0, 360);
+            density = ColorCurves.clamp(density, -100, 100);
+            saturation = ColorCurves.clamp(saturation, -100, 100);
+            exposure = ColorCurves.clamp(exposure, -100, 100);
+                
+            // Remap the slider/config filter density with non-linear mapping and also scale by half
+            // so that the maximum filter density is only 50% control. This provides fine control 
+            // for small values and reasonable range.
+            density = ColorCurves.applyColorGradingSliderNonlinear(density);
+            density *= 0.5;
+
+            exposure = ColorCurves.applyColorGradingSliderNonlinear(exposure);
+
+            if (density < 0) {
+                density *= -1;
+                hue = (hue + 180) % 360;
+            }
+            
+            ColorCurves.fromHSBToRef(hue, density, 50 + 0.25 * exposure, result);            
+            result.scaleToRef(2, result);
+            result.a = 1 + 0.01 * saturation;
+        }
+        
+        /**
+         * Takes an input slider value and returns an adjusted value that provides extra control near the centre.
+         * @param value The input slider value in range [-100,100].
+         * @returns Adjusted value.
+         */
+        private static applyColorGradingSliderNonlinear(value: number): number {
+            value /= 100;
+
+            var x: number = Math.abs(value);
+            x = Math.pow(x, 2);
+
+            if (value < 0) {
+                x *= -1;
+            }
+
+            x *= 100;
+
+            return x;
+        }
+        
+        /**
+         * Returns an RGBA Color4 based on Hue, Saturation and Brightness (also referred to as value, HSV).
+         * @param hue The hue (H) input.
+         * @param saturation The saturation (S) input.
+         * @param brightness The brightness (B) input.
+         * @result An RGBA color represented as Vector4.
+         */
+        private static fromHSBToRef(hue: number, saturation: number, brightness: number, result: Color4): void {
+            var h: number = ColorCurves.clamp(hue, 0, 360);
+            var s: number = ColorCurves.clamp(saturation / 100, 0, 1);
+            var v: number = ColorCurves.clamp(brightness / 100, 0, 1);
+
+            if (s === 0) {
+                result.r = v;
+                result.g = v;
+                result.b = v;
+            } else {
+                // sector 0 to 5
+                h /= 60;
+                var i = Math.floor(h);
+
+                // fractional part of h
+                var f = h - i;
+                var p = v * (1 - s);
+                var q = v * (1 - s * f);
+                var t = v * (1 - s * (1 - f));
+
+                switch (i) {
+                    case 0:
+                        result.r = v;
+                        result.g = t;
+                        result.b = p;
+                        break;
+                    case 1:
+                        result.r = q;
+                        result.g = v;
+                        result.b = p;
+                        break;
+                    case 2:
+                        result.r = p;
+                        result.g = v;
+                        result.b = t;
+                        break;
+                    case 3:
+                        result.r = p;
+                        result.g = q;
+                        result.b = v;
+                        break;
+                    case 4:
+                        result.r = t;
+                        result.g = p;
+                        result.b = v;
+                        break;
+                    default:       // case 5:
+                        result.r = v;
+                        result.g = p;
+                        result.b = q;
+                        break;
+                }
+            }
+
+            result.a = 1;
+        }
+        
+        /**
+         * Returns a value clamped between min and max
+         * @param value The value to clamp
+         * @param min The minimum of value
+         * @param max The maximum of value
+         * @returns The clamped value.
+         */
+        private static clamp(value, min, max): number {
+            return Math.min(Math.max(value, min), max);
+        }
+
+        /**
+         * Clones the current color curve instance.
+         * @return The cloned curves
+         */
+        public clone(): ColorCurves {
+            return SerializationHelper.Clone(() => new ColorCurves(), this);
+        }
+
+        /**
+         * Serializes the current color curve instance to a json representation.
+         * @return a JSON representation
+         */
+        public serialize(): any {
+            return SerializationHelper.Serialize(this);
+        }
+
+        /**
+         * Parses the color curve from a json representation.
+         * @param source the JSON source to parse
+         * @return The parsed curves
+         */      
+        public static Parse(source: any) : ColorCurves {
+            return SerializationHelper.Parse(() => new ColorCurves(), source, null, null);
+        }
+    }
+} 

+ 18 - 0
src/Materials/babylon.pbrMaterial.js

@@ -62,6 +62,7 @@ var BABYLON;
             this.CAMERATONEMAP = false;
             this.CAMERACONTRAST = false;
             this.CAMERACOLORGRADING = false;
+            this.CAMERACOLORCURVES = false;
             this.OVERLOADEDVALUES = false;
             this.OVERLOADEDSHADOWVALUES = false;
             this.USESPHERICALFROMREFLECTIONMAP = false;
@@ -153,6 +154,13 @@ var BABYLON;
             this.cameraColorGradingTexture = null;
             this._cameraColorGradingScaleOffset = new BABYLON.Vector4(1.0, 1.0, 0.0, 0.0);
             this._cameraColorGradingInfos = new BABYLON.Vector4(1.0, 1.0, 0.0, 0.0);
+            /**
+             * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT).
+             * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects.
+             * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image;
+             * corresponding to low luminance, medium luminance, and high luminance areas respectively.
+             */
+            this.cameraColorCurves = null;
             this._cameraInfos = new BABYLON.Vector4(1.0, 1.0, 0.0, 0.0);
             this._microsurfaceTextureLods = new BABYLON.Vector2(0.0, 0.0);
             /**
@@ -618,6 +626,9 @@ var BABYLON;
             if (this.cameraExposure != 1) {
                 this._defines.CAMERATONEMAP = true;
             }
+            if (this.cameraColorCurves) {
+                this._defines.CAMERACOLORCURVES = true;
+            }
             if (this.overloadedShadeIntensity != 1 ||
                 this.overloadedShadowIntensity != 1) {
                 this._defines.OVERLOADEDSHADOWVALUES = true;
@@ -783,6 +794,7 @@ var BABYLON;
                 ];
                 var samplers = ["albedoSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "reflectivitySampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                     "cameraColorGrading2DSampler"];
+                BABYLON.ColorCurves.PrepareUniforms(uniforms);
                 BABYLON.MaterialHelper.PrepareUniformsAndSamplersList(uniforms, samplers, this._defines, this.maxSimultaneousLights);
                 this._effect = scene.getEngine().createEffect(shaderName, attribs, uniforms, samplers, join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights });
             }
@@ -967,6 +979,9 @@ var BABYLON;
                 this._cameraInfos.x = this.cameraExposure;
                 this._cameraInfos.y = this.cameraContrast;
                 this._effect.setVector4("vCameraInfos", this._cameraInfos);
+                if (this.cameraColorCurves) {
+                    BABYLON.ColorCurves.Bind(this.cameraColorCurves, this._effect);
+                }
                 this._overloadedIntensity.x = this.overloadedAmbientIntensity;
                 this._overloadedIntensity.y = this.overloadedAlbedoIntensity;
                 this._overloadedIntensity.z = this.overloadedReflectivityIntensity;
@@ -1109,6 +1124,9 @@ var BABYLON;
             BABYLON.serializeAsTexture()
         ], PBRMaterial.prototype, "cameraColorGradingTexture", void 0);
         __decorate([
+            BABYLON.serializeAsColorCurves()
+        ], PBRMaterial.prototype, "cameraColorCurves", void 0);
+        __decorate([
             BABYLON.serializeAsColor3()
         ], PBRMaterial.prototype, "overloadedAmbient", void 0);
         __decorate([

+ 19 - 0
src/Materials/babylon.pbrMaterial.ts

@@ -47,6 +47,7 @@
         public CAMERATONEMAP = false;
         public CAMERACONTRAST = false;
         public CAMERACOLORGRADING = false;
+        public CAMERACOLORCURVES = false;
         public OVERLOADEDVALUES = false;
         public OVERLOADEDSHADOWVALUES = false;
         public USESPHERICALFROMREFLECTIONMAP = false;
@@ -154,6 +155,15 @@
 
         private _cameraColorGradingScaleOffset: Vector4 = new Vector4(1.0, 1.0, 0.0, 0.0);
         private _cameraColorGradingInfos: Vector4 = new Vector4(1.0, 1.0, 0.0, 0.0);
+        
+        /**
+         * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT). 
+         * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects.
+         * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; 
+         * corresponding to low luminance, medium luminance, and high luminance areas respectively.
+         */
+        @serializeAsColorCurves()
+        public cameraColorCurves: ColorCurves = null;
          
         private _cameraInfos: Vector4 = new Vector4(1.0, 1.0, 0.0, 0.0);
 
@@ -808,6 +818,10 @@
             if (this.cameraExposure != 1) {
                 this._defines.CAMERATONEMAP = true;
             }
+            
+            if (this.cameraColorCurves) {
+                this._defines.CAMERACOLORCURVES = true;
+            }
 
             if (this.overloadedShadeIntensity != 1 ||
                 this.overloadedShadowIntensity != 1) {
@@ -1016,6 +1030,7 @@
                 var samplers = ["albedoSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "reflectivitySampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                     "cameraColorGrading2DSampler"];
                 
+                ColorCurves.PrepareUniforms(uniforms); 
                 MaterialHelper.PrepareUniformsAndSamplersList(uniforms, samplers, this._defines, this.maxSimultaneousLights); 
                 
                 this._effect = scene.getEngine().createEffect(shaderName,
@@ -1287,6 +1302,10 @@
                 this._cameraInfos.x = this.cameraExposure;
                 this._cameraInfos.y = this.cameraContrast;
                 this._effect.setVector4("vCameraInfos", this._cameraInfos);
+                
+                if (this.cameraColorCurves) {
+                    ColorCurves.Bind(this.cameraColorCurves, this._effect);
+                }
 
                 this._overloadedIntensity.x = this.overloadedAmbientIntensity;
                 this._overloadedIntensity.y = this.overloadedAlbedoIntensity;

+ 48 - 0
src/Math/babylon.math.js

@@ -251,6 +251,27 @@ var BABYLON;
             result.a = this.a * scale;
             return this;
         };
+        /**
+          * Multipy an RGBA Color4 value by another and return a new Color4 object
+          * @param color The Color4 (RGBA) value to multiply by
+          * @returns A new Color4.
+          */
+        Color4.prototype.multiply = function (color) {
+            return new Color4(this.r * color.r, this.g * color.g, this.b * color.b, this.a * color.a);
+        };
+        /**
+         * Multipy an RGBA Color4 value by another and push the result in a reference value
+         * @param color The Color4 (RGBA) value to multiply by
+         * @param result The Color4 (RGBA) to fill the result in
+         * @returns the result Color4.
+         */
+        Color4.prototype.multiplyToRef = function (color, result) {
+            result.r = this.r * color.r;
+            result.g = this.g * color.g;
+            result.b = this.b * color.b;
+            result.a = this.a * color.a;
+            return result;
+        };
         Color4.prototype.toString = function () {
             return "{R: " + this.r + " G:" + this.g + " B:" + this.b + " A:" + this.a + "}";
         };
@@ -370,12 +391,22 @@ var BABYLON;
         Vector2.prototype.add = function (otherVector) {
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         };
+        Vector2.prototype.addToRef = function (otherVector, result) {
+            result.x = this.x + otherVector.x;
+            result.y = this.y + otherVector.y;
+            return this;
+        };
         Vector2.prototype.addVector3 = function (otherVector) {
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         };
         Vector2.prototype.subtract = function (otherVector) {
             return new Vector2(this.x - otherVector.x, this.y - otherVector.y);
         };
+        Vector2.prototype.subtractToRef = function (otherVector, result) {
+            result.x = this.x - otherVector.x;
+            result.y = this.y - otherVector.y;
+            return this;
+        };
         Vector2.prototype.subtractInPlace = function (otherVector) {
             this.x -= otherVector.x;
             this.y -= otherVector.y;
@@ -520,6 +551,13 @@ var BABYLON;
             result.x = x;
             result.y = y;
         };
+        Vector2.PointInTriangle = function (p, p0, p1, p2) {
+            var a = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
+            var sign = a < 0 ? -1 : 1;
+            var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign;
+            var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign;
+            return s > 0 && t > 0 && (s + t) < 2 * a * sign;
+        };
         Vector2.Distance = function (value1, value2) {
             return Math.sqrt(Vector2.DistanceSquared(value1, value2));
         };
@@ -528,6 +566,16 @@ var BABYLON;
             var y = value1.y - value2.y;
             return (x * x) + (y * y);
         };
+        Vector2.DistanceOfPointFromSegment = function (p, segA, segB) {
+            var l2 = Vector2.DistanceSquared(segA, segB);
+            if (l2 === 0.0) {
+                return Vector2.Distance(p, segA);
+            }
+            var v = segB.subtract(segA);
+            var t = Math.max(0, Math.min(1, Vector2.Dot(p.subtract(segA), v) / l2));
+            var proj = segA.add(v.multiplyByFloats(t, t));
+            return Vector2.Distance(p, proj);
+        };
         return Vector2;
     })();
     BABYLON.Vector2 = Vector2;

+ 29 - 5
src/Math/babylon.math.ts

@@ -304,7 +304,31 @@
 
             return this;
         }
-
+        
+       /**
+         * Multipy an RGBA Color4 value by another and return a new Color4 object
+         * @param color The Color4 (RGBA) value to multiply by
+         * @returns A new Color4.
+         */
+        public multiply(color: Color4): Color4 {
+            return new Color4(this.r * color.r, this.g * color.g, this.b * color.b, this.a * color.a);
+        }
+        
+        /**
+         * Multipy an RGBA Color4 value by another and push the result in a reference value
+         * @param color The Color4 (RGBA) value to multiply by
+         * @param result The Color4 (RGBA) to fill the result in 
+         * @returns the result Color4.
+         */
+        public multiplyToRef(color: Color4, result: Color4): Color4 {
+            result.r = this.r * color.r;
+            result.g = this.g * color.g;
+            result.b = this.b * color.b;
+            result.a = this.a * color.a;
+            
+            return result;
+        }
+        
         public toString(): string {
             return "{R: " + this.r + " G:" + this.g + " B:" + this.b + " A:" + this.a + "}";
         }
@@ -452,10 +476,6 @@
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         }
 
-        public addVector3(otherVector: Vector3): Vector2 {
-            return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
-        }
-
         public addToRef(otherVector: Vector2, result: Vector2): Vector2 {
             result.x = this.x + otherVector.x;
             result.y = this.y + otherVector.y;
@@ -463,6 +483,10 @@
             return this;
         }
 
+        public addVector3(otherVector: Vector3): Vector2 {
+            return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
+        }
+
         public subtract(otherVector: Vector2): Vector2 {
             return new Vector2(this.x - otherVector.x, this.y - otherVector.y);
         }

+ 6 - 1
src/Mesh/babylon.abstractMesh.ts

@@ -95,7 +95,6 @@
         public renderingGroupId = 0;
         public material: Material;
         public receiveShadows = false;
-        public actionManager: ActionManager;
         public renderOutline = false;
         public outlineColor = Color3.Red();
         public outlineWidth = 0.02;
@@ -117,6 +116,12 @@
 
         public alwaysSelectAsActiveMesh = false;
 
+        /**
+         * This scene's action manager
+         * @type {BABYLON.ActionManager}
+        */
+        public actionManager: ActionManager;
+
         // Physics
         public physicsImpostor: BABYLON.PhysicsImpostor;
         //Deprecated, Legacy support

+ 82 - 0
src/Mesh/babylon.buffer.js

@@ -0,0 +1,82 @@
+var BABYLON;
+(function (BABYLON) {
+    var Buffer = (function () {
+        function Buffer(engine, data, updatable, stride, postponeInternalCreation, instanced) {
+            if (engine instanceof BABYLON.Mesh) {
+                this._engine = engine.getScene().getEngine();
+            }
+            else {
+                this._engine = engine;
+            }
+            this._updatable = updatable;
+            this._data = data;
+            this._strideSize = stride;
+            if (!postponeInternalCreation) {
+                this.create();
+            }
+            this._instanced = instanced;
+        }
+        Buffer.prototype.createVertexBuffer = function (kind, offset, size, stride) {
+            // a lot of these parameters are ignored as they are overriden by the buffer
+            return new BABYLON.VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, this._instanced, offset, size);
+        };
+        // Properties
+        Buffer.prototype.isUpdatable = function () {
+            return this._updatable;
+        };
+        Buffer.prototype.getData = function () {
+            return this._data;
+        };
+        Buffer.prototype.getBuffer = function () {
+            return this._buffer;
+        };
+        Buffer.prototype.getStrideSize = function () {
+            return this._strideSize;
+        };
+        Buffer.prototype.getIsInstanced = function () {
+            return this._instanced;
+        };
+        // Methods
+        Buffer.prototype.create = function (data) {
+            if (!data && this._buffer) {
+                return; // nothing to do
+            }
+            data = data || this._data;
+            if (!this._buffer) {
+                if (this._updatable) {
+                    this._buffer = this._engine.createDynamicVertexBuffer(data);
+                    this._data = data;
+                }
+                else {
+                    this._buffer = this._engine.createVertexBuffer(data);
+                }
+            }
+            else if (this._updatable) {
+                this._engine.updateDynamicVertexBuffer(this._buffer, data);
+                this._data = data;
+            }
+        };
+        Buffer.prototype.update = function (data) {
+            this.create(data);
+        };
+        Buffer.prototype.updateDirectly = function (data, offset, vertexCount) {
+            if (!this._buffer) {
+                return;
+            }
+            if (this._updatable) {
+                this._engine.updateDynamicVertexBuffer(this._buffer, data, offset, (vertexCount ? vertexCount * this.getStrideSize() : undefined));
+                this._data = null;
+            }
+        };
+        Buffer.prototype.dispose = function () {
+            if (!this._buffer) {
+                return;
+            }
+            if (this._engine._releaseBuffer(this._buffer)) {
+                this._buffer = null;
+            }
+        };
+        return Buffer;
+    })();
+    BABYLON.Buffer = Buffer;
+})(BABYLON || (BABYLON = {}));

+ 102 - 0
src/Mesh/babylon.buffer.ts

@@ -0,0 +1,102 @@
+module BABYLON {
+    export class Buffer {
+        private _engine: Engine;
+        private _buffer: WebGLBuffer;
+        private _data: number[] | Float32Array;
+        private _updatable: boolean;
+        private _strideSize: number;
+        private _instanced: boolean;
+
+        constructor(engine: any, data: number[] | Float32Array, updatable: boolean, stride: number, postponeInternalCreation?: boolean, instanced?: boolean) {
+            if (engine instanceof Mesh) { // old versions of BABYLON.VertexBuffer accepted 'mesh' instead of 'engine'
+                this._engine = engine.getScene().getEngine();
+            }
+            else {
+                this._engine = engine;
+            }
+
+            this._updatable = updatable;
+
+            this._data = data;
+
+            this._strideSize = stride;
+
+            if (!postponeInternalCreation) { // by default
+                this.create();
+            }
+
+            this._instanced = instanced;
+        }
+
+        public createVertexBuffer(kind: string, offset: number, size: number, stride?: number): VertexBuffer {
+            // a lot of these parameters are ignored as they are overriden by the buffer
+            return new VertexBuffer(this._engine, this, kind, this._updatable, true, stride ? stride : this._strideSize, this._instanced, offset, size);
+        }
+
+        // Properties
+        public isUpdatable(): boolean {
+            return this._updatable;
+        }
+
+        public getData(): number[] | Float32Array {
+            return this._data;
+        }
+
+        public getBuffer(): WebGLBuffer {
+            return this._buffer;
+        }
+
+        public getStrideSize(): number {
+            return this._strideSize;
+        }
+
+        public getIsInstanced(): boolean {
+            return this._instanced;
+        }
+
+        // Methods
+        public create(data?: number[] | Float32Array): void {
+            if (!data && this._buffer) {
+                return; // nothing to do
+            }
+
+            data = data || this._data;
+
+            if (!this._buffer) { // create buffer
+                if (this._updatable) {
+                    this._buffer = this._engine.createDynamicVertexBuffer(data);
+                    this._data = data;
+                } else {
+                    this._buffer = this._engine.createVertexBuffer(data);
+                }
+            } else if (this._updatable) { // update buffer
+                this._engine.updateDynamicVertexBuffer(this._buffer, data);
+                this._data = data;
+            }
+        }
+
+        public update(data: number[] | Float32Array): void {
+            this.create(data);
+        }
+
+        public updateDirectly(data: Float32Array, offset: number, vertexCount?: number): void {
+            if (!this._buffer) {
+                return;
+            }
+
+            if (this._updatable) { // update buffer
+                this._engine.updateDynamicVertexBuffer(this._buffer, data, offset, (vertexCount ? vertexCount * this.getStrideSize() : undefined));
+                this._data = null;
+            }
+        }
+
+        public dispose(): void {
+            if (!this._buffer) {
+                return;
+            }
+            if (this._engine._releaseBuffer(this._buffer)) {
+                this._buffer = null;
+            }
+        }
+    }
+} 

+ 45 - 8
src/Mesh/babylon.geometry.js

@@ -74,14 +74,20 @@ var BABYLON;
             this.notifyUpdate();
         };
         Geometry.prototype.setVerticesData = function (kind, data, updatable, stride) {
+            var buffer = new BABYLON.VertexBuffer(this._engine, data, kind, updatable, this._meshes.length === 0, stride);
+            this.setVerticesBuffer(buffer);
+        };
+        Geometry.prototype.setVerticesBuffer = function (buffer) {
+            var kind = buffer.getKind();
             if (this._vertexBuffers[kind]) {
                 this._vertexBuffers[kind].dispose();
             }
-            this._vertexBuffers[kind] = new BABYLON.VertexBuffer(this._engine, data, kind, updatable, this._meshes.length === 0, stride);
+            this._vertexBuffers[kind] = buffer;
             if (kind === BABYLON.VertexBuffer.PositionKind) {
-                stride = this._vertexBuffers[kind].getStrideSize();
+                var data = buffer.getData();
+                var stride = buffer.getStrideSize();
                 this._totalVertices = data.length / stride;
-                this.updateExtend(data);
+                this.updateExtend(data, stride);
                 var meshes = this._meshes;
                 var numOfMeshes = meshes.length;
                 for (var index = 0; index < numOfMeshes; index++) {
@@ -278,12 +284,12 @@ var BABYLON;
                 mesh._boundingInfo = this._boundingInfo;
             }
         };
-        Geometry.prototype.updateExtend = function (data) {
+        Geometry.prototype.updateExtend = function (data, stride) {
             if (data === void 0) { data = null; }
             if (!data) {
                 data = this._vertexBuffers[BABYLON.VertexBuffer.PositionKind].getData();
             }
-            this._extend = BABYLON.Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias);
+            this._extend = BABYLON.Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias, stride);
         };
         Geometry.prototype._applyToMesh = function (mesh) {
             var numOfMeshes = this._meshes.length;
@@ -292,7 +298,7 @@ var BABYLON;
                 if (numOfMeshes === 1) {
                     this._vertexBuffers[kind].create();
                 }
-                this._vertexBuffers[kind]._buffer.references = numOfMeshes;
+                this._vertexBuffers[kind].getBuffer().references = numOfMeshes;
                 if (kind === BABYLON.VertexBuffer.PositionKind) {
                     mesh._resetPointsArrayCache();
                     if (!this._extend) {
@@ -305,7 +311,7 @@ var BABYLON;
                 }
             }
             // indexBuffer
-            if (numOfMeshes === 1 && this._indices) {
+            if (numOfMeshes === 1 && this._indices && this._indices.length > 0) {
                 this._indexBuffer = this._engine.createIndexBuffer(this._indices);
             }
             if (this._indexBuffer) {
@@ -345,6 +351,37 @@ var BABYLON;
                 }
             }, function () { }, scene.database);
         };
+        /**
+         * Invert the geometry to move from a right handed system to a left handed one.
+         */
+        Geometry.prototype.toLeftHanded = function () {
+            // Flip faces
+            var tIndices = this.getIndices(false);
+            if (tIndices != null && tIndices.length > 0) {
+                for (var i = 0; i < tIndices.length; i += 3) {
+                    var tTemp = tIndices[i + 0];
+                    tIndices[i + 0] = tIndices[i + 2];
+                    tIndices[i + 2] = tTemp;
+                }
+                this.setIndices(tIndices);
+            }
+            // Negate position.z
+            var tPositions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind, false);
+            if (tPositions != null && tPositions.length > 0) {
+                for (var i = 0; i < tPositions.length; i += 3) {
+                    tPositions[i + 2] = -tPositions[i + 2];
+                }
+                this.setVerticesData(BABYLON.VertexBuffer.PositionKind, tPositions, false);
+            }
+            // Negate normal.z
+            var tNormals = this.getVerticesData(BABYLON.VertexBuffer.NormalKind, false);
+            if (tNormals != null && tNormals.length > 0) {
+                for (var i = 0; i < tNormals.length; i += 3) {
+                    tNormals[i + 2] = -tNormals[i + 2];
+                }
+                this.setVerticesData(BABYLON.VertexBuffer.NormalKind, tNormals, false);
+            }
+        };
         Geometry.prototype.isDisposed = function () {
             return this._isDisposed;
         };
@@ -359,7 +396,7 @@ var BABYLON;
             for (var kind in this._vertexBuffers) {
                 this._vertexBuffers[kind].dispose();
             }
-            this._vertexBuffers = [];
+            this._vertexBuffers = {};
             this._totalVertices = 0;
             if (this._indexBuffer) {
                 this._engine._releaseBuffer(this._indexBuffer);

+ 56 - 12
src/Mesh/babylon.geometry.ts

@@ -12,12 +12,12 @@
         private _meshes: Mesh[];
         private _totalVertices = 0;
         private _indices: number[] | Int32Array;
-        private _vertexBuffers;
+        private _vertexBuffers: { [key: string]: VertexBuffer; };
         private _isDisposed = false;
         private _extend: { minimum: Vector3, maximum: Vector3 };
         private _boundingBias: Vector2;
         public _delayInfo; //ANY
-        private _indexBuffer;
+        private _indexBuffer: WebGLBuffer;
         public _boundingInfo: BoundingInfo;
         public _delayLoadingFunction: (any: any, geometry: Geometry) => void;
         public _softwareSkinningRenderId: number;
@@ -92,18 +92,26 @@
         }
 
         public setVerticesData(kind: string, data: number[] | Float32Array, updatable?: boolean, stride?: number): void {
+            var buffer = new VertexBuffer(this._engine, data, kind, updatable, this._meshes.length === 0, stride);
+
+            this.setVerticesBuffer(buffer);
+        }
+
+        public setVerticesBuffer(buffer: VertexBuffer): void {
+            var kind = buffer.getKind();
             if (this._vertexBuffers[kind]) {
                 this._vertexBuffers[kind].dispose();
             }
 
-            this._vertexBuffers[kind] = new VertexBuffer(this._engine, data, kind, updatable, this._meshes.length === 0, stride);
+            this._vertexBuffers[kind] = buffer;
 
             if (kind === VertexBuffer.PositionKind) {
-                stride = this._vertexBuffers[kind].getStrideSize();
+                var data = buffer.getData();
+                var stride = buffer.getStrideSize();
 
                 this._totalVertices = data.length / stride;
 
-                this.updateExtend(data);
+                this.updateExtend(data, stride);
 
                 var meshes = this._meshes;
                 var numOfMeshes = meshes.length;
@@ -116,6 +124,7 @@
                     mesh.computeWorldMatrix(true);
                 }
             }
+
             this.notifyUpdate(kind);
         }
 
@@ -205,7 +214,7 @@
             return this._vertexBuffers[kind];
         }
 
-        public getVertexBuffers(): VertexBuffer[] {
+        public getVertexBuffers(): { [key: string]: VertexBuffer; } {
             if (!this.isReady()) {
                 return null;
             }
@@ -285,7 +294,7 @@
             }
         }
 
-        public getIndexBuffer(): any {
+        public getIndexBuffer(): WebGLBuffer {
             if (!this.isReady()) {
                 return null;
             }
@@ -344,12 +353,12 @@
             }
         }
 
-        private updateExtend(data = null) {
+        private updateExtend(data = null, stride? : number) {
             if (!data) {
                 data = this._vertexBuffers[VertexBuffer.PositionKind].getData();
             }
 
-            this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias);
+            this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias, stride);
         }
 
         private _applyToMesh(mesh: Mesh): void {
@@ -360,7 +369,7 @@
                 if (numOfMeshes === 1) {
                     this._vertexBuffers[kind].create();
                 }
-                this._vertexBuffers[kind]._buffer.references = numOfMeshes;
+                this._vertexBuffers[kind].getBuffer().references = numOfMeshes;
 
                 if (kind === VertexBuffer.PositionKind) {
                     mesh._resetPointsArrayCache();
@@ -378,7 +387,7 @@
             }
 
             // indexBuffer
-            if (numOfMeshes === 1 && this._indices) {
+            if (numOfMeshes === 1 && this._indices && this._indices.length > 0) {
                 this._indexBuffer = this._engine.createIndexBuffer(this._indices);
             }
             if (this._indexBuffer) {
@@ -427,6 +436,41 @@
             }, () => { }, scene.database);
         }
 
+        /**
+         * Invert the geometry to move from a right handed system to a left handed one.
+         */
+        public toLeftHanded(): void {
+
+            // Flip faces
+            let tIndices = this.getIndices(false);
+            if (tIndices != null && tIndices.length > 0) {
+                for (let i = 0; i < tIndices.length; i += 3) {
+                    let tTemp = tIndices[i + 0];
+                    tIndices[i + 0] = tIndices[i + 2];
+                    tIndices[i + 2] = tTemp;
+                }
+                this.setIndices(tIndices);
+            }
+
+            // Negate position.z
+            let tPositions = this.getVerticesData(VertexBuffer.PositionKind, false);
+            if (tPositions != null && tPositions.length > 0) {
+                for (let i = 0; i < tPositions.length; i += 3) {
+                    tPositions[i + 2] = -tPositions[i + 2];
+                }
+                this.setVerticesData(VertexBuffer.PositionKind, tPositions, false);
+            }
+
+            // Negate normal.z
+            let tNormals = this.getVerticesData(VertexBuffer.NormalKind, false);
+            if (tNormals != null && tNormals.length > 0) {
+                for (let i = 0; i < tNormals.length; i += 3) {
+                    tNormals[i + 2] = -tNormals[i + 2];
+                }
+                this.setVerticesData(VertexBuffer.NormalKind, tNormals, false);
+            }
+        }
+
         public isDisposed(): boolean {
             return this._isDisposed;
         }
@@ -443,7 +487,7 @@
             for (var kind in this._vertexBuffers) {
                 this._vertexBuffers[kind].dispose();
             }
-            this._vertexBuffers = [];
+            this._vertexBuffers = {};
             this._totalVertices = 0;
 
             if (this._indexBuffer) {

+ 5 - 3
src/Mesh/babylon.linesMesh.js

@@ -12,16 +12,18 @@ var BABYLON;
             _super.call(this, name, scene, parent, source, doNotCloneChildren);
             this.color = new BABYLON.Color3(1, 1, 1);
             this.alpha = 1;
+            this._positionBuffer = {};
             if (source) {
                 this.color = source.color.clone();
                 this.alpha = source.alpha;
             }
             this._intersectionThreshold = 0.1;
             this._colorShader = new BABYLON.ShaderMaterial("colorShader", scene, "color", {
-                attributes: ["position"],
+                attributes: [BABYLON.VertexBuffer.PositionKind],
                 uniforms: ["worldViewProjection", "color"],
                 needAlphaBlending: true
             });
+            this._positionBuffer[BABYLON.VertexBuffer.PositionKind] = null;
         }
         Object.defineProperty(LinesMesh.prototype, "intersectionThreshold", {
             /**
@@ -70,9 +72,9 @@ var BABYLON;
         };
         LinesMesh.prototype._bind = function (subMesh, effect, fillMode) {
             var engine = this.getScene().getEngine();
-            var indexToBind = this._geometry.getIndexBuffer();
+            this._positionBuffer[BABYLON.VertexBuffer.PositionKind] = this._geometry.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
             // VBOs
-            engine.bindBuffers(this._geometry.getVertexBuffer(BABYLON.VertexBuffer.PositionKind).getBuffer(), indexToBind, [3], 3 * 4, this._colorShader.getEffect());
+            engine.bindBuffers(this._positionBuffer, this._geometry.getIndexBuffer(), this._colorShader.getEffect());
             // Color
             this._colorShader.setColor4("color", this.color.toColor4(this.alpha));
         };

+ 7 - 3
src/Mesh/babylon.linesMesh.ts

@@ -3,6 +3,8 @@
         public color = new Color3(1, 1, 1);
         public alpha = 1;
 
+        private _positionBuffer: { [key: string]: VertexBuffer } = {};
+
         /**
          * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
          * This margin is expressed in world space coordinates, so its value may vary.
@@ -43,10 +45,12 @@
             this._intersectionThreshold = 0.1;
             this._colorShader = new ShaderMaterial("colorShader", scene, "color",
                 {
-                    attributes: ["position"],
+                    attributes: [VertexBuffer.PositionKind],
                     uniforms: ["worldViewProjection", "color"],
                     needAlphaBlending: true
                 });
+
+            this._positionBuffer[VertexBuffer.PositionKind] = null;
         }
 
         public get material(): Material {
@@ -65,10 +69,10 @@
         public _bind(subMesh: SubMesh, effect: Effect, fillMode: number): void {
             var engine = this.getScene().getEngine();
 
-            var indexToBind = this._geometry.getIndexBuffer();
+            this._positionBuffer[VertexBuffer.PositionKind] = this._geometry.getVertexBuffer(VertexBuffer.PositionKind);
 
             // VBOs
-            engine.bindBuffers(this._geometry.getVertexBuffer(VertexBuffer.PositionKind).getBuffer(), indexToBind, [3], 3 * 4, this._colorShader.getEffect());
+            engine.bindBuffers(this._positionBuffer, this._geometry.getIndexBuffer(), this._colorShader.getEffect());
 
             // Color
             this._colorShader.setColor4("color", this.color.toColor4(this.alpha));

+ 125 - 83
src/Mesh/babylon.mesh.js

@@ -18,11 +18,11 @@ var BABYLON;
         __extends(Mesh, _super);
         /**
          * @constructor
-         * @param {string} name - The value used by scene.getMeshByName() to do a lookup.
-         * @param {Scene} scene - The scene to add this mesh to.
-         * @param {Node} parent - The parent of this mesh, if it has one
-         * @param {Mesh} source - An optional Mesh from which geometry is shared, cloned.
-         * @param {boolean} doNotCloneChildren - When cloning, skip cloning child meshes of source, default False.
+         * @param {string} name The value used by scene.getMeshByName() to do a lookup.
+         * @param {Scene} scene The scene to add this mesh to.
+         * @param {Node} parent The parent of this mesh, if it has one
+         * @param {Mesh} source An optional Mesh from which geometry is shared, cloned.
+         * @param {boolean} doNotCloneChildren When cloning, skip cloning child meshes of source, default False.
          *                  When false, achieved by calling a clone(), also passing False.
          *                  This will make creation of children, recursive.
          */
@@ -230,9 +230,9 @@ var BABYLON;
         /**
          * Add a mesh as LOD level triggered at the given distance.
          * tuto : http://doc.babylonjs.com/tutorials/How_to_use_LOD
-         * @param {number} distance - the distance from the center of the object to show this level
-         * @param {Mesh} mesh - the mesh to be added as LOD level
-         * @return {Mesh} this mesh (for chaining)
+         * @param {number} distance The distance from the center of the object to show this level
+         * @param {Mesh} mesh The mesh to be added as LOD level
+         * @return {Mesh} This mesh (for chaining)
          */
         Mesh.prototype.addLODLevel = function (distance, mesh) {
             if (mesh && mesh._masterMesh) {
@@ -264,8 +264,8 @@ var BABYLON;
         /**
          * Remove a mesh from the LOD array
          * tuto : http://doc.babylonjs.com/tutorials/How_to_use_LOD
-         * @param {Mesh} mesh - the mesh to be removed.
-         * @return {Mesh} this mesh (for chaining)
+         * @param {Mesh} mesh The mesh to be removed.
+         * @return {Mesh} This mesh (for chaining)
          */
         Mesh.prototype.removeLODLevel = function (mesh) {
             for (var index = 0; index < this._LODLevels.length; index++) {
@@ -314,7 +314,7 @@ var BABYLON;
         };
         Object.defineProperty(Mesh.prototype, "geometry", {
             /**
-             * Returns the mesh internal `Geometry` object.
+             * Returns the mesh internal Geometry object.
              */
             get: function () {
                 return this._geometry;
@@ -356,7 +356,7 @@ var BABYLON;
             return this._geometry.getVerticesData(kind, copyWhenShared);
         };
         /**
-         * Returns the mesh `VertexBuffer` object from the requested `kind` : positions, indices, normals, etc.
+         * Returns the mesh VertexBuffer object from the requested `kind` : positions, indices, normals, etc.
          * Returns `undefined` if the mesh has no geometry.
          * Possible `kind` values :
          * - BABYLON.VertexBuffer.PositionKind
@@ -515,6 +515,16 @@ var BABYLON;
         Mesh.prototype.unfreezeNormals = function () {
             this._areNormalsFrozen = false;
         };
+        Object.defineProperty(Mesh.prototype, "overridenInstanceCount", {
+            /**
+             * Overrides instance count. Only applicable when custom instanced InterleavedVertexBuffer are used rather than InstancedMeshs
+             */
+            set: function (count) {
+                this._overridenInstanceCount = count;
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Methods
         Mesh.prototype._preActivate = function () {
             var sceneRenderId = this.getScene().getRenderId();
@@ -541,7 +551,7 @@ var BABYLON;
             this._visibleInstances[renderId].push(instance);
         };
         /**
-         * This method recomputes and sets a new `BoundingInfo` to the mesh unless it is locked.
+         * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
          * This means the mesh underlying bounding box and sphere are recomputed.
          */
         Mesh.prototype.refreshBoundingInfo = function () {
@@ -591,12 +601,12 @@ var BABYLON;
         };
         /**
          * Sets the vertex data of the mesh geometry for the requested `kind`.
-         * If the mesh has no geometry, a new `Geometry` object is set to the mesh and then passed this vertex data.
+         * If the mesh has no geometry, a new Geometry object is set to the mesh and then passed this vertex data.
          * The `data` are either a numeric array either a Float32Array.
-         * The parameter `updatable` is passed as is to the underlying `Geometry` object constructor (if initianilly none) or updater.
+         * The parameter `updatable` is passed as is to the underlying Geometry object constructor (if initianilly none) or updater.
          * The parameter `stride` is an optional positive integer, it is usually automatically deducted from the `kind` (3 for positions or normals, 2 for UV, etc).
-         * Note that a new underlying `VertexBuffer` object is created each call.
-         * If the `kind` is the `PositionKind`, the mesh `BoundingInfo` is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
+         * Note that a new underlying VertexBuffer object is created each call.
+         * If the `kind` is the `PositionKind`, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
          *
          * Possible `kind` values :
          * - BABYLON.VertexBuffer.PositionKind
@@ -623,12 +633,19 @@ var BABYLON;
                 this._geometry.setVerticesData(kind, data, updatable, stride);
             }
         };
+        Mesh.prototype.setVerticesBuffer = function (buffer) {
+            if (!this._geometry) {
+                var scene = this.getScene();
+                new BABYLON.Geometry(BABYLON.Geometry.RandomId(), scene).applyToMesh(this);
+            }
+            this._geometry.setVerticesBuffer(buffer);
+        };
         /**
          * Updates the existing vertex data of the mesh geometry for the requested `kind`.
          * If the mesh has no geometry, it is simply returned as it is.
          * The `data` are either a numeric array either a Float32Array.
-         * No new underlying `VertexBuffer` object is created.
-         * If the `kind` is the `PositionKind` and if `updateExtends` is true, the mesh `BoundingInfo` is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
+         * No new underlying VertexBuffer object is created.
+         * If the `kind` is the `PositionKind` and if `updateExtends` is true, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
          * If the parameter `makeItUnique` is true, a new global geometry is created from this positions and is set to the mesh.
          *
          * Possible `kind` values :
@@ -715,6 +732,15 @@ var BABYLON;
                 this._geometry.setIndices(indices, totalVertices);
             }
         };
+        /**
+         * Invert the geometry to move from a right handed system to a left handed one.
+         */
+        Mesh.prototype.toLeftHanded = function () {
+            if (!this._geometry) {
+                return;
+            }
+            this._geometry.toLeftHanded();
+        };
         Mesh.prototype._bind = function (subMesh, effect, fillMode) {
             var engine = this.getScene().getEngine();
             // Wireframe
@@ -737,7 +763,7 @@ var BABYLON;
                 }
             }
             // VBOs
-            engine.bindMultiBuffers(this._geometry.getVertexBuffers(), indexToBind, effect);
+            engine.bindBuffers(this._geometry.getVertexBuffers(), indexToBind, effect);
         };
         Mesh.prototype._draw = function (subMesh, fillMode, instancesCount) {
             if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
@@ -827,40 +853,47 @@ var BABYLON;
             var visibleInstances = batch.visibleInstances[subMesh._id];
             var matricesCount = visibleInstances.length + 1;
             var bufferSize = matricesCount * 16 * 4;
+            var currentInstancesBufferSize = this._instancesBufferSize;
+            var instancesBuffer = this._instancesBuffer;
             while (this._instancesBufferSize < bufferSize) {
                 this._instancesBufferSize *= 2;
             }
-            if (!this._worldMatricesInstancesBuffer || this._worldMatricesInstancesBuffer.capacity < this._instancesBufferSize) {
-                if (this._worldMatricesInstancesBuffer) {
-                    engine.deleteInstancesBuffer(this._worldMatricesInstancesBuffer);
-                }
-                this._worldMatricesInstancesBuffer = engine.createInstancesBuffer(this._instancesBufferSize);
-                this._worldMatricesInstancesArray = new Float32Array(this._instancesBufferSize / 4);
+            if (!this._instancesData || currentInstancesBufferSize != this._instancesBufferSize) {
+                this._instancesData = new Float32Array(this._instancesBufferSize / 4);
             }
             var offset = 0;
             var instancesCount = 0;
             var world = this.getWorldMatrix();
             if (batch.renderSelf[subMesh._id]) {
-                world.copyToArray(this._worldMatricesInstancesArray, offset);
+                world.copyToArray(this._instancesData, offset);
                 offset += 16;
                 instancesCount++;
             }
             if (visibleInstances) {
                 for (var instanceIndex = 0; instanceIndex < visibleInstances.length; instanceIndex++) {
                     var instance = visibleInstances[instanceIndex];
-                    instance.getWorldMatrix().copyToArray(this._worldMatricesInstancesArray, offset);
+                    instance.getWorldMatrix().copyToArray(this._instancesData, offset);
                     offset += 16;
                     instancesCount++;
                 }
             }
-            var offsetLocation0 = effect.getAttributeLocationByName("world0");
-            var offsetLocation1 = effect.getAttributeLocationByName("world1");
-            var offsetLocation2 = effect.getAttributeLocationByName("world2");
-            var offsetLocation3 = effect.getAttributeLocationByName("world3");
-            var offsetLocations = [offsetLocation0, offsetLocation1, offsetLocation2, offsetLocation3];
-            engine.updateAndBindInstancesBuffer(this._worldMatricesInstancesBuffer, this._worldMatricesInstancesArray, offsetLocations);
+            if (!instancesBuffer || currentInstancesBufferSize != this._instancesBufferSize) {
+                if (instancesBuffer) {
+                    instancesBuffer.dispose();
+                }
+                instancesBuffer = new BABYLON.Buffer(engine, this._instancesData, true, 16, false, true);
+                this._instancesBuffer = instancesBuffer;
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world0", 0, 4));
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world1", 4, 4));
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world2", 8, 4));
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world3", 12, 4));
+                engine.bindBuffers(this.geometry.getVertexBuffers(), this.geometry.getIndexBuffer(), effect);
+            }
+            else {
+                instancesBuffer.updateDirectly(this._instancesData, 0, instancesCount);
+            }
             this._draw(subMesh, fillMode, instancesCount);
-            engine.unBindInstancesBuffer(this._worldMatricesInstancesBuffer, offsetLocations);
+            engine.unbindInstanceAttributes();
         };
         Mesh.prototype._processRendering = function (subMesh, effect, fillMode, batch, hardwareInstancedRendering, onBeforeDraw) {
             var scene = this.getScene();
@@ -874,7 +907,7 @@ var BABYLON;
                     if (onBeforeDraw) {
                         onBeforeDraw(false, this.getWorldMatrix());
                     }
-                    this._draw(subMesh, fillMode);
+                    this._draw(subMesh, fillMode, this._overridenInstanceCount);
                 }
                 if (batch.visibleInstances[subMesh._id]) {
                     for (var instanceIndex = 0; instanceIndex < batch.visibleInstances[subMesh._id].length; instanceIndex++) {
@@ -957,7 +990,7 @@ var BABYLON;
             this.onAfterRenderObservable.notifyObservers(this);
         };
         /**
-         * Returns an array populated with `ParticleSystem` objects whose the mesh is the emitter.
+         * Returns an array populated with ParticleSystem objects whose the mesh is the emitter.
          */
         Mesh.prototype.getEmittedParticleSystems = function () {
             var results = new Array();
@@ -970,7 +1003,7 @@ var BABYLON;
             return results;
         };
         /**
-         * Returns an array populated with `ParticleSystem` objects whose the mesh or its children are the emitter.
+         * Returns an array populated with ParticleSystem objects whose the mesh or its children are the emitter.
          */
         Mesh.prototype.getHierarchyEmittedParticleSystems = function () {
             var results = new Array();
@@ -1008,7 +1041,7 @@ var BABYLON;
             }
         };
         /**
-         * Boolean, true is the mesh in the frustum defined by the `Plane` objects from the `frustumPlanes` array parameter.
+         * Boolean, true is the mesh in the frustum defined by the Plane objects from the `frustumPlanes` array parameter.
          */
         Mesh.prototype.isInFrustum = function (frustumPlanes) {
             if (this.delayLoadState === BABYLON.Engine.DELAYLOADSTATE_LOADING) {
@@ -1131,7 +1164,7 @@ var BABYLON;
             return true;
         };
         /**
-         * Returns a new `Mesh` object generated from the current mesh properties.
+         * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().
          * The parameter `name` is a string, the name given to the new mesh.
          * The optional parameter `newParent` can be any `Node` object (default `null`).
@@ -1151,9 +1184,9 @@ var BABYLON;
                 this._geometry.releaseForMesh(this, true);
             }
             // Instances
-            if (this._worldMatricesInstancesBuffer) {
-                this.getEngine().deleteInstancesBuffer(this._worldMatricesInstancesBuffer);
-                this._worldMatricesInstancesBuffer = null;
+            if (this._instancesBuffer) {
+                this._instancesBuffer.dispose();
+                this._instancesBuffer = null;
             }
             while (this.instances.length) {
                 this.instances[0].dispose();
@@ -1401,7 +1434,7 @@ var BABYLON;
         };
         // Instances
         /**
-         * Creates a new `InstancedMesh` object from the mesh model.
+         * Creates a new InstancedMesh object from the mesh model.
          * An instance shares the same properties and the same material than its model.
          * Only these properties of each instance can then be set individually :
          * - position
@@ -1410,7 +1443,7 @@ var BABYLON;
          * - setPivotMatrix
          * - scaling
          * tuto : http://doc.babylonjs.com/tutorials/How_to_use_Instances
-         * Warning : this method is not supported for `Line` mesh and `LineSystem`
+         * Warning : this method is not supported for Line mesh and LineSystem
          */
         Mesh.prototype.createInstance = function (name) {
             return new BABYLON.InstancedMesh(name, this);
@@ -1485,7 +1518,7 @@ var BABYLON;
         };
         // 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.
          * The parameter `parsedMesh` is the mesh to be copied.
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
@@ -1653,7 +1686,7 @@ var BABYLON;
         };
         /**
          * Creates a ribbon mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The ribbon is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
          *
          * Please read this full tutorial to understand how to design a ribbon : http://doc.babylonjs.com/tutorials/Ribbon_Tutorial
@@ -1680,7 +1713,7 @@ var BABYLON;
         };
         /**
          * Creates a plane polygonal mesh.  By default, this is a disc.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `radius` sets the radius size (float) of the polygon (default 0.5).
          * The parameter `tessellation` sets the number of polygon sides (positive integer, default 64). So a tessellation valued to 3 will build a triangle, to 4 a square, etc.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
@@ -1698,7 +1731,7 @@ var BABYLON;
         };
         /**
          * Creates a box mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `size` sets the size (float) of each box side (default 1).
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
@@ -1714,7 +1747,7 @@ var BABYLON;
         };
         /**
          * Creates a sphere mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `diameter` sets the diameter size (float) of the sphere (default 1).
          * The parameter `segments` sets the sphere number of horizontal stripes (positive integer, default 32).
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
@@ -1734,7 +1767,7 @@ var BABYLON;
         };
         /**
          * Creates a cylinder or a cone mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `height` sets the height size (float) of the cylinder/cone (float, default 2).
          * The parameter `diameter` sets the diameter of the top and bottom cap at once (float, default 1).
          * The parameters `diameterTop` and `diameterBottom` overwrite the parameter `diameter` and set respectively the top cap and bottom cap diameter (floats, default 1). The parameter "diameterBottom" can't be zero.
@@ -1767,7 +1800,7 @@ var BABYLON;
         // Torus  (Code from SharpDX.org)
         /**
          * Creates a torus mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `diameter` sets the diameter size (float) of the torus (default 1).
          * The parameter `thickness` sets the diameter size of the tube of the torus (float, default 0.5).
          * The parameter `tessellation` sets the number of torus sides (postive integer, default 16).
@@ -1787,7 +1820,7 @@ var BABYLON;
         };
         /**
          * Creates a torus knot mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `radius` sets the global radius size (float) of the torus knot (default 2).
          * The parameter `radialSegments` sets the number of sides on each tube segments (positive integer, default 32).
          * The parameter `tubularSegments` sets the number of tubes to decompose the knot into (positive integer, default 32).
@@ -1811,7 +1844,7 @@ var BABYLON;
         };
         /**
          * Creates a line mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * A line mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of points as an input parameter.
          * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineMesh to this static function.
          * The parameter `points` is an array successive Vector3.
@@ -1829,7 +1862,7 @@ var BABYLON;
         };
         /**
          * Creates a dashed line mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * A dashed line mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of points as an input parameter.
          * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineMesh to this static function.
          * The parameter `points` is an array successive Vector3.
@@ -1853,7 +1886,7 @@ var BABYLON;
         /**
          * Creates an extruded shape mesh.
          * The extrusion is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          *
          * Please read this full tutorial to understand how to design an extruded shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes#extrusion
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be extruded in its local space : the shape must be designed in the xOy plane and will be
@@ -1884,7 +1917,7 @@ var BABYLON;
         /**
          * Creates an custom extruded shape mesh.
          * The custom extrusion is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          *
          * Please read this full tutorial to understand how to design a custom extruded shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes#extrusion
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be extruded in its local space : the shape must be designed in the xOy plane and will be
@@ -1892,15 +1925,19 @@ var BABYLON;
          * The parameter `path` is a required array of successive Vector3. This is the axis curve the shape is extruded along.
          * The parameter `rotationFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path
          * and the distance of this point from the begining of the path :
-         * ```rotationFunction = function(i, distance) {
-         *  // do things
-         *  return rotationValue; }```
+         * ```javascript
+         * var rotationFunction = function(i, distance) {
+         *     // do things
+         *     return rotationValue; }
+         * ```
          * It must returns a float value that will be the rotation in radians applied to the shape on each path point.
          * The parameter `scaleFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path
          * and the distance of this point from the begining of the path :
-         * ````scaleFunction = function(i, distance) {
-         *   // do things
-         *  return scaleValue;}```
+         * ```javascript
+         * var scaleFunction = function(i, distance) {
+         *     // do things
+         *    return scaleValue;}
+         * ```
          * It must returns a float value that will be the scale value applied to the shape on each path point.
          * The parameter `ribbonClosePath` (boolean, default false) forces the extrusion underlying ribbon to close all the paths in its `pathArray`.
          * The parameter `ribbonCloseArray` (boolean, default false) forces the extrusion underlying ribbon to close its `pathArray`.
@@ -1929,7 +1966,7 @@ var BABYLON;
         /**
          * Creates lathe mesh.
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          *
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.
@@ -1951,7 +1988,7 @@ var BABYLON;
         };
         /**
          * Creates a plane mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `size` sets the size (float) of both sides of the plane at once (default 1).
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
@@ -1969,7 +2006,7 @@ var BABYLON;
         };
         /**
          * Creates a ground mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameters `width` and `height` (floats, default 1) set the width and height sizes of the ground.
          * The parameter `subdivisions` (positive integer) sets the number of subdivisions per side.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
@@ -1985,7 +2022,7 @@ var BABYLON;
         };
         /**
          * Creates a tiled ground mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameters `xmin` and `xmax` (floats, default -1 and 1) set the ground minimum and maximum X coordinates.
          * The parameters `zmin` and `zmax` (floats, default -1 and 1) set the ground minimum and maximum Z coordinates.
          * The parameter `subdivisions` is a javascript object `{w: positive integer, h: positive integer}` (default `{w: 6, h: 6}`). `w` and `h` are the
@@ -2009,15 +2046,18 @@ var BABYLON;
         /**
          * Creates a ground mesh from a height map.
          * tuto : http://doc.babylonjs.com/tutorials/14._Height_Map
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `url` sets the URL of the height map image resource.
          * The parameters `width` and `height` (positive floats, default 10) set the ground width and height sizes.
          * The parameter `subdivisions` (positive integer, default 1) sets the number of subdivision per side.
          * The parameter `minHeight` (float, default 0) is the minimum altitude on the ground.
          * The parameter `maxHeight` (float, default 1) is the maximum altitude on the ground.
          * The parameter `onReady` is a javascript callback function that will be called  once the mesh is just built (the height map download can last some time).
-         * This function is passed the newly built mesh : ```function(mesh) { // do things
-         * return; }```
+         * This function is passed the newly built mesh :
+         * ```javascript
+         * function(mesh) { // do things
+         *     return; }
+         * ```
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         Mesh.CreateGroundFromHeightMap = function (name, url, width, height, subdivisions, minHeight, maxHeight, scene, updatable, onReady) {
@@ -2036,16 +2076,18 @@ var BABYLON;
          * Creates a tube mesh.
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
          *
-         * Please consider using the same method from the `MeshBuilder` class instead.
-         * The parameter `path` is a required array of successive `Vector3`. It is the curve used as the axis of the tube.
+         * Please consider using the same method from the MeshBuilder class instead.
+         * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.
          * The parameter `radius` (positive float, default 1) sets the tube radius size.
          * The parameter `tessellation` (positive float, default 64) is the number of sides on the tubular surface.
          * The parameter `radiusFunction` (javascript function, default null) is a vanilla javascript function. If it is not null, it overwrittes the parameter `radius`.
          * This function is called on each point of the tube path and is passed the index `i` of the i-th point and the distance of this point from the first point of the path.
          * It must return a radius value (positive float) :
-         * ```var radiusFunction = function(i, distance) {
-         *   // do things
-         *   return radius; }```
+         * ```javascript
+         * var radiusFunction = function(i, distance) {
+         *     // do things
+         *     return radius; }
+         * ```
          * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL
          * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#tube
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
@@ -2069,14 +2111,14 @@ var BABYLON;
         /**
          * Creates a polyhedron mesh.
          *
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          *  to choose the wanted type.
          * The parameter `size` (positive float, default 1) sets the polygon size.
          * You can overwrite the `size` on each dimension bu using the parameters `sizeX`, `sizeY` or `sizeZ` (positive floats, default to `size` value).
          * You can build other polyhedron types than the 15 embbeded ones by setting the parameter `custom` (`polyhedronObject`, default null). If you set the parameter `custom`, this overwrittes the parameter `type`.
          * A `polyhedronObject` is a formatted javascript object. You'll find a full file with pre-set polyhedra here : https://github.com/BabylonJS/Extensions/tree/master/Polyhedron
-         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (`Color4`, default `(1, 1, 1, 1)`) and faceUV (`Vector4`, default `(0, 0, 1, 1)`).
+         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (Color4, default `(1, 1, 1, 1)`) and faceUV (Vector4, default `(0, 0, 1, 1)`).
          * To understand how to set `faceUV` or `faceColors`, please read this by considering the right number of faces of your polyhedron, instead of only 6 for the box : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors
          * The parameter `flat` (boolean, default true). If set to false, it gives the polyhedron a single global face, so less vertices and shared normals. In this case, `faceColors` and `faceUV` are ignored.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
@@ -2088,7 +2130,7 @@ var BABYLON;
         };
         /**
          * Creates a sphere based upon an icosahedron with 20 triangular faces which can be subdivided.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `radius` sets the radius size (float) of the icosphere (default 1).
          * You can set some different icosphere dimensions, for instance to build an ellipsoid, by using the parameters `radiusX`, `radiusY` and `radiusZ` (all by default have the same value than `radius`).
          * The parameter `subdivisions` sets the number of subdivisions (postive integer, default 4). The more subdivisions, the more faces on the icosphere whatever its size.
@@ -2102,11 +2144,11 @@ var BABYLON;
         };
         /**
          * Creates a decal mesh.
-         * Please consider using the same method from the `MeshBuilder` class instead.
+         * Please consider using the same method from the MeshBuilder class instead.
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.
-         * The parameter `position` (`Vector3`, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
-         * The parameter `normal` (`Vector3`, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.
-         * The parameter `size` (`Vector3`, default `(1, 1, 1)`) sets the decal scaling.
+         * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
+         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.
+         * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.
          */
         Mesh.CreateDecal = function (name, sourceMesh, position, normal, size, angle) {
@@ -2233,7 +2275,7 @@ var BABYLON;
         // Tools
         /**
          * Returns an object `{min: Vector3, max: Vector3}`
-         * This min and max `Vector3` are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
+         * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          */
         Mesh.MinMax = function (meshes) {
             var minVector = null;

+ 133 - 87
src/Mesh/babylon.mesh.ts

@@ -104,12 +104,15 @@
         public _geometry: Geometry;
         public _delayInfo; //ANY
         public _delayLoadingFunction: (any: any, mesh: Mesh) => void;
+
         public _visibleInstances: any = {};
         private _renderIdForInstances = new Array<number>();
         private _batchCache = new _InstancesBatch();
-        private _worldMatricesInstancesBuffer: WebGLBuffer;
-        private _worldMatricesInstancesArray: Float32Array;
         private _instancesBufferSize = 32 * 16 * 4; // let's start with a maximum of 32 instances
+        private _instancesBuffer: Buffer;
+        private _instancesData: Float32Array;
+        private _overridenInstanceCount: number;
+
         public _shouldGenerateFlatShading: boolean;
         private _preActivateId: number;
         private _sideOrientation: number = Mesh._DEFAULTSIDE;
@@ -120,11 +123,11 @@
 
         /**
          * @constructor
-         * @param {string} name - The value used by scene.getMeshByName() to do a lookup.
-         * @param {Scene} scene - The scene to add this mesh to.
-         * @param {Node} parent - The parent of this mesh, if it has one
-         * @param {Mesh} source - An optional Mesh from which geometry is shared, cloned.
-         * @param {boolean} doNotCloneChildren - When cloning, skip cloning child meshes of source, default False.
+         * @param {string} name The value used by scene.getMeshByName() to do a lookup.
+         * @param {Scene} scene The scene to add this mesh to.
+         * @param {Node} parent The parent of this mesh, if it has one
+         * @param {Mesh} source An optional Mesh from which geometry is shared, cloned.
+         * @param {boolean} doNotCloneChildren When cloning, skip cloning child meshes of source, default False.
          *                  When false, achieved by calling a clone(), also passing False.
          *                  This will make creation of children, recursive.
          */
@@ -228,9 +231,9 @@
         /**
          * Add a mesh as LOD level triggered at the given distance.
          * tuto : http://doc.babylonjs.com/tutorials/How_to_use_LOD
-         * @param {number} distance - the distance from the center of the object to show this level
-         * @param {Mesh} mesh - the mesh to be added as LOD level
-         * @return {Mesh} this mesh (for chaining)
+         * @param {number} distance The distance from the center of the object to show this level
+         * @param {Mesh} mesh The mesh to be added as LOD level
+         * @return {Mesh} This mesh (for chaining)
          */
         public addLODLevel(distance: number, mesh: Mesh): Mesh {
             if (mesh && mesh._masterMesh) {
@@ -268,8 +271,8 @@
         /**
          * Remove a mesh from the LOD array
          * tuto : http://doc.babylonjs.com/tutorials/How_to_use_LOD
-         * @param {Mesh} mesh - the mesh to be removed.
-         * @return {Mesh} this mesh (for chaining)
+         * @param {Mesh} mesh The mesh to be removed.
+         * @return {Mesh} This mesh (for chaining)
          */
         public removeLODLevel(mesh: Mesh): Mesh {
 
@@ -326,7 +329,7 @@
             return this;
         }
         /**
-         * Returns the mesh internal `Geometry` object.  
+         * Returns the mesh internal Geometry object.  
          */
         public get geometry(): Geometry {
             return this._geometry;
@@ -368,7 +371,7 @@
         }
 
         /**
-         * Returns the mesh `VertexBuffer` object from the requested `kind` : positions, indices, normals, etc.
+         * Returns the mesh VertexBuffer object from the requested `kind` : positions, indices, normals, etc.
          * Returns `undefined` if the mesh has no geometry.   
          * Possible `kind` values :
          * - BABYLON.VertexBuffer.PositionKind
@@ -529,6 +532,13 @@
             this._areNormalsFrozen = false;
         }
 
+        /**
+         * Overrides instance count. Only applicable when custom instanced InterleavedVertexBuffer are used rather than InstancedMeshs
+         */
+        public set overridenInstanceCount(count: number) {
+            this._overridenInstanceCount = count;
+        }
+
         // Methods
         public _preActivate(): void {
             var sceneRenderId = this.getScene().getRenderId();
@@ -561,7 +571,7 @@
         }
 
         /**
-         * This method recomputes and sets a new `BoundingInfo` to the mesh unless it is locked.
+         * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
          * This means the mesh underlying bounding box and sphere are recomputed. 
          */
         public refreshBoundingInfo(): void {
@@ -624,12 +634,12 @@
 
         /**
          * Sets the vertex data of the mesh geometry for the requested `kind`.
-         * If the mesh has no geometry, a new `Geometry` object is set to the mesh and then passed this vertex data.  
+         * If the mesh has no geometry, a new Geometry object is set to the mesh and then passed this vertex data.  
          * The `data` are either a numeric array either a Float32Array. 
-         * The parameter `updatable` is passed as is to the underlying `Geometry` object constructor (if initianilly none) or updater. 
+         * The parameter `updatable` is passed as is to the underlying Geometry object constructor (if initianilly none) or updater. 
          * The parameter `stride` is an optional positive integer, it is usually automatically deducted from the `kind` (3 for positions or normals, 2 for UV, etc).  
-         * Note that a new underlying `VertexBuffer` object is created each call. 
-         * If the `kind` is the `PositionKind`, the mesh `BoundingInfo` is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed. 
+         * Note that a new underlying VertexBuffer object is created each call. 
+         * If the `kind` is the `PositionKind`, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed. 
          *
          * Possible `kind` values :
          * - BABYLON.VertexBuffer.PositionKind
@@ -659,12 +669,22 @@
             }
         }
 
+        public setVerticesBuffer(buffer: VertexBuffer): void {
+            if (!this._geometry) {
+                var scene = this.getScene();
+
+                new Geometry(Geometry.RandomId(), scene).applyToMesh(this);
+            }
+
+            this._geometry.setVerticesBuffer(buffer);
+        }
+
         /**
          * Updates the existing vertex data of the mesh geometry for the requested `kind`.
          * If the mesh has no geometry, it is simply returned as it is.  
          * The `data` are either a numeric array either a Float32Array. 
-         * No new underlying `VertexBuffer` object is created. 
-         * If the `kind` is the `PositionKind` and if `updateExtends` is true, the mesh `BoundingInfo` is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.  
+         * No new underlying VertexBuffer object is created. 
+         * If the `kind` is the `PositionKind` and if `updateExtends` is true, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.  
          * If the parameter `makeItUnique` is true, a new global geometry is created from this positions and is set to the mesh.
          *
          * Possible `kind` values :
@@ -759,6 +779,17 @@
             }
         }
 
+        /**
+         * Invert the geometry to move from a right handed system to a left handed one.
+         */
+        public toLeftHanded(): void {
+            if (!this._geometry) {
+                return;
+            }
+
+            this._geometry.toLeftHanded();
+        }
+
         public _bind(subMesh: SubMesh, effect: Effect, fillMode: number): void {
             var engine = this.getScene().getEngine();
 
@@ -783,7 +814,7 @@
             }
 
             // VBOs
-            engine.bindMultiBuffers(this._geometry.getVertexBuffers(), indexToBind, effect);
+            engine.bindBuffers(this._geometry.getVertexBuffers(), indexToBind, effect);
         }
 
         public _draw(subMesh: SubMesh, fillMode: number, instancesCount?: number): void {
@@ -889,17 +920,15 @@
             var matricesCount = visibleInstances.length + 1;
             var bufferSize = matricesCount * 16 * 4;
 
+            var currentInstancesBufferSize = this._instancesBufferSize;
+            var instancesBuffer = this._instancesBuffer;
+
             while (this._instancesBufferSize < bufferSize) {
                 this._instancesBufferSize *= 2;
             }
 
-            if (!this._worldMatricesInstancesBuffer || this._worldMatricesInstancesBuffer.capacity < this._instancesBufferSize) {
-                if (this._worldMatricesInstancesBuffer) {
-                    engine.deleteInstancesBuffer(this._worldMatricesInstancesBuffer);
-                }
-
-                this._worldMatricesInstancesBuffer = engine.createInstancesBuffer(this._instancesBufferSize);
-                this._worldMatricesInstancesArray = new Float32Array(this._instancesBufferSize / 4);
+            if (!this._instancesData || currentInstancesBufferSize != this._instancesBufferSize) {
+                this._instancesData = new Float32Array(this._instancesBufferSize / 4);
             }
 
             var offset = 0;
@@ -907,33 +936,41 @@
 
             var world = this.getWorldMatrix();
             if (batch.renderSelf[subMesh._id]) {
-                world.copyToArray(this._worldMatricesInstancesArray, offset);
+                world.copyToArray(this._instancesData, offset);
                 offset += 16;
                 instancesCount++;
             }
 
-
             if (visibleInstances) {
                 for (var instanceIndex = 0; instanceIndex < visibleInstances.length; instanceIndex++) {
                     var instance = visibleInstances[instanceIndex];
-                    instance.getWorldMatrix().copyToArray(this._worldMatricesInstancesArray, offset);
+                    instance.getWorldMatrix().copyToArray(this._instancesData, offset);
                     offset += 16;
                     instancesCount++;
                 }
             }
 
-            var offsetLocation0 = effect.getAttributeLocationByName("world0");
-            var offsetLocation1 = effect.getAttributeLocationByName("world1");
-            var offsetLocation2 = effect.getAttributeLocationByName("world2");
-            var offsetLocation3 = effect.getAttributeLocationByName("world3");
+            if (!instancesBuffer || currentInstancesBufferSize != this._instancesBufferSize) {
+                if (instancesBuffer) {
+                    instancesBuffer.dispose();
+                }
 
-            var offsetLocations = [offsetLocation0, offsetLocation1, offsetLocation2, offsetLocation3];
+                instancesBuffer = new BABYLON.Buffer(engine, this._instancesData, true, 16, false, true);
+                this._instancesBuffer = instancesBuffer;
 
-            engine.updateAndBindInstancesBuffer(this._worldMatricesInstancesBuffer, this._worldMatricesInstancesArray, offsetLocations);
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world0", 0, 4));
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world1", 4, 4));
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world2", 8, 4));
+                this.setVerticesBuffer(instancesBuffer.createVertexBuffer("world3", 12, 4));
+
+                engine.bindBuffers(this.geometry.getVertexBuffers(), this.geometry.getIndexBuffer(), effect);
+            } else {
+                instancesBuffer.updateDirectly(this._instancesData, 0, instancesCount);
+            }
 
             this._draw(subMesh, fillMode, instancesCount);
 
-            engine.unBindInstancesBuffer(this._worldMatricesInstancesBuffer, offsetLocations);
+            engine.unbindInstanceAttributes();
         }
 
         public _processRendering(subMesh: SubMesh, effect: Effect, fillMode: number, batch: _InstancesBatch, hardwareInstancedRendering: boolean,
@@ -950,7 +987,7 @@
                         onBeforeDraw(false, this.getWorldMatrix());
                     }
 
-                    this._draw(subMesh, fillMode);
+                    this._draw(subMesh, fillMode, this._overridenInstanceCount);
                 }
 
                 if (batch.visibleInstances[subMesh._id]) {
@@ -1057,7 +1094,7 @@
         }
 
         /**
-         * Returns an array populated with `ParticleSystem` objects whose the mesh is the emitter. 
+         * Returns an array populated with ParticleSystem objects whose the mesh is the emitter. 
          */
         public getEmittedParticleSystems(): ParticleSystem[] {
             var results = new Array<ParticleSystem>();
@@ -1072,7 +1109,7 @@
         }
 
         /**
-         * Returns an array populated with `ParticleSystem` objects whose the mesh or its children are the emitter.
+         * Returns an array populated with ParticleSystem objects whose the mesh or its children are the emitter.
          */
         public getHierarchyEmittedParticleSystems(): ParticleSystem[] {
             var results = new Array<ParticleSystem>();
@@ -1119,7 +1156,7 @@
         }
 
         /**
-         * Boolean, true is the mesh in the frustum defined by the `Plane` objects from the `frustumPlanes` array parameter.
+         * Boolean, true is the mesh in the frustum defined by the Plane objects from the `frustumPlanes` array parameter.
          */
         public isInFrustum(frustumPlanes: Plane[]): boolean {
             if (this.delayLoadState === Engine.DELAYLOADSTATE_LOADING) {
@@ -1266,7 +1303,7 @@
         }
 
         /**
-         * Returns a new `Mesh` object generated from the current mesh properties.
+         * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().  
          * The parameter `name` is a string, the name given to the new mesh. 
          * The optional parameter `newParent` can be any `Node` object (default `null`).  
@@ -1287,9 +1324,9 @@
             }
 
             // Instances
-            if (this._worldMatricesInstancesBuffer) {
-                this.getEngine().deleteInstancesBuffer(this._worldMatricesInstancesBuffer);
-                this._worldMatricesInstancesBuffer = null;
+            if (this._instancesBuffer) {
+                this._instancesBuffer.dispose();
+                this._instancesBuffer = null;
             }
 
             while (this.instances.length) {
@@ -1588,7 +1625,7 @@
 
         // Instances
         /**
-         * Creates a new `InstancedMesh` object from the mesh model.
+         * Creates a new InstancedMesh object from the mesh model.
          * An instance shares the same properties and the same material than its model.
          * Only these properties of each instance can then be set individually :
          * - position
@@ -1597,7 +1634,7 @@
          * - setPivotMatrix
          * - scaling
          * tuto : http://doc.babylonjs.com/tutorials/How_to_use_Instances
-         * Warning : this method is not supported for `Line` mesh and `LineSystem`
+         * Warning : this method is not supported for Line mesh and LineSystem
          */
         public createInstance(name: string): InstancedMesh {
             return new InstancedMesh(name, this);
@@ -1675,7 +1712,7 @@
 
         // 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. 
          * The parameter `parsedMesh` is the mesh to be copied.
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
@@ -1887,7 +1924,7 @@
 
         /**
          * Creates a ribbon mesh.   
-         * Please consider using the same method from the `MeshBuilder` class instead.   
+         * Please consider using the same method from the MeshBuilder class instead.   
          * The ribbon is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.    
          *
          * Please read this full tutorial to understand how to design a ribbon : http://doc.babylonjs.com/tutorials/Ribbon_Tutorial    
@@ -1914,7 +1951,7 @@
         }
         /**
          * Creates a plane polygonal mesh.  By default, this is a disc.   
-         * Please consider using the same method from the `MeshBuilder` class instead.   
+         * Please consider using the same method from the MeshBuilder class instead.   
          * The parameter `radius` sets the radius size (float) of the polygon (default 0.5).  
          * The parameter `tessellation` sets the number of polygon sides (positive integer, default 64). So a tessellation valued to 3 will build a triangle, to 4 a square, etc.  
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
@@ -1933,7 +1970,7 @@
         }
         /**
          * Creates a box mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.   
+         * Please consider using the same method from the MeshBuilder class instead.   
          * The parameter `size` sets the size (float) of each box side (default 1).  
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation    
@@ -1950,7 +1987,7 @@
         }
         /**
          * Creates a sphere mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.   
+         * Please consider using the same method from the MeshBuilder class instead.   
          * The parameter `diameter` sets the diameter size (float) of the sphere (default 1).  
          * The parameter `segments` sets the sphere number of horizontal stripes (positive integer, default 32).  
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
@@ -1972,7 +2009,7 @@
 
         /**
          * Creates a cylinder or a cone mesh.   
-         * Please consider using the same method from the `MeshBuilder` class instead.   
+         * Please consider using the same method from the MeshBuilder class instead.   
          * The parameter `height` sets the height size (float) of the cylinder/cone (float, default 2).  
          * The parameter `diameter` sets the diameter of the top and bottom cap at once (float, default 1).  
          * The parameters `diameterTop` and `diameterBottom` overwrite the parameter `diameter` and set respectively the top cap and bottom cap diameter (floats, default 1). The parameter "diameterBottom" can't be zero.  
@@ -2008,7 +2045,7 @@
         // Torus  (Code from SharpDX.org)
         /**
          * Creates a torus mesh.   
-         * Please consider using the same method from the `MeshBuilder` class instead.      
+         * Please consider using the same method from the MeshBuilder class instead.      
          * The parameter `diameter` sets the diameter size (float) of the torus (default 1).  
          * The parameter `thickness` sets the diameter size of the tube of the torus (float, default 0.5).  
          * The parameter `tessellation` sets the number of torus sides (postive integer, default 16).  
@@ -2029,7 +2066,7 @@
         }
         /**
          * Creates a torus knot mesh.   
-         * Please consider using the same method from the `MeshBuilder` class instead.     
+         * Please consider using the same method from the MeshBuilder class instead.     
          * The parameter `radius` sets the global radius size (float) of the torus knot (default 2).  
          * The parameter `radialSegments` sets the number of sides on each tube segments (positive integer, default 32).  
          * The parameter `tubularSegments` sets the number of tubes to decompose the knot into (positive integer, default 32).  
@@ -2055,7 +2092,7 @@
 
         /**
          * Creates a line mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.     
+         * Please consider using the same method from the MeshBuilder class instead.     
          * A line mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of points as an input parameter.  
          * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineMesh to this static function.  
          * The parameter `points` is an array successive Vector3.   
@@ -2074,7 +2111,7 @@
 
         /**
          * Creates a dashed line mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * A dashed line mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of points as an input parameter.  
          * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineMesh to this static function.  
          * The parameter `points` is an array successive Vector3.  
@@ -2099,7 +2136,7 @@
         /**
          * Creates an extruded shape mesh.    
          * The extrusion is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          *
          * Please read this full tutorial to understand how to design an extruded shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes#extrusion     
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be extruded in its local space : the shape must be designed in the xOy plane and will be
@@ -2131,7 +2168,7 @@
         /**
          * Creates an custom extruded shape mesh.    
          * The custom extrusion is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          *
          * Please read this full tutorial to understand how to design a custom extruded shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes#extrusion     
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be extruded in its local space : the shape must be designed in the xOy plane and will be
@@ -2139,15 +2176,19 @@
          * The parameter `path` is a required array of successive Vector3. This is the axis curve the shape is extruded along.      
          * The parameter `rotationFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path 
          * and the distance of this point from the begining of the path : 
-         * ```rotationFunction = function(i, distance) {
-         *  // do things
-         *  return rotationValue; }```  
+         * ```javascript
+         * var rotationFunction = function(i, distance) {
+         *     // do things
+         *     return rotationValue; }
+         * ```  
          * It must returns a float value that will be the rotation in radians applied to the shape on each path point.      
          * The parameter `scaleFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path 
          * and the distance of this point from the begining of the path : 
-         * ````scaleFunction = function(i, distance) {
-         *   // do things
-         *  return scaleValue;}```  
+         * ```javascript
+         * var scaleFunction = function(i, distance) {
+         *     // do things
+         *    return scaleValue;}
+         * ```  
          * It must returns a float value that will be the scale value applied to the shape on each path point.   
          * The parameter `ribbonClosePath` (boolean, default false) forces the extrusion underlying ribbon to close all the paths in its `pathArray`.  
          * The parameter `ribbonCloseArray` (boolean, default false) forces the extrusion underlying ribbon to close its `pathArray`.
@@ -2178,7 +2219,7 @@
         /**
          * Creates lathe mesh.  
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.      
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          *
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.    
@@ -2202,7 +2243,7 @@
 
         /**
          * Creates a plane mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * The parameter `size` sets the size (float) of both sides of the plane at once (default 1).  
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation    
@@ -2221,7 +2262,7 @@
         }
         /**
          * Creates a ground mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * The parameters `width` and `height` (floats, default 1) set the width and height sizes of the ground.    
          * The parameter `subdivisions` (positive integer) sets the number of subdivisions per side.       
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.  
@@ -2238,7 +2279,7 @@
         }
         /**
          * Creates a tiled ground mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * The parameters `xmin` and `xmax` (floats, default -1 and 1) set the ground minimum and maximum X coordinates.     
          * The parameters `zmin` and `zmax` (floats, default -1 and 1) set the ground minimum and maximum Z coordinates.   
          * The parameter `subdivisions` is a javascript object `{w: positive integer, h: positive integer}` (default `{w: 6, h: 6}`). `w` and `h` are the
@@ -2263,15 +2304,18 @@
         /**
          * Creates a ground mesh from a height map.    
          * tuto : http://doc.babylonjs.com/tutorials/14._Height_Map   
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * The parameter `url` sets the URL of the height map image resource.  
          * The parameters `width` and `height` (positive floats, default 10) set the ground width and height sizes.     
          * The parameter `subdivisions` (positive integer, default 1) sets the number of subdivision per side.  
          * The parameter `minHeight` (float, default 0) is the minimum altitude on the ground.     
          * The parameter `maxHeight` (float, default 1) is the maximum altitude on the ground.   
          * The parameter `onReady` is a javascript callback function that will be called  once the mesh is just built (the height map download can last some time).  
-         * This function is passed the newly built mesh : ```function(mesh) { // do things
-         * return; }```
+         * This function is passed the newly built mesh : 
+         * ```javascript
+         * function(mesh) { // do things
+         *     return; }
+         * ```
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.  
          */
         public static CreateGroundFromHeightMap(name: string, url: string, width: number, height: number, subdivisions: number, minHeight: number, maxHeight: number, scene: Scene, updatable?: boolean, onReady?: (mesh: GroundMesh) => void): GroundMesh {
@@ -2291,16 +2335,18 @@
          * Creates a tube mesh.    
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.    
          *
-         * Please consider using the same method from the `MeshBuilder` class instead.    
-         * The parameter `path` is a required array of successive `Vector3`. It is the curve used as the axis of the tube.        
+         * Please consider using the same method from the MeshBuilder class instead.    
+         * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.        
          * The parameter `radius` (positive float, default 1) sets the tube radius size.    
          * The parameter `tessellation` (positive float, default 64) is the number of sides on the tubular surface.  
          * The parameter `radiusFunction` (javascript function, default null) is a vanilla javascript function. If it is not null, it overwrittes the parameter `radius`. 
          * This function is called on each point of the tube path and is passed the index `i` of the i-th point and the distance of this point from the first point of the path. 
          * It must return a radius value (positive float) : 
-         * ```var radiusFunction = function(i, distance) {
-         *   // do things
-         *   return radius; }```
+         * ```javascript
+         * var radiusFunction = function(i, distance) {
+         *     // do things
+         *     return radius; }
+         * ```
          * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL         
          * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#tube    
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
@@ -2324,14 +2370,14 @@
         /**
          * Creates a polyhedron mesh.  
          * 
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          *  to choose the wanted type.  
          * The parameter `size` (positive float, default 1) sets the polygon size.  
          * You can overwrite the `size` on each dimension bu using the parameters `sizeX`, `sizeY` or `sizeZ` (positive floats, default to `size` value).  
          * You can build other polyhedron types than the 15 embbeded ones by setting the parameter `custom` (`polyhedronObject`, default null). If you set the parameter `custom`, this overwrittes the parameter `type`.  
          * A `polyhedronObject` is a formatted javascript object. You'll find a full file with pre-set polyhedra here : https://github.com/BabylonJS/Extensions/tree/master/Polyhedron    
-         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (`Color4`, default `(1, 1, 1, 1)`) and faceUV (`Vector4`, default `(0, 0, 1, 1)`). 
+         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (Color4, default `(1, 1, 1, 1)`) and faceUV (Vector4, default `(0, 0, 1, 1)`). 
          * To understand how to set `faceUV` or `faceColors`, please read this by considering the right number of faces of your polyhedron, instead of only 6 for the box : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors  
          * The parameter `flat` (boolean, default true). If set to false, it gives the polyhedron a single global face, so less vertices and shared normals. In this case, `faceColors` and `faceUV` are ignored.    
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
@@ -2343,7 +2389,7 @@
         }
         /**
          * Creates a sphere based upon an icosahedron with 20 triangular faces which can be subdivided.   
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * The parameter `radius` sets the radius size (float) of the icosphere (default 1).  
          * You can set some different icosphere dimensions, for instance to build an ellipsoid, by using the parameters `radiusX`, `radiusY` and `radiusZ` (all by default have the same value than `radius`).  
          * The parameter `subdivisions` sets the number of subdivisions (postive integer, default 4). The more subdivisions, the more faces on the icosphere whatever its size.    
@@ -2358,11 +2404,11 @@
 
         /**
          * Creates a decal mesh.  
-         * Please consider using the same method from the `MeshBuilder` class instead.    
+         * Please consider using the same method from the MeshBuilder class instead.    
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.  
-         * The parameter `position` (`Vector3`, default `(0, 0, 0)`) sets the position of the decal in World coordinates.  
-         * The parameter `normal` (`Vector3`, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.  
-         * The parameter `size` (`Vector3`, default `(1, 1, 1)`) sets the decal scaling.  
+         * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.  
+         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.  
+         * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.  
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.  
          */
         public static CreateDecal(name: string, sourceMesh: AbstractMesh, position: Vector3, normal: Vector3, size: Vector3, angle: number): Mesh {
@@ -2513,7 +2559,7 @@
         // Tools
         /**
          * Returns an object `{min: Vector3, max: Vector3}`
-         * This min and max `Vector3` are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
+         * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          */
         public static MinMax(meshes: AbstractMesh[]): { min: Vector3; max: Vector3 } {
             var minVector: Vector3 = null;

+ 28 - 19
src/Mesh/babylon.meshBuilder.js

@@ -8,7 +8,7 @@ var BABYLON;
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#box
          * The parameter `size` sets the size (float) of each box side (default 1).
          * You can set some different box dimensions by using the parameters `width`, `height` and `depth` (all by default have the same value than `size`).
-         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of 6 `Color3` elements) and `faceUV` (an array of 6 `Vector4` elements).
+         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of 6 Color3 elements) and `faceUV` (an array of 6 Vector4 elements).
          * Please read this tutorial : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
@@ -178,7 +178,7 @@ var BABYLON;
          * The parameter `hasRings` (boolean, default false) makes the subdivisions independent from each other, so they become different faces.
          * The parameter `enclose`  (boolean, default false) adds two extra faces per subdivision to a sliced cylinder to close it around its height axis.
          * The parameter `arc` (float, default 1) is the ratio (max 1) to apply to the circumference to slice the cylinder.
-         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of n `Color3` elements) and `faceUV` (an array of n `Vector4` elements).
+         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of n Color3 elements) and `faceUV` (an array of n Vector4 elements).
          * The value of n is the number of cylinder faces. If the cylinder has only 1 subdivisions, n equals : top face + cylinder surface + bottom face = 3
          * Now, if the cylinder has 5 independent subdivisions (hasRings = true), n equals : top face + 5 stripe surfaces + bottom face = 2 + 5 = 7
          * Finally, if the cylinder has 5 independent subdivisions and is enclose, n equals : top face + 5 x (stripe surface + 2 closing faces) + bottom face = 2 + 5 * 3 = 17
@@ -392,15 +392,19 @@ var BABYLON;
          * The parameter `path` is a required array of successive Vector3. This is the axis curve the shape is extruded along.
          * The parameter `rotationFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path
          * and the distance of this point from the begining of the path :
-         * ```rotationFunction = function(i, distance) {
-         *  // do things
-         *  return rotationValue; }```
+         * ```javascript
+         * var rotationFunction = function(i, distance) {
+         *     // do things
+         *     return rotationValue; }
+         * ```
          * It must returns a float value that will be the rotation in radians applied to the shape on each path point.
          * The parameter `scaleFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path
          * and the distance of this point from the begining of the path :
-         * ````scaleFunction = function(i, distance) {
-         *   // do things
-         *  return scaleValue;}```
+         * ```javascript
+         * var scaleFunction = function(i, distance) {
+         *     // do things
+         *     return scaleValue;}
+         * ```
          * It must returns a float value that will be the scale value applied to the shape on each path point.
          * The parameter `ribbonClosePath` (boolean, default false) forces the extrusion underlying ribbon to close all the paths in its `pathArray`.
          * The parameter `ribbonCloseArray` (boolean, default false) forces the extrusion underlying ribbon to close its `pathArray`.
@@ -482,7 +486,7 @@ var BABYLON;
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#plane
          * The parameter `size` sets the size (float) of both sides of the plane at once (default 1).
          * You can set some different plane dimensions by using the parameters `width` and `height` (both by default have the same value than `size`).
-         * The parameter `sourcePlane` is a `Plane` instance. It builds a mesh plane from a Math plane.
+         * The parameter `sourcePlane` is a Plane instance. It builds a mesh plane from a Math plane.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
@@ -548,8 +552,11 @@ var BABYLON;
          * The parameter `minHeight` (float, default 0) is the minimum altitude on the ground.
          * The parameter `maxHeight` (float, default 1) is the maximum altitude on the ground.
          * The parameter `onReady` is a javascript callback function that will be called  once the mesh is just built (the height map download can last some time).
-         * This function is passed the newly built mesh : ```function(mesh) { // do things
-         * return; }```
+         * This function is passed the newly built mesh :
+         * ```javascript
+         * function(mesh) { // do things
+         *     return; }
+         * ```
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.CreateGroundFromHeightMap = function (name, url, options, scene) {
@@ -602,15 +609,17 @@ var BABYLON;
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
          *
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#tube
-         * The parameter `path` is a required array of successive `Vector3`. It is the curve used as the axis of the tube.
+         * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.
          * The parameter `radius` (positive float, default 1) sets the tube radius size.
          * The parameter `tessellation` (positive float, default 64) is the number of sides on the tubular surface.
          * The parameter `radiusFunction` (javascript function, default null) is a vanilla javascript function. If it is not null, it overwrittes the parameter `radius`.
          * This function is called on each point of the tube path and is passed the index `i` of the i-th point and the distance of this point from the first point of the path.
          * It must return a radius value (positive float) :
-         * ```var radiusFunction = function(i, distance) {
-         *   // do things
-         *   return radius; }```
+         * ```javascript
+         * var radiusFunction = function(i, distance) {
+         *     // do things
+         *     return radius; }
+         * ```
          * The parameter `arc` (positive float, maximum 1, default 1) is the ratio to apply to the tube circumference : 2 x PI x arc.
          * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL
          * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#tube
@@ -722,7 +731,7 @@ var BABYLON;
          * You can overwrite the `size` on each dimension bu using the parameters `sizeX`, `sizeY` or `sizeZ` (positive floats, default to `size` value).
          * You can build other polyhedron types than the 15 embbeded ones by setting the parameter `custom` (`polyhedronObject`, default null). If you set the parameter `custom`, this overwrittes the parameter `type`.
          * A `polyhedronObject` is a formatted javascript object. You'll find a full file with pre-set polyhedra here : https://github.com/BabylonJS/Extensions/tree/master/Polyhedron
-         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (`Color4`, default `(1, 1, 1, 1)`) and faceUV (`Vector4`, default `(0, 0, 1, 1)`).
+         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (Color4, default `(1, 1, 1, 1)`) and faceUV (Vector4, default `(0, 0, 1, 1)`).
          * To understand how to set `faceUV` or `faceColors`, please read this by considering the right number of faces of your polyhedron, instead of only 6 for the box : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors
          * The parameter `flat` (boolean, default true). If set to false, it gives the polyhedron a single global face, so less vertices and shared normals. In this case, `faceColors` and `faceUV` are ignored.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
@@ -739,9 +748,9 @@ var BABYLON;
          * Creates a decal mesh.
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#decals
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.
-         * The parameter `position` (`Vector3`, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
-         * The parameter `normal` (`Vector3`, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.
-         * The parameter `size` (`Vector3`, default `(1, 1, 1)`) sets the decal scaling.
+         * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
+         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.
+         * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.
          */
         MeshBuilder.CreateDecal = function (name, sourceMesh, options) {

+ 28 - 19
src/Mesh/babylon.meshBuilder.ts

@@ -5,7 +5,7 @@
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#box  
          * The parameter `size` sets the size (float) of each box side (default 1).  
          * You can set some different box dimensions by using the parameters `width`, `height` and `depth` (all by default have the same value than `size`).  
-         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of 6 `Color3` elements) and `faceUV` (an array of 6 `Vector4` elements).
+         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of 6 Color3 elements) and `faceUV` (an array of 6 Vector4 elements).
          * Please read this tutorial : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors  
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation    
@@ -195,7 +195,7 @@
          * The parameter `hasRings` (boolean, default false) makes the subdivisions independent from each other, so they become different faces.  
          * The parameter `enclose`  (boolean, default false) adds two extra faces per subdivision to a sliced cylinder to close it around its height axis.  
          * The parameter `arc` (float, default 1) is the ratio (max 1) to apply to the circumference to slice the cylinder.   
-         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of n `Color3` elements) and `faceUV` (an array of n `Vector4` elements).   
+         * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of n Color3 elements) and `faceUV` (an array of n Vector4 elements).   
          * The value of n is the number of cylinder faces. If the cylinder has only 1 subdivisions, n equals : top face + cylinder surface + bottom face = 3   
          * Now, if the cylinder has 5 independent subdivisions (hasRings = true), n equals : top face + 5 stripe surfaces + bottom face = 2 + 5 = 7   
          * Finally, if the cylinder has 5 independent subdivisions and is enclose, n equals : top face + 5 x (stripe surface + 2 closing faces) + bottom face = 2 + 5 * 3 = 17    
@@ -426,15 +426,19 @@
          * The parameter `path` is a required array of successive Vector3. This is the axis curve the shape is extruded along.      
          * The parameter `rotationFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path 
          * and the distance of this point from the begining of the path : 
-         * ```rotationFunction = function(i, distance) {
-         *  // do things
-         *  return rotationValue; }```  
+         * ```javascript
+         * var rotationFunction = function(i, distance) {
+         *     // do things
+         *     return rotationValue; }
+         * ```  
          * It must returns a float value that will be the rotation in radians applied to the shape on each path point.      
          * The parameter `scaleFunction` (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path 
          * and the distance of this point from the begining of the path : 
-         * ````scaleFunction = function(i, distance) {
-         *   // do things
-         *  return scaleValue;}```  
+         * ```javascript
+         * var scaleFunction = function(i, distance) {
+         *     // do things
+         *     return scaleValue;}
+         * ```  
          * It must returns a float value that will be the scale value applied to the shape on each path point.   
          * The parameter `ribbonClosePath` (boolean, default false) forces the extrusion underlying ribbon to close all the paths in its `pathArray`.  
          * The parameter `ribbonCloseArray` (boolean, default false) forces the extrusion underlying ribbon to close its `pathArray`.
@@ -519,7 +523,7 @@
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#plane  
          * The parameter `size` sets the size (float) of both sides of the plane at once (default 1).  
          * You can set some different plane dimensions by using the parameters `width` and `height` (both by default have the same value than `size`).
-         * The parameter `sourcePlane` is a `Plane` instance. It builds a mesh plane from a Math plane.    
+         * The parameter `sourcePlane` is a Plane instance. It builds a mesh plane from a Math plane.    
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation    
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.  
@@ -601,8 +605,11 @@
          * The parameter `minHeight` (float, default 0) is the minimum altitude on the ground.     
          * The parameter `maxHeight` (float, default 1) is the maximum altitude on the ground.   
          * The parameter `onReady` is a javascript callback function that will be called  once the mesh is just built (the height map download can last some time).  
-         * This function is passed the newly built mesh : ```function(mesh) { // do things
-         * return; }```
+         * This function is passed the newly built mesh : 
+         * ```javascript
+         * function(mesh) { // do things
+         *     return; }
+         * ```
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.  
          */
         public static CreateGroundFromHeightMap(name: string, url: string, options: { width?: number, height?: number, subdivisions?: number, minHeight?: number, maxHeight?: number, updatable?: boolean, onReady?: (mesh: GroundMesh) => void }, scene: Scene): GroundMesh {
@@ -666,15 +673,17 @@
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.    
          *
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#tube     
-         * The parameter `path` is a required array of successive `Vector3`. It is the curve used as the axis of the tube.        
+         * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.        
          * The parameter `radius` (positive float, default 1) sets the tube radius size.    
          * The parameter `tessellation` (positive float, default 64) is the number of sides on the tubular surface.  
          * The parameter `radiusFunction` (javascript function, default null) is a vanilla javascript function. If it is not null, it overwrittes the parameter `radius`. 
          * This function is called on each point of the tube path and is passed the index `i` of the i-th point and the distance of this point from the first point of the path. 
          * It must return a radius value (positive float) : 
-         * ```var radiusFunction = function(i, distance) {
-         *   // do things
-         *   return radius; }```
+         * ```javascript
+         * var radiusFunction = function(i, distance) {
+         *     // do things
+         *     return radius; }
+         * ```
          * The parameter `arc` (positive float, maximum 1, default 1) is the ratio to apply to the tube circumference : 2 x PI x arc.  
          * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL         
          * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#tube    
@@ -792,7 +801,7 @@
          * You can overwrite the `size` on each dimension bu using the parameters `sizeX`, `sizeY` or `sizeZ` (positive floats, default to `size` value).  
          * You can build other polyhedron types than the 15 embbeded ones by setting the parameter `custom` (`polyhedronObject`, default null). If you set the parameter `custom`, this overwrittes the parameter `type`.  
          * A `polyhedronObject` is a formatted javascript object. You'll find a full file with pre-set polyhedra here : https://github.com/BabylonJS/Extensions/tree/master/Polyhedron    
-         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (`Color4`, default `(1, 1, 1, 1)`) and faceUV (`Vector4`, default `(0, 0, 1, 1)`). 
+         * You can set the color and the UV of each side of the polyhedron with the parameters `faceColors` (Color4, default `(1, 1, 1, 1)`) and faceUV (Vector4, default `(0, 0, 1, 1)`). 
          * To understand how to set `faceUV` or `faceColors`, please read this by considering the right number of faces of your polyhedron, instead of only 6 for the box : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors  
          * The parameter `flat` (boolean, default true). If set to false, it gives the polyhedron a single global face, so less vertices and shared normals. In this case, `faceColors` and `faceUV` are ignored.    
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE  
@@ -813,9 +822,9 @@
          * Creates a decal mesh.  
          * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#decals  
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.  
-         * The parameter `position` (`Vector3`, default `(0, 0, 0)`) sets the position of the decal in World coordinates.  
-         * The parameter `normal` (`Vector3`, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.  
-         * The parameter `size` (`Vector3`, default `(1, 1, 1)`) sets the decal scaling.  
+         * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.  
+         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.  
+         * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.  
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.  
          */
         public static CreateDecal(name: string, sourceMesh: AbstractMesh, options: { position?: Vector3, normal?: Vector3, size?: Vector3, angle?: number }): Mesh {

+ 63 - 74
src/Mesh/babylon.vertexBuffer.js

@@ -1,102 +1,91 @@
 var BABYLON;
 (function (BABYLON) {
     var VertexBuffer = (function () {
-        function VertexBuffer(engine, data, kind, updatable, postponeInternalCreation, stride) {
-            if (engine instanceof BABYLON.Mesh) {
-                this._engine = engine.getScene().getEngine();
+        function VertexBuffer(engine, data, kind, updatable, postponeInternalCreation, stride, instanced, offset, size) {
+            if (!stride) {
+                // Deduce stride from kind
+                switch (kind) {
+                    case VertexBuffer.PositionKind:
+                        stride = 3;
+                        break;
+                    case VertexBuffer.NormalKind:
+                        stride = 3;
+                        break;
+                    case VertexBuffer.UVKind:
+                    case VertexBuffer.UV2Kind:
+                    case VertexBuffer.UV3Kind:
+                    case VertexBuffer.UV4Kind:
+                    case VertexBuffer.UV5Kind:
+                    case VertexBuffer.UV6Kind:
+                        stride = 2;
+                        break;
+                    case VertexBuffer.ColorKind:
+                        stride = 4;
+                        break;
+                    case VertexBuffer.MatricesIndicesKind:
+                    case VertexBuffer.MatricesIndicesExtraKind:
+                        stride = 4;
+                        break;
+                    case VertexBuffer.MatricesWeightsKind:
+                    case VertexBuffer.MatricesWeightsExtraKind:
+                        stride = 4;
+                        break;
+                }
             }
-            else {
-                this._engine = engine;
+            if (data instanceof BABYLON.Buffer) {
+                if (!stride) {
+                    stride = data.getStrideSize();
+                }
+                this._buffer = data;
+                this._ownsBuffer = false;
             }
-            this._updatable = updatable;
-            this._data = data;
-            if (!postponeInternalCreation) {
-                this.create();
+            else {
+                this._buffer = new BABYLON.Buffer(engine, data, updatable, stride, postponeInternalCreation, instanced);
+                this._ownsBuffer = true;
             }
+            this._stride = stride;
+            this._offset = offset ? offset : 0;
+            this._size = size ? size : stride;
             this._kind = kind;
-            if (stride) {
-                this._strideSize = stride;
-                return;
-            }
-            // Deduce stride from kind
-            switch (kind) {
-                case VertexBuffer.PositionKind:
-                    this._strideSize = 3;
-                    break;
-                case VertexBuffer.NormalKind:
-                    this._strideSize = 3;
-                    break;
-                case VertexBuffer.UVKind:
-                case VertexBuffer.UV2Kind:
-                case VertexBuffer.UV3Kind:
-                case VertexBuffer.UV4Kind:
-                case VertexBuffer.UV5Kind:
-                case VertexBuffer.UV6Kind:
-                    this._strideSize = 2;
-                    break;
-                case VertexBuffer.ColorKind:
-                    this._strideSize = 4;
-                    break;
-                case VertexBuffer.MatricesIndicesKind:
-                case VertexBuffer.MatricesIndicesExtraKind:
-                    this._strideSize = 4;
-                    break;
-                case VertexBuffer.MatricesWeightsKind:
-                case VertexBuffer.MatricesWeightsExtraKind:
-                    this._strideSize = 4;
-                    break;
-            }
         }
+        VertexBuffer.prototype.getKind = function () {
+            return this._kind;
+        };
         // Properties
         VertexBuffer.prototype.isUpdatable = function () {
-            return this._updatable;
+            return this._buffer.isUpdatable();
         };
         VertexBuffer.prototype.getData = function () {
-            return this._data;
+            return this._buffer.getData();
         };
         VertexBuffer.prototype.getBuffer = function () {
-            return this._buffer;
+            return this._buffer.getBuffer();
         };
         VertexBuffer.prototype.getStrideSize = function () {
-            return this._strideSize;
+            return this._stride;
+        };
+        VertexBuffer.prototype.getOffset = function () {
+            return this._offset;
+        };
+        VertexBuffer.prototype.getSize = function () {
+            return this._size;
+        };
+        VertexBuffer.prototype.getIsInstanced = function () {
+            return this._buffer.getIsInstanced();
         };
         // Methods
         VertexBuffer.prototype.create = function (data) {
-            if (!data && this._buffer) {
-                return; // nothing to do
-            }
-            data = data || this._data;
-            if (!this._buffer) {
-                if (this._updatable) {
-                    this._buffer = this._engine.createDynamicVertexBuffer(data.length * 4);
-                }
-                else {
-                    this._buffer = this._engine.createVertexBuffer(data);
-                }
-            }
-            if (this._updatable) {
-                this._engine.updateDynamicVertexBuffer(this._buffer, data);
-                this._data = data;
-            }
+            return this._buffer.create(data);
         };
         VertexBuffer.prototype.update = function (data) {
-            this.create(data);
+            return this._buffer.update(data);
         };
         VertexBuffer.prototype.updateDirectly = function (data, offset) {
-            if (!this._buffer) {
-                return;
-            }
-            if (this._updatable) {
-                this._engine.updateDynamicVertexBuffer(this._buffer, data, offset);
-                this._data = null;
-            }
+            return this._buffer.updateDirectly(data, offset);
         };
         VertexBuffer.prototype.dispose = function () {
-            if (!this._buffer) {
-                return;
-            }
-            if (this._engine._releaseBuffer(this._buffer)) {
-                this._buffer = null;
+            if (this._ownsBuffer) {
+                this._buffer.dispose();
             }
         };
         Object.defineProperty(VertexBuffer, "PositionKind", {

+ 74 - 84
src/Mesh/babylon.vertexBuffer.ts

@@ -1,126 +1,116 @@
 module BABYLON {
     export class VertexBuffer {
-        private _mesh: Mesh;
-        private _engine: Engine;
-        private _buffer: WebGLBuffer;
-        private _data: number[] | Float32Array;
-        private _updatable: boolean;
+        private _buffer: Buffer;
         private _kind: string;
-        private _strideSize: number;
-
-        constructor(engine: any, data: number[] | Float32Array, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number) {
-            if (engine instanceof Mesh) { // old versions of BABYLON.VertexBuffer accepted 'mesh' instead of 'engine'
-                this._engine = engine.getScene().getEngine();
+        private _offset: number;
+        private _size: number;
+        private _stride: number;
+        private _ownsBuffer: boolean;
+
+        constructor(engine: any, data: number[] | Float32Array | Buffer, kind: string, updatable: boolean, postponeInternalCreation?: boolean, stride?: number, instanced?: boolean, offset?: number, size?: number) {
+            if (!stride) {
+                // Deduce stride from kind
+                switch (kind) {
+                    case VertexBuffer.PositionKind:
+                        stride = 3;
+                        break;
+                    case VertexBuffer.NormalKind:
+                        stride = 3;
+                        break;
+                    case VertexBuffer.UVKind:
+                    case VertexBuffer.UV2Kind:
+                    case VertexBuffer.UV3Kind:
+                    case VertexBuffer.UV4Kind:
+                    case VertexBuffer.UV5Kind:
+                    case VertexBuffer.UV6Kind:
+                        stride = 2;
+                        break;
+                    case VertexBuffer.ColorKind:
+                        stride = 4;
+                        break;
+                    case VertexBuffer.MatricesIndicesKind:
+                    case VertexBuffer.MatricesIndicesExtraKind:
+                        stride = 4;
+                        break;
+                    case VertexBuffer.MatricesWeightsKind:
+                    case VertexBuffer.MatricesWeightsExtraKind:
+                        stride = 4;
+                        break;
+                }
             }
-            else {
-                this._engine = engine;
+
+            if (data instanceof Buffer) {
+                if (!stride) {
+                    stride = data.getStrideSize();
+                }
+                this._buffer = data;
+                this._ownsBuffer = false;
+            } else {
+                this._buffer = new Buffer(engine, <number[] | Float32Array>data, updatable, stride, postponeInternalCreation, instanced);
+                this._ownsBuffer = true;
             }
 
-            this._updatable = updatable;
+            this._stride = stride;
 
-            this._data = data;
 
-            if (!postponeInternalCreation) { // by default
-                this.create();
-            }
+            this._offset = offset ? offset : 0;
+            this._size = size ? size : stride;
 
             this._kind = kind;
+        }
 
-            if (stride) {
-                this._strideSize = stride;
-                return;
-            }
 
-            // Deduce stride from kind
-            switch (kind) {
-                case VertexBuffer.PositionKind:
-                    this._strideSize = 3;
-                    break;
-                case VertexBuffer.NormalKind:
-                    this._strideSize = 3;
-                    break;
-                case VertexBuffer.UVKind:
-                case VertexBuffer.UV2Kind:
-                case VertexBuffer.UV3Kind:
-                case VertexBuffer.UV4Kind:
-                case VertexBuffer.UV5Kind:
-                case VertexBuffer.UV6Kind:
-                    this._strideSize = 2;
-                    break;
-                case VertexBuffer.ColorKind:
-                    this._strideSize = 4;
-                    break;
-                case VertexBuffer.MatricesIndicesKind:
-                case VertexBuffer.MatricesIndicesExtraKind:
-                    this._strideSize = 4;
-                    break;
-                case VertexBuffer.MatricesWeightsKind:
-                case VertexBuffer.MatricesWeightsExtraKind:
-                    this._strideSize = 4;
-                    break;
-            }
+        public getKind(): string {
+            return this._kind;
         }
 
         // Properties
         public isUpdatable(): boolean {
-            return this._updatable;
+            return this._buffer.isUpdatable();
         }
 
         public getData(): number[] | Float32Array {
-            return this._data;
+            return this._buffer.getData();
         }
 
         public getBuffer(): WebGLBuffer {
-            return this._buffer;
+            return this._buffer.getBuffer();
         }
 
         public getStrideSize(): number {
-            return this._strideSize;
+            return this._stride;
         }
 
-        // Methods
-        public create(data?: number[] | Float32Array): void {
-            if (!data && this._buffer) {
-                return; // nothing to do
-            }
+        public getOffset(): number {
+            return this._offset;
+        }
 
-            data = data || this._data;
+        public getSize(): number {
+            return this._size;
+        }
 
-            if (!this._buffer) { // create buffer
-                if (this._updatable) {
-                    this._buffer = this._engine.createDynamicVertexBuffer(data.length * 4);
-                } else {
-                    this._buffer = this._engine.createVertexBuffer(data);
-                }
-            }
+        public getIsInstanced(): boolean {
+            return this._buffer.getIsInstanced();
+        }
 
-            if (this._updatable) { // update buffer
-                this._engine.updateDynamicVertexBuffer(this._buffer, data);
-                this._data = data;
-            }
+        // Methods
+
+
+        public create(data?: number[] | Float32Array): void {
+            return this._buffer.create(data);
         }
 
         public update(data: number[] | Float32Array): void {
-            this.create(data);
+            return this._buffer.update(data);
         }
 
         public updateDirectly(data: Float32Array, offset: number): void {
-            if (!this._buffer) {
-                return;
-            }
-
-            if (this._updatable) { // update buffer
-                this._engine.updateDynamicVertexBuffer(this._buffer, data, offset);
-                this._data = null;
-            }
+            return this._buffer.updateDirectly(data, offset);
         }
 
         public dispose(): void {
-            if (!this._buffer) {
-                return;
-            }
-            if (this._engine._releaseBuffer(this._buffer)) {
-                this._buffer = null;
+            if (this._ownsBuffer) {
+                this._buffer.dispose();
             }
         }
 

+ 26 - 22
src/Particles/babylon.particleSystem.js

@@ -46,10 +46,9 @@ var BABYLON;
             this.colorDead = new BABYLON.Color4(0, 0, 0, 1.0);
             this.textureMask = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
             this.particles = new Array();
-            this._vertexDeclaration = [3, 4, 4];
-            this._vertexStrideSize = 11 * 4; // 11 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY)
             this._stockParticles = new Array();
             this._newPartsExcess = 0;
+            this._vertexBuffers = {};
             this._scaledColorStep = new BABYLON.Color4(0, 0, 0, 0);
             this._colorDiff = new BABYLON.Color4(0, 0, 0, 0);
             this._scaledDirection = BABYLON.Vector3.Zero();
@@ -63,8 +62,6 @@ var BABYLON;
             this._scene = scene;
             this._customEffect = customEffect;
             scene.particleSystems.push(this);
-            // VBO
-            this._vertexBuffer = scene.getEngine().createDynamicVertexBuffer(capacity * this._vertexStrideSize * 4);
             var indices = [];
             var index = 0;
             for (var count = 0; count < capacity; count++) {
@@ -77,7 +74,15 @@ var BABYLON;
                 index += 4;
             }
             this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
-            this._vertices = new Float32Array(capacity * this._vertexStrideSize);
+            // 11 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY) + 1 filler
+            this._vertexData = new Float32Array(capacity * 11 * 4);
+            this._vertexBuffer = new BABYLON.Buffer(scene.getEngine(), this._vertexData, true, 11);
+            var positions = this._vertexBuffer.createVertexBuffer(BABYLON.VertexBuffer.PositionKind, 0, 3);
+            var colors = this._vertexBuffer.createVertexBuffer(BABYLON.VertexBuffer.ColorKind, 3, 4);
+            var options = this._vertexBuffer.createVertexBuffer("options", 7, 4);
+            this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = positions;
+            this._vertexBuffers[BABYLON.VertexBuffer.ColorKind] = colors;
+            this._vertexBuffers["options"] = options;
             // Default behaviors
             this.startDirectionFunction = function (emitPower, worldMatrix, directionToUpdate, particle) {
                 var randX = randomNumber(_this.direction1.x, _this.direction2.x);
@@ -150,17 +155,17 @@ var BABYLON;
         };
         ParticleSystem.prototype._appendParticleVertex = function (index, particle, offsetX, offsetY) {
             var offset = index * 11;
-            this._vertices[offset] = particle.position.x;
-            this._vertices[offset + 1] = particle.position.y;
-            this._vertices[offset + 2] = particle.position.z;
-            this._vertices[offset + 3] = particle.color.r;
-            this._vertices[offset + 4] = particle.color.g;
-            this._vertices[offset + 5] = particle.color.b;
-            this._vertices[offset + 6] = particle.color.a;
-            this._vertices[offset + 7] = particle.angle;
-            this._vertices[offset + 8] = particle.size;
-            this._vertices[offset + 9] = offsetX;
-            this._vertices[offset + 10] = offsetY;
+            this._vertexData[offset] = particle.position.x;
+            this._vertexData[offset + 1] = particle.position.y;
+            this._vertexData[offset + 2] = particle.position.z;
+            this._vertexData[offset + 3] = particle.color.r;
+            this._vertexData[offset + 4] = particle.color.g;
+            this._vertexData[offset + 5] = particle.color.b;
+            this._vertexData[offset + 6] = particle.color.a;
+            this._vertexData[offset + 7] = particle.angle;
+            this._vertexData[offset + 8] = particle.size;
+            this._vertexData[offset + 9] = offsetX;
+            this._vertexData[offset + 10] = offsetY;
         };
         ParticleSystem.prototype._update = function (newParticles) {
             // Update current
@@ -212,7 +217,7 @@ var BABYLON;
             var join = defines.join("\n");
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
-                this._effect = this._scene.getEngine().createEffect("particles", ["position", "color", "options"], ["invView", "view", "projection", "vClipPlane", "textureMask"], ["diffuseSampler"], join);
+                this._effect = this._scene.getEngine().createEffect("particles", [BABYLON.VertexBuffer.PositionKind, BABYLON.VertexBuffer.ColorKind, "options"], ["invView", "view", "projection", "vClipPlane", "textureMask"], ["diffuseSampler"], join);
             }
             return this._effect;
         };
@@ -271,8 +276,7 @@ var BABYLON;
                 this._appendParticleVertex(offset++, particle, 1, 1);
                 this._appendParticleVertex(offset++, particle, 0, 1);
             }
-            var engine = this._scene.getEngine();
-            engine.updateDynamicVertexBuffer(this._vertexBuffer, this._vertices);
+            this._vertexBuffer.update(this._vertexData);
         };
         ParticleSystem.prototype.render = function () {
             var effect = this._getEffect();
@@ -296,7 +300,7 @@ var BABYLON;
                 effect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
             }
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
             // Draw order
             if (this.blendMode === ParticleSystem.BLENDMODE_ONEONE) {
                 engine.setAlphaMode(BABYLON.Engine.ALPHA_ONEONE);
@@ -313,7 +317,7 @@ var BABYLON;
         };
         ParticleSystem.prototype.dispose = function () {
             if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+                this._vertexBuffer.dispose();
                 this._vertexBuffer = null;
             }
             if (this._indexBuffer) {
@@ -334,7 +338,7 @@ var BABYLON;
         // Clone
         ParticleSystem.prototype.clone = function (name, newEmitter) {
             var result = new ParticleSystem(name, this._capacity, this._scene);
-            BABYLON.Tools.DeepCopy(this, result, ["particles"], ["_vertexDeclaration", "_vertexStrideSize"]);
+            BABYLON.Tools.DeepCopy(this, result, ["particles"]);
             if (newEmitter === undefined) {
                 newEmitter = this.emitter;
             }

+ 31 - 25
src/Particles/babylon.particleSystem.ts

@@ -77,13 +77,12 @@
 
         private _capacity: number;
         private _scene: Scene;
-        private _vertexDeclaration = [3, 4, 4];
-        private _vertexStrideSize = 11 * 4; // 11 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY)
         private _stockParticles = new Array<Particle>();
         private _newPartsExcess = 0;
-        private _vertexBuffer: WebGLBuffer;
+        private _vertexData: Float32Array;
+        private _vertexBuffer: Buffer;
+        private _vertexBuffers: { [key: string]: VertexBuffer } = {};
         private _indexBuffer: WebGLBuffer;
-        private _vertices: Float32Array;
         private _effect: Effect;
         private _customEffect: Effect;
         private _cachedDefines: string;
@@ -110,9 +109,6 @@
 
             scene.particleSystems.push(this);
 
-            // VBO
-            this._vertexBuffer = scene.getEngine().createDynamicVertexBuffer(capacity * this._vertexStrideSize * 4);
-
             var indices = [];
             var index = 0;
             for (var count = 0; count < capacity; count++) {
@@ -127,7 +123,17 @@
 
             this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
 
-            this._vertices = new Float32Array(capacity * this._vertexStrideSize);
+            // 11 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY) + 1 filler
+            this._vertexData = new Float32Array(capacity * 11 * 4);
+            this._vertexBuffer = new Buffer(scene.getEngine(), this._vertexData, true, 11);
+
+            var positions = this._vertexBuffer.createVertexBuffer(VertexBuffer.PositionKind, 0, 3);
+            var colors = this._vertexBuffer.createVertexBuffer(VertexBuffer.ColorKind, 3, 4);
+            var options = this._vertexBuffer.createVertexBuffer("options", 7, 4);
+
+            this._vertexBuffers[VertexBuffer.PositionKind] = positions;
+            this._vertexBuffers[VertexBuffer.ColorKind] = colors;
+            this._vertexBuffers["options"] = options;
 
             // Default behaviors
             this.startDirectionFunction = (emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void => {
@@ -208,17 +214,17 @@
 
         public _appendParticleVertex(index: number, particle: Particle, offsetX: number, offsetY: number): void {
             var offset = index * 11;
-            this._vertices[offset] = particle.position.x;
-            this._vertices[offset + 1] = particle.position.y;
-            this._vertices[offset + 2] = particle.position.z;
-            this._vertices[offset + 3] = particle.color.r;
-            this._vertices[offset + 4] = particle.color.g;
-            this._vertices[offset + 5] = particle.color.b;
-            this._vertices[offset + 6] = particle.color.a;
-            this._vertices[offset + 7] = particle.angle;
-            this._vertices[offset + 8] = particle.size;
-            this._vertices[offset + 9] = offsetX;
-            this._vertices[offset + 10] = offsetY;
+            this._vertexData[offset] = particle.position.x;
+            this._vertexData[offset + 1] = particle.position.y;
+            this._vertexData[offset + 2] = particle.position.z;
+            this._vertexData[offset + 3] = particle.color.r;
+            this._vertexData[offset + 4] = particle.color.g;
+            this._vertexData[offset + 5] = particle.color.b;
+            this._vertexData[offset + 6] = particle.color.a;
+            this._vertexData[offset + 7] = particle.angle;
+            this._vertexData[offset + 8] = particle.size;
+            this._vertexData[offset + 9] = offsetX;
+            this._vertexData[offset + 10] = offsetY;
         }
 
         private _update(newParticles: number): void {
@@ -287,7 +293,7 @@
 
                 this._effect = this._scene.getEngine().createEffect(
                     "particles",
-                    ["position", "color", "options"],
+                    [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options"],
                     ["invView", "view", "projection", "vClipPlane", "textureMask"],
                     ["diffuseSampler"], join);
             }
@@ -363,8 +369,8 @@
                 this._appendParticleVertex(offset++, particle, 1, 1);
                 this._appendParticleVertex(offset++, particle, 0, 1);
             }
-            var engine = this._scene.getEngine();
-            engine.updateDynamicVertexBuffer(this._vertexBuffer, this._vertices);
+
+            this._vertexBuffer.update(this._vertexData);
         }
 
         public render(): number {
@@ -395,7 +401,7 @@
             }
 
             // VBOs
-            engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
 
             // Draw order
             if (this.blendMode === ParticleSystem.BLENDMODE_ONEONE) {
@@ -416,7 +422,7 @@
 
         public dispose(): void {
             if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+                this._vertexBuffer.dispose();
                 this._vertexBuffer = null;
             }
 
@@ -443,7 +449,7 @@
         public clone(name: string, newEmitter: any): ParticleSystem {
             var result = new ParticleSystem(name, this._capacity, this._scene);
 
-            Tools.DeepCopy(this, result, ["particles"], ["_vertexDeclaration", "_vertexStrideSize"]);
+            Tools.DeepCopy(this, result, ["particles"]);
 
             if (newEmitter === undefined) {
                 newEmitter = this.emitter;

+ 6 - 6
src/Particles/babylon.solidParticleSystem.js

@@ -8,7 +8,7 @@ var BABYLON;
         * Creates a SPS (Solid Particle System) object.
         * `name` (String) is the SPS name, this will be the underlying mesh name.
         * `scene` (Scene) is the scene in which the SPS is added.
-        * `updatableè (default true) : if the SPS must be updatable or immutable.
+        * `updatable` (default true) : if the SPS must be updatable or immutable.
         * `isPickable` (default false) : if the solid particles must be pickable.
         */
         function SolidParticleSystem(name, scene, options) {
@@ -140,7 +140,7 @@ var BABYLON;
         * Digests the mesh and generates as many solid particles in the system as wanted. Returns the SPS.
         * These particles will have the same geometry than the mesh parts and will be positioned at the same localisation than the mesh original places.
         * Thus the particles generated from `digest()` have their property `position` set yet.
-        * `mesh` (`Mesh`) is the mesh to be digested
+        * `mesh` ( Mesh ) is the mesh to be digested
         * `facetNb` (optional integer, default 1) is the number of mesh facets per particle, this parameter is overriden by the parameter `number` if any
         * `delta` (optional integer, default 0) is the random extra number of facets per particle , each particle will have between `facetNb` and `facetNb + delta` facets
         * `number` (optional positive integer) is the wanted number of particles : each particle is built with `mesh_total_facets / number` facets
@@ -328,7 +328,7 @@ var BABYLON;
         /**
         * Adds some particles to the SPS from the model shape. Returns the shape id.
         * Please read the doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#create-an-immutable-sps
-        * `mesh` is any `Mesh` object that will be used as a model for the solid particles.
+        * `mesh` is any Mesh object that will be used as a model for the solid particles.
         * `nb` (positive integer) the number of particles to be created from this model
         * `positionFunction` is an optional javascript function to called for each particle on SPS creation.
         * `vertexFunction` is an optional javascript function to called for each vertex of each particle on SPS creation
@@ -413,9 +413,9 @@ var BABYLON;
         *  Sets all the particles : this method actually really updates the mesh according to the particle positions, rotations, colors, textures, etc.
         *  This method calls `updateParticle()` for each particle of the SPS.
         *  For an animated SPS, it is usually called within the render loop.
-        * @param start (default 0) the particle index in the particle array where to start to compute the particle property values
-        * @param end (default nbParticle - 1)  the particle index in the particle array where to stop to compute the particle property values
-        * @param update (default true) if the mesh must be finally updated on this call after all the particle computations.
+        * @param start The particle index in the particle array where to start to compute the particle property values _(default 0)_
+        * @param end The particle index in the particle array where to stop to compute the particle property values _(default nbParticle - 1)_
+        * @param update If the mesh must be finally updated on this call after all the particle computations _(default true)_
         */
         SolidParticleSystem.prototype.setParticles = function (start, end, update) {
             if (start === void 0) { start = 0; }

+ 6 - 6
src/Particles/babylon.solidParticleSystem.ts

@@ -108,7 +108,7 @@
         * Creates a SPS (Solid Particle System) object.
         * `name` (String) is the SPS name, this will be the underlying mesh name.  
         * `scene` (Scene) is the scene in which the SPS is added.  
-        * `updatableè (default true) : if the SPS must be updatable or immutable.  
+        * `updatable` (default true) : if the SPS must be updatable or immutable.  
         * `isPickable` (default false) : if the solid particles must be pickable.  
         */
         constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean }) {
@@ -174,7 +174,7 @@
         * Digests the mesh and generates as many solid particles in the system as wanted. Returns the SPS.  
         * These particles will have the same geometry than the mesh parts and will be positioned at the same localisation than the mesh original places.
         * Thus the particles generated from `digest()` have their property `position` set yet.  
-        * `mesh` (`Mesh`) is the mesh to be digested  
+        * `mesh` ( Mesh ) is the mesh to be digested  
         * `facetNb` (optional integer, default 1) is the number of mesh facets per particle, this parameter is overriden by the parameter `number` if any
         * `delta` (optional integer, default 0) is the random extra number of facets per particle , each particle will have between `facetNb` and `facetNb + delta` facets
         * `number` (optional positive integer) is the wanted number of particles : each particle is built with `mesh_total_facets / number` facets
@@ -383,7 +383,7 @@
         /**
         * Adds some particles to the SPS from the model shape. Returns the shape id.   
         * Please read the doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#create-an-immutable-sps
-        * `mesh` is any `Mesh` object that will be used as a model for the solid particles.
+        * `mesh` is any Mesh object that will be used as a model for the solid particles.
         * `nb` (positive integer) the number of particles to be created from this model
         * `positionFunction` is an optional javascript function to called for each particle on SPS creation. 
         * `vertexFunction` is an optional javascript function to called for each vertex of each particle on SPS creation
@@ -481,9 +481,9 @@
         *  Sets all the particles : this method actually really updates the mesh according to the particle positions, rotations, colors, textures, etc.
         *  This method calls `updateParticle()` for each particle of the SPS.
         *  For an animated SPS, it is usually called within the render loop.
-        * @param start (default 0) the particle index in the particle array where to start to compute the particle property values
-        * @param end (default nbParticle - 1)  the particle index in the particle array where to stop to compute the particle property values
-        * @param update (default true) if the mesh must be finally updated on this call after all the particle computations.
+        * @param start The particle index in the particle array where to start to compute the particle property values _(default 0)_
+        * @param end The particle index in the particle array where to stop to compute the particle property values _(default nbParticle - 1)_
+        * @param update If the mesh must be finally updated on this call after all the particle computations _(default true)_
         */
         public setParticles(start: number = 0, end: number = this.nbParticles - 1, update: boolean = true): void {
             if (!this._updatable) {

+ 2 - 2
src/PostProcess/babylon.anaglyphPostProcess.js

@@ -7,9 +7,9 @@ var BABYLON;
 (function (BABYLON) {
     var AnaglyphPostProcess = (function (_super) {
         __extends(AnaglyphPostProcess, _super);
-        function AnaglyphPostProcess(name, ratio, rigCameras, samplingMode, engine, reusable) {
+        function AnaglyphPostProcess(name, options, rigCameras, samplingMode, engine, reusable) {
             var _this = this;
-            _super.call(this, name, "anaglyph", null, ["leftSampler"], ratio, rigCameras[1], samplingMode, engine, reusable);
+            _super.call(this, name, "anaglyph", null, ["leftSampler"], options, rigCameras[1], samplingMode, engine, reusable);
             this._passedProcess = rigCameras[0]._rigPostProcess;
             this.onApplyObservable.add(function (effect) {
                 effect.setTextureFromPostProcess("leftSampler", _this._passedProcess);

+ 2 - 2
src/PostProcess/babylon.anaglyphPostProcess.ts

@@ -2,8 +2,8 @@
     export class AnaglyphPostProcess extends PostProcess {
         private _passedProcess : PostProcess;
 
-        constructor(name: string, ratio: number,  rigCameras: Camera[], samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "anaglyph", null, ["leftSampler"], ratio, rigCameras[1], samplingMode, engine, reusable);
+        constructor(name: string, options: number | PostProcessOptions,  rigCameras: Camera[], samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "anaglyph", null, ["leftSampler"], options, rigCameras[1], samplingMode, engine, reusable);
             this._passedProcess = rigCameras[0]._rigPostProcess;
 
             this.onApplyObservable.add((effect: Effect) => {

+ 2 - 2
src/PostProcess/babylon.blackAndWhitePostProcess.js

@@ -7,8 +7,8 @@ var BABYLON;
 (function (BABYLON) {
     var BlackAndWhitePostProcess = (function (_super) {
         __extends(BlackAndWhitePostProcess, _super);
-        function BlackAndWhitePostProcess(name, ratio, camera, samplingMode, engine, reusable) {
-            _super.call(this, name, "blackAndWhite", null, null, ratio, camera, samplingMode, engine, reusable);
+        function BlackAndWhitePostProcess(name, options, camera, samplingMode, engine, reusable) {
+            _super.call(this, name, "blackAndWhite", null, null, options, camera, samplingMode, engine, reusable);
         }
         return BlackAndWhitePostProcess;
     })(BABYLON.PostProcess);

+ 2 - 2
src/PostProcess/babylon.blackAndWhitePostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class BlackAndWhitePostProcess extends PostProcess {
-        constructor(name: string, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "blackAndWhite", null, null, ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "blackAndWhite", null, null, options, camera, samplingMode, engine, reusable);
         }
     }
 } 

+ 2 - 2
src/PostProcess/babylon.blurPostProcess.js

@@ -7,10 +7,10 @@ var BABYLON;
 (function (BABYLON) {
     var BlurPostProcess = (function (_super) {
         __extends(BlurPostProcess, _super);
-        function BlurPostProcess(name, direction, blurWidth, ratio, camera, samplingMode, engine, reusable) {
+        function BlurPostProcess(name, direction, blurWidth, options, camera, samplingMode, engine, reusable) {
             var _this = this;
             if (samplingMode === void 0) { samplingMode = BABYLON.Texture.BILINEAR_SAMPLINGMODE; }
-            _super.call(this, name, "blur", ["screenSize", "direction", "blurWidth"], null, ratio, camera, samplingMode, engine, reusable);
+            _super.call(this, name, "blur", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
             this.direction = direction;
             this.blurWidth = blurWidth;
             this.onApplyObservable.add(function (effect) {

+ 2 - 2
src/PostProcess/babylon.blurPostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class BlurPostProcess extends PostProcess {
-        constructor(name: string, public direction: Vector2, public blurWidth: number, ratio: number, camera: Camera, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
-            super(name, "blur", ["screenSize", "direction", "blurWidth"], null, ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, public direction: Vector2, public blurWidth: number, options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
+            super(name, "blur", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
             this.onApplyObservable.add((effect: Effect) => {
                 effect.setFloat2("screenSize", this.width, this.height);
                 effect.setVector2("direction", this.direction);

+ 2 - 2
src/PostProcess/babylon.colorCorrectionPostProcess.js

@@ -20,9 +20,9 @@ var BABYLON;
 (function (BABYLON) {
     var ColorCorrectionPostProcess = (function (_super) {
         __extends(ColorCorrectionPostProcess, _super);
-        function ColorCorrectionPostProcess(name, colorTableUrl, ratio, camera, samplingMode, engine, reusable) {
+        function ColorCorrectionPostProcess(name, colorTableUrl, options, camera, samplingMode, engine, reusable) {
             var _this = this;
-            _super.call(this, name, 'colorCorrection', null, ['colorTable'], ratio, camera, samplingMode, engine, reusable);
+            _super.call(this, name, 'colorCorrection', null, ['colorTable'], options, camera, samplingMode, engine, reusable);
             this._colorTableTexture = new BABYLON.Texture(colorTableUrl, camera.getScene(), true, false, BABYLON.Texture.TRILINEAR_SAMPLINGMODE);
             this._colorTableTexture.anisotropicFilteringLevel = 1;
             this._colorTableTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;

+ 2 - 2
src/PostProcess/babylon.colorCorrectionPostProcess.ts

@@ -17,8 +17,8 @@ module BABYLON {
 
         private _colorTableTexture: Texture;
 
-        constructor(name: string, colorTableUrl: string, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, 'colorCorrection', null, ['colorTable'], ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, colorTableUrl: string, options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, 'colorCorrection', null, ['colorTable'], options, camera, samplingMode, engine, reusable);
 
             this._colorTableTexture = new Texture(colorTableUrl, camera.getScene(), true, false, Texture.TRILINEAR_SAMPLINGMODE);
             this._colorTableTexture.anisotropicFilteringLevel = 1;

+ 2 - 2
src/PostProcess/babylon.convolutionPostProcess.js

@@ -7,9 +7,9 @@ var BABYLON;
 (function (BABYLON) {
     var ConvolutionPostProcess = (function (_super) {
         __extends(ConvolutionPostProcess, _super);
-        function ConvolutionPostProcess(name, kernel, ratio, camera, samplingMode, engine, reusable) {
+        function ConvolutionPostProcess(name, kernel, options, camera, samplingMode, engine, reusable) {
             var _this = this;
-            _super.call(this, name, "convolution", ["kernel", "screenSize"], null, ratio, camera, samplingMode, engine, reusable);
+            _super.call(this, name, "convolution", ["kernel", "screenSize"], null, options, camera, samplingMode, engine, reusable);
             this.kernel = kernel;
             this.onApply = function (effect) {
                 effect.setFloat2("screenSize", _this.width, _this.height);

+ 2 - 2
src/PostProcess/babylon.convolutionPostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class ConvolutionPostProcess extends PostProcess{
-        constructor(name: string, public kernel: number[], ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "convolution", ["kernel", "screenSize"], null, ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, public kernel: number[], options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "convolution", ["kernel", "screenSize"], null, options, camera, samplingMode, engine, reusable);
 
             this.onApply = (effect: Effect) => {
                 effect.setFloat2("screenSize", this.width, this.height);

+ 2 - 2
src/PostProcess/babylon.displayPassPostProcess.js

@@ -7,8 +7,8 @@ var BABYLON;
 (function (BABYLON) {
     var DisplayPassPostProcess = (function (_super) {
         __extends(DisplayPassPostProcess, _super);
-        function DisplayPassPostProcess(name, ratio, camera, samplingMode, engine, reusable) {
-            _super.call(this, name, "displayPass", ["passSampler"], ["passSampler"], ratio, camera, samplingMode, engine, reusable);
+        function DisplayPassPostProcess(name, options, camera, samplingMode, engine, reusable) {
+            _super.call(this, name, "displayPass", ["passSampler"], ["passSampler"], options, camera, samplingMode, engine, reusable);
         }
         return DisplayPassPostProcess;
     })(BABYLON.PostProcess);

+ 2 - 2
src/PostProcess/babylon.displayPassPostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class DisplayPassPostProcess extends PostProcess {
-        constructor(name: string, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "displayPass", ["passSampler"], ["passSampler"], ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "displayPass", ["passSampler"], ["passSampler"], options, camera, samplingMode, engine, reusable);
         }
     }
 }

+ 2 - 2
src/PostProcess/babylon.filterPostProcess.js

@@ -7,9 +7,9 @@ var BABYLON;
 (function (BABYLON) {
     var FilterPostProcess = (function (_super) {
         __extends(FilterPostProcess, _super);
-        function FilterPostProcess(name, kernelMatrix, ratio, camera, samplingMode, engine, reusable) {
+        function FilterPostProcess(name, kernelMatrix, options, camera, samplingMode, engine, reusable) {
             var _this = this;
-            _super.call(this, name, "filter", ["kernelMatrix"], null, ratio, camera, samplingMode, engine, reusable);
+            _super.call(this, name, "filter", ["kernelMatrix"], null, options, camera, samplingMode, engine, reusable);
             this.kernelMatrix = kernelMatrix;
             this.onApply = function (effect) {
                 effect.setMatrix("kernelMatrix", _this.kernelMatrix);

+ 2 - 2
src/PostProcess/babylon.filterPostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class FilterPostProcess extends PostProcess {
-        constructor(name: string, public kernelMatrix: Matrix, ratio: number, camera?: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "filter", ["kernelMatrix"], null, ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, public kernelMatrix: Matrix, options: number | PostProcessOptions, camera?: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "filter", ["kernelMatrix"], null, options, camera, samplingMode, engine, reusable);
 
             this.onApply = (effect: Effect) => {
                 effect.setMatrix("kernelMatrix", this.kernelMatrix);

+ 2 - 2
src/PostProcess/babylon.fxaaPostProcess.js

@@ -7,9 +7,9 @@ var BABYLON;
 (function (BABYLON) {
     var FxaaPostProcess = (function (_super) {
         __extends(FxaaPostProcess, _super);
-        function FxaaPostProcess(name, ratio, camera, samplingMode, engine, reusable) {
+        function FxaaPostProcess(name, options, camera, samplingMode, engine, reusable) {
             var _this = this;
-            _super.call(this, name, "fxaa", ["texelSize"], null, ratio, camera, samplingMode, engine, reusable);
+            _super.call(this, name, "fxaa", ["texelSize"], null, options, camera, samplingMode, engine, reusable);
             this.onSizeChangedObservable.add(function () {
                 _this.texelWidth = 1.0 / _this.width;
                 _this.texelHeight = 1.0 / _this.height;

+ 2 - 2
src/PostProcess/babylon.fxaaPostProcess.ts

@@ -3,8 +3,8 @@
         public texelWidth: number;
         public texelHeight: number;
 
-        constructor(name: string, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "fxaa", ["texelSize"], null, ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "fxaa", ["texelSize"], null, options, camera, samplingMode, engine, reusable);
 
             this.onSizeChangedObservable.add(() => {
                 this.texelWidth = 1.0 / this.width;

+ 2 - 2
src/PostProcess/babylon.passPostProcess.js

@@ -7,8 +7,8 @@ var BABYLON;
 (function (BABYLON) {
     var PassPostProcess = (function (_super) {
         __extends(PassPostProcess, _super);
-        function PassPostProcess(name, ratio, camera, samplingMode, engine, reusable) {
-            _super.call(this, name, "pass", null, null, ratio, camera, samplingMode, engine, reusable);
+        function PassPostProcess(name, options, camera, samplingMode, engine, reusable) {
+            _super.call(this, name, "pass", null, null, options, camera, samplingMode, engine, reusable);
         }
         return PassPostProcess;
     })(BABYLON.PostProcess);

+ 2 - 2
src/PostProcess/babylon.passPostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
     export class PassPostProcess extends PostProcess {
-        constructor(name: string, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "pass", null, null, ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "pass", null, null, options, camera, samplingMode, engine, reusable);
         }
     }
 } 

+ 8 - 8
src/PostProcess/babylon.postProcess.js

@@ -1,7 +1,7 @@
 var BABYLON;
 (function (BABYLON) {
     var PostProcess = (function () {
-        function PostProcess(name, fragmentUrl, parameters, samplers, ratio, camera, samplingMode, engine, reusable, defines, textureType) {
+        function PostProcess(name, fragmentUrl, parameters, samplers, options, camera, samplingMode, engine, reusable, defines, textureType) {
             if (samplingMode === void 0) { samplingMode = BABYLON.Texture.NEAREST_SAMPLINGMODE; }
             if (textureType === void 0) { textureType = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT; }
             this.name = name;
@@ -51,7 +51,7 @@ var BABYLON;
             else {
                 this._engine = engine;
             }
-            this._renderRatio = ratio;
+            this._options = options;
             this.renderTargetSamplingMode = samplingMode ? samplingMode : BABYLON.Texture.NEAREST_SAMPLINGMODE;
             this._reusable = reusable || false;
             this._textureType = textureType;
@@ -126,15 +126,15 @@ var BABYLON;
             camera = camera || this._camera;
             var scene = camera.getScene();
             var maxSize = camera.getEngine().getCaps().maxTextureSize;
-            var requiredWidth = ((sourceTexture ? sourceTexture._width : this._engine.getRenderingCanvas().width) * this._renderRatio) | 0;
-            var requiredHeight = ((sourceTexture ? sourceTexture._height : this._engine.getRenderingCanvas().height) * this._renderRatio) | 0;
-            var desiredWidth = this._renderRatio.width || requiredWidth;
-            var desiredHeight = this._renderRatio.height || requiredHeight;
+            var requiredWidth = ((sourceTexture ? sourceTexture._width : this._engine.getRenderingCanvas().width) * this._options) | 0;
+            var requiredHeight = ((sourceTexture ? sourceTexture._height : this._engine.getRenderingCanvas().height) * this._options) | 0;
+            var desiredWidth = this._options.width || requiredWidth;
+            var desiredHeight = this._options.height || requiredHeight;
             if (this.renderTargetSamplingMode !== BABYLON.Texture.NEAREST_SAMPLINGMODE) {
-                if (!this._renderRatio.width) {
+                if (!this._options.width) {
                     desiredWidth = BABYLON.Tools.GetExponentOfTwo(desiredWidth, maxSize);
                 }
-                if (!this._renderRatio.height) {
+                if (!this._options.height) {
                     desiredHeight = BABYLON.Tools.GetExponentOfTwo(desiredHeight, maxSize);
                 }
             }

+ 13 - 10
src/PostProcess/babylon.postProcess.ts

@@ -1,5 +1,8 @@
 module BABYLON {
-    export class PostProcess {
+    export type PostProcessOptions = { width: number, height: number };
+
+    export class PostProcess
+    {
         public width = -1;
         public height = -1;
         public renderTargetSamplingMode: number;
@@ -14,7 +17,7 @@
         private _camera: Camera;
         private _scene: Scene;
         private _engine: Engine;
-        private _renderRatio: number|any;
+        private _options: number | PostProcessOptions;
         private _reusable = false;
         private _textureType: number;
         public _textures = new SmartArray<WebGLTexture>(2);
@@ -97,7 +100,7 @@
             this._onAfterRenderObserver = this.onAfterRenderObservable.add(callback);
         }
 
-        constructor(public name: string, fragmentUrl: string, parameters: string[], samplers: string[], ratio: number|any, camera: Camera, samplingMode: number = Texture.NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines?: string, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
+        constructor(public name: string, fragmentUrl: string, parameters: string[], samplers: string[], options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines?: string, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
             if (camera != null) {
                 this._camera = camera;
                 this._scene = camera.getScene();
@@ -108,7 +111,7 @@
                 this._engine = engine;
             }
 
-            this._renderRatio = ratio;
+            this._options = options;
             this.renderTargetSamplingMode = samplingMode ? samplingMode : Texture.NEAREST_SAMPLINGMODE;
             this._reusable = reusable || false;
             this._textureType = textureType;
@@ -146,18 +149,18 @@
             var scene = camera.getScene();
             var maxSize = camera.getEngine().getCaps().maxTextureSize;
 
-            var requiredWidth = ((sourceTexture ? sourceTexture._width : this._engine.getRenderingCanvas().width) * this._renderRatio) | 0;
-            var requiredHeight = ((sourceTexture ? sourceTexture._height : this._engine.getRenderingCanvas().height) * this._renderRatio) | 0;
+            var requiredWidth = ((sourceTexture ? sourceTexture._width : this._engine.getRenderingCanvas().width) * <number>this._options) | 0;
+            var requiredHeight = ((sourceTexture ? sourceTexture._height : this._engine.getRenderingCanvas().height) * <number>this._options) | 0;
 
-            var desiredWidth = this._renderRatio.width || requiredWidth;
-            var desiredHeight = this._renderRatio.height || requiredHeight;
+            var desiredWidth = (<PostProcessOptions>this._options).width || requiredWidth;
+            var desiredHeight = (<PostProcessOptions>this._options).height || requiredHeight;
 
             if (this.renderTargetSamplingMode !== Texture.NEAREST_SAMPLINGMODE) {
-                if (!this._renderRatio.width) {
+                if (!(<PostProcessOptions>this._options).width) {
                     desiredWidth = Tools.GetExponentOfTwo(desiredWidth, maxSize);
                 }
 
-                if (!this._renderRatio.height) {
+                if (!(<PostProcessOptions>this._options).height) {
                     desiredHeight = Tools.GetExponentOfTwo(desiredHeight, maxSize);
                 }
             }

+ 9 - 9
src/PostProcess/babylon.postProcessManager.js

@@ -2,12 +2,11 @@ var BABYLON;
 (function (BABYLON) {
     var PostProcessManager = (function () {
         function PostProcessManager(scene) {
-            this._vertexDeclaration = [2];
-            this._vertexStrideSize = 2 * 4;
+            this._vertexBuffers = {};
             this._scene = scene;
         }
         PostProcessManager.prototype._prepareBuffers = function () {
-            if (this._vertexBuffer) {
+            if (this._vertexBuffers[BABYLON.VertexBuffer.PositionKind]) {
                 return;
             }
             // VBO
@@ -16,7 +15,7 @@ var BABYLON;
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
+            this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = new BABYLON.VertexBuffer(this._scene.getEngine(), vertices, BABYLON.VertexBuffer.PositionKind, false, false, 2);
             // Indices
             var indices = [];
             indices.push(0);
@@ -56,7 +55,7 @@ var BABYLON;
                     pp.onBeforeRenderObservable.notifyObservers(effect);
                     // VBOs
                     this._prepareBuffers();
-                    engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
+                    engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
                     // Draw order
                     engine.draw(true, 0, 6);
                     pp.onAfterRenderObservable.notifyObservers(effect);
@@ -93,7 +92,7 @@ var BABYLON;
                     pp.onBeforeRenderObservable.notifyObservers(effect);
                     // VBOs
                     this._prepareBuffers();
-                    engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
+                    engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
                     // Draw order
                     engine.draw(true, 0, 6);
                     pp.onAfterRenderObservable.notifyObservers(effect);
@@ -104,9 +103,10 @@ var BABYLON;
             engine.setDepthWrite(true);
         };
         PostProcessManager.prototype.dispose = function () {
-            if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
-                this._vertexBuffer = null;
+            var buffer = this._vertexBuffers[BABYLON.VertexBuffer.PositionKind];
+            if (buffer) {
+                buffer.dispose();
+                this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = null;
             }
             if (this._indexBuffer) {
                 this._scene.getEngine()._releaseBuffer(this._indexBuffer);

+ 11 - 11
src/PostProcess/babylon.postProcessManager.ts

@@ -2,16 +2,14 @@
     export class PostProcessManager {
         private _scene: Scene;
         private _indexBuffer: WebGLBuffer;
-        private _vertexDeclaration = [2];
-        private _vertexStrideSize = 2 * 4;
-        private _vertexBuffer: WebGLBuffer;
+        private _vertexBuffers: { [key: string]: VertexBuffer } = {};
 
         constructor(scene: Scene) {
             this._scene = scene;
         }
 
         private _prepareBuffers(): void {
-            if (this._vertexBuffer) {
+            if (this._vertexBuffers[VertexBuffer.PositionKind]) {
                 return;
             }
 
@@ -21,7 +19,8 @@
             vertices.push(-1, 1);
             vertices.push(-1, -1);
             vertices.push(1, -1);
-            this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
+
+            this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(this._scene.getEngine(), vertices, VertexBuffer.PositionKind, false, false, 2);
 
             // Indices
             var indices = [];
@@ -70,12 +69,12 @@
 
                     // VBOs
                     this._prepareBuffers();
-                    engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
+                    engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
 
                     // Draw order
                     engine.draw(true, 0, 6);
 
-                    pp.onAfterRenderObservable.notifyObservers(effect);                    
+                    pp.onAfterRenderObservable.notifyObservers(effect);
                 }
             }
 
@@ -114,7 +113,7 @@
 
                     // VBOs
                     this._prepareBuffers();
-                    engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, effect);
+                    engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
 
                     // Draw order
                     engine.draw(true, 0, 6);
@@ -129,9 +128,10 @@
         }
 
         public dispose(): void {
-            if (this._vertexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
-                this._vertexBuffer = null;
+            var buffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (buffer) {
+                buffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
             }
 
             if (this._indexBuffer) {

+ 2 - 2
src/PostProcess/babylon.refractionPostProcess.js

@@ -7,9 +7,9 @@ var BABYLON;
 (function (BABYLON) {
     var RefractionPostProcess = (function (_super) {
         __extends(RefractionPostProcess, _super);
-        function RefractionPostProcess(name, refractionTextureUrl, color, depth, colorLevel, ratio, camera, samplingMode, engine, reusable) {
+        function RefractionPostProcess(name, refractionTextureUrl, color, depth, colorLevel, options, camera, samplingMode, engine, reusable) {
             var _this = this;
-            _super.call(this, name, "refraction", ["baseColor", "depth", "colorLevel"], ["refractionSampler"], ratio, camera, samplingMode, engine, reusable);
+            _super.call(this, name, "refraction", ["baseColor", "depth", "colorLevel"], ["refractionSampler"], options, camera, samplingMode, engine, reusable);
             this.color = color;
             this.depth = depth;
             this.colorLevel = colorLevel;

+ 2 - 2
src/PostProcess/babylon.refractionPostProcess.ts

@@ -1,8 +1,8 @@
 module BABYLON {
     export class RefractionPostProcess extends PostProcess {
         private _refRexture: Texture;
-        constructor(name: string, refractionTextureUrl: string, public color: Color3, public depth: number, public colorLevel: number, ratio: number, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
-            super(name, "refraction", ["baseColor", "depth", "colorLevel"], ["refractionSampler"], ratio, camera, samplingMode, engine, reusable);
+        constructor(name: string, refractionTextureUrl: string, public color: Color3, public depth: number, public colorLevel: number, options: number | PostProcessOptions, camera: Camera, samplingMode?: number, engine?: Engine, reusable?: boolean) {
+            super(name, "refraction", ["baseColor", "depth", "colorLevel"], ["refractionSampler"], options, camera, samplingMode, engine, reusable);
 
             this.onActivateObservable.add((cam: Camera) => {
                 this._refRexture = this._refRexture || new Texture(refractionTextureUrl, cam.getScene());

+ 78 - 14
src/PostProcess/babylon.ssaoRenderingPipeline.js

@@ -3,6 +3,12 @@ var __extends = (this && this.__extends) || function (d, b) {
     function __() { this.constructor = d; }
     d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
 };
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
 var BABYLON;
 (function (BABYLON) {
     var SSAORenderingPipeline = (function (_super) {
@@ -80,10 +86,13 @@ var BABYLON;
             this._depthTexture = scene.enableDepthRenderer().getDepthMap(); // Force depth renderer "on"
             var ssaoRatio = ratio.ssaoRatio || ratio;
             var combineRatio = ratio.combineRatio || ratio;
+            this._ratio = {
+                ssaoRatio: ssaoRatio,
+                combineRatio: combineRatio
+            };
             this._originalColorPostProcess = new BABYLON.PassPostProcess("SSAOOriginalSceneColor", combineRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
             this._createSSAOPostProcess(ssaoRatio);
-            this._blurHPostProcess = new BABYLON.BlurPostProcess("SSAOBlurH", new BABYLON.Vector2(1.0, 0.0), 2.0, ssaoRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
-            this._blurVPostProcess = new BABYLON.BlurPostProcess("SSAOBlurV", new BABYLON.Vector2(0.0, 1.0), 2.0, ssaoRatio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._createBlurPostProcess(ssaoRatio);
             this._createSSAOCombinePostProcess(combineRatio);
             // Set up pipeline
             this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), this.SSAOOriginalSceneColorEffect, function () { return _this._originalColorPostProcess; }, true));
@@ -102,31 +111,68 @@ var BABYLON;
          * @return {BABYLON.BlurPostProcess} The horizontal blur post-process
          */
         SSAORenderingPipeline.prototype.getBlurHPostProcess = function () {
-            return this._blurHPostProcess;
+            BABYLON.Tools.Error("SSAORenderinPipeline.getBlurHPostProcess() is deprecated, no more blur post-process exists");
+            return null;
         };
         /**
          * Returns the vertical blur PostProcess
          * @return {BABYLON.BlurPostProcess} The vertical blur post-process
          */
         SSAORenderingPipeline.prototype.getBlurVPostProcess = function () {
-            return this._blurVPostProcess;
+            BABYLON.Tools.Error("SSAORenderinPipeline.getBlurVPostProcess() is deprecated, no more blur post-process exists");
+            return null;
         };
         /**
          * Removes the internal pipeline assets and detatches the pipeline from the scene cameras
          */
         SSAORenderingPipeline.prototype.dispose = function (disableDepthRender) {
             if (disableDepthRender === void 0) { disableDepthRender = false; }
-            this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
-            this._originalColorPostProcess = undefined;
-            this._ssaoPostProcess = undefined;
-            this._blurHPostProcess = undefined;
-            this._blurVPostProcess = undefined;
-            this._ssaoCombinePostProcess = undefined;
+            for (var i = 0; i < this._scene.cameras.length; i++) {
+                var camera = this._scene.cameras[i];
+                this._originalColorPostProcess.dispose(camera);
+                this._ssaoPostProcess.dispose(camera);
+                this._blurHPostProcess.dispose(camera);
+                this._blurVPostProcess.dispose(camera);
+                this._ssaoCombinePostProcess.dispose(camera);
+            }
             this._randomTexture.dispose();
             if (disableDepthRender)
                 this._scene.disableDepthRenderer();
+            this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
         };
         // Private Methods
+        SSAORenderingPipeline.prototype._createBlurPostProcess = function (ratio) {
+            var _this = this;
+            /*
+            var samplerOffsets = [
+                -8.0, -6.0, -4.0, -2.0,
+                0.0,
+                2.0, 4.0, 6.0, 8.0
+            ];
+            */
+            var samples = 16;
+            var samplerOffsets = [];
+            for (var i = -8; i < 8; i++) {
+                samplerOffsets.push(i * 2);
+            }
+            this._blurHPostProcess = new BABYLON.PostProcess("BlurH", "ssao", ["outSize", "samplerOffsets"], ["depthSampler"], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define BILATERAL_BLUR_H\n#define SAMPLES 16");
+            this._blurHPostProcess.onApply = function (effect) {
+                effect.setFloat("outSize", _this._ssaoCombinePostProcess.width);
+                effect.setTexture("depthSampler", _this._depthTexture);
+                if (_this._firstUpdate) {
+                    effect.setArray("samplerOffsets", samplerOffsets);
+                }
+            };
+            this._blurVPostProcess = new BABYLON.PostProcess("BlurV", "ssao", ["outSize", "samplerOffsets"], ["depthSampler"], ratio, null, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define SAMPLES 16");
+            this._blurVPostProcess.onApply = function (effect) {
+                effect.setFloat("outSize", _this._ssaoCombinePostProcess.height);
+                effect.setTexture("depthSampler", _this._depthTexture);
+                if (_this._firstUpdate) {
+                    effect.setArray("samplerOffsets", samplerOffsets);
+                    _this._firstUpdate = false;
+                }
+            };
+        };
         SSAORenderingPipeline.prototype._createSSAOPostProcess = function (ratio) {
             var _this = this;
             var numSamples = 16;
@@ -151,14 +197,14 @@ var BABYLON;
             var samplesFactor = 1.0 / numSamples;
             this._ssaoPostProcess = new BABYLON.PostProcess("ssao", "ssao", [
                 "sampleSphere", "samplesFactor", "randTextureTiles", "totalStrength", "radius",
-                "area", "fallOff", "base"
-            ], ["randomSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define SAMPLES " + numSamples);
+                "area", "fallOff", "base", "range", "viewport"
+            ], ["randomSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define SAMPLES " + numSamples + "\n#define SSAO");
+            var viewport = new BABYLON.Vector2(0, 0);
             this._ssaoPostProcess.onApply = function (effect) {
                 if (_this._firstUpdate) {
                     effect.setArray3("sampleSphere", sampleSphere);
                     effect.setFloat("samplesFactor", samplesFactor);
                     effect.setFloat("randTextureTiles", 4.0);
-                    _this._firstUpdate = false;
                 }
                 effect.setFloat("totalStrength", _this.totalStrength);
                 effect.setFloat("radius", _this.radius);
@@ -178,7 +224,7 @@ var BABYLON;
         };
         SSAORenderingPipeline.prototype._createRandomTexture = function () {
             var size = 512;
-            this._randomTexture = new BABYLON.DynamicTexture("SSAORandomTexture", size, this._scene, false, BABYLON.Texture.BILINEAR_SAMPLINGMODE);
+            this._randomTexture = new BABYLON.DynamicTexture("SSAORandomTexture", size, this._scene, false, BABYLON.Texture.TRILINEAR_SAMPLINGMODE);
             this._randomTexture.wrapU = BABYLON.Texture.WRAP_ADDRESSMODE;
             this._randomTexture.wrapV = BABYLON.Texture.WRAP_ADDRESSMODE;
             var context = this._randomTexture.getContext();
@@ -197,6 +243,24 @@ var BABYLON;
             }
             this._randomTexture.update(false);
         };
+        __decorate([
+            BABYLON.serialize()
+        ], SSAORenderingPipeline.prototype, "totalStrength", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], SSAORenderingPipeline.prototype, "radius", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], SSAORenderingPipeline.prototype, "area", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], SSAORenderingPipeline.prototype, "fallOff", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], SSAORenderingPipeline.prototype, "base", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], SSAORenderingPipeline.prototype, "_ratio", void 0);
         return SSAORenderingPipeline;
     })(BABYLON.PostProcessRenderPipeline);
     BABYLON.SSAORenderingPipeline = SSAORenderingPipeline;

+ 74 - 17
src/PostProcess/babylon.ssaoRenderingPipeline.ts

@@ -32,12 +32,14 @@
         * The output strength of the SSAO post-process. Default value is 1.0.
         * @type {number}
         */
+        @serialize()
         public totalStrength: number = 1.0;
 
         /**
         * The radius around the analyzed pixel used by the SSAO post-process. Default value is 0.0006
         * @type {number}
         */
+        @serialize()
         public radius: number = 0.0001;
 
         /**
@@ -46,6 +48,7 @@
         * Default value is 0.975
         * @type {number}
         */
+        @serialize()
         public area: number = 0.0075;
 
         /**
@@ -54,6 +57,7 @@
         * Default value is 0.0
         * @type {number}
         */
+        @serialize()
         public fallOff: number = 0.000001;
 
         /**
@@ -61,6 +65,7 @@
         * The final result is "base + ssao" between [0, 1]
         * @type {number}
         */
+        @serialize()
         public base: number = 0.5;
 
         private _scene: Scene;
@@ -69,12 +74,15 @@
 
         private _originalColorPostProcess: PassPostProcess;
         private _ssaoPostProcess: PostProcess;
-        private _blurHPostProcess: BlurPostProcess;
-        private _blurVPostProcess: BlurPostProcess;
+        private _blurHPostProcess: PostProcess;
+        private _blurVPostProcess: PostProcess;
         private _ssaoCombinePostProcess: PostProcess;
 
         private _firstUpdate: boolean = true;
 
+        @serialize()
+        private _ratio: any;
+
         /**
          * @constructor
          * @param {string} name - The rendering pipeline name
@@ -93,11 +101,14 @@
 
             var ssaoRatio = ratio.ssaoRatio || ratio;
             var combineRatio = ratio.combineRatio || ratio;
+            this._ratio = {
+                ssaoRatio: ssaoRatio,
+                combineRatio: combineRatio
+            };
 
             this._originalColorPostProcess = new PassPostProcess("SSAOOriginalSceneColor", combineRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
             this._createSSAOPostProcess(ssaoRatio);
-            this._blurHPostProcess = new BlurPostProcess("SSAOBlurH", new Vector2(1.0, 0.0), 2.0, ssaoRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
-            this._blurVPostProcess = new BlurPostProcess("SSAOBlurV", new Vector2(0.0, 1.0), 2.0, ssaoRatio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._createBlurPostProcess(ssaoRatio);
             this._createSSAOCombinePostProcess(combineRatio);
 
             // Set up pipeline
@@ -105,6 +116,7 @@
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAORenderEffect, () => { return this._ssaoPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOBlurHRenderEffect, () => { return this._blurHPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOBlurVRenderEffect, () => { return this._blurVPostProcess; }, true));
+
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOCombineRenderEffect, () => { return this._ssaoCombinePostProcess; }, true));
 
             // Finish
@@ -119,7 +131,8 @@
          * @return {BABYLON.BlurPostProcess} The horizontal blur post-process
          */
         public getBlurHPostProcess(): BlurPostProcess {
-            return this._blurHPostProcess;
+            Tools.Error("SSAORenderinPipeline.getBlurHPostProcess() is deprecated, no more blur post-process exists");
+            return null;
         }
 
         /**
@@ -127,28 +140,70 @@
          * @return {BABYLON.BlurPostProcess} The vertical blur post-process
          */
         public getBlurVPostProcess(): BlurPostProcess {
-            return this._blurVPostProcess;
+            Tools.Error("SSAORenderinPipeline.getBlurVPostProcess() is deprecated, no more blur post-process exists");
+            return null;
         }
 
         /**
          * Removes the internal pipeline assets and detatches the pipeline from the scene cameras
          */
         public dispose(disableDepthRender: boolean = false): void {
-            this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
-
-            this._originalColorPostProcess = undefined;
-            this._ssaoPostProcess = undefined;
-            this._blurHPostProcess = undefined;
-            this._blurVPostProcess = undefined;
-            this._ssaoCombinePostProcess = undefined;
+            for (var i = 0; i < this._scene.cameras.length; i++) {
+                var camera = this._scene.cameras[i];
+
+                this._originalColorPostProcess.dispose(camera);
+                this._ssaoPostProcess.dispose(camera);
+                this._blurHPostProcess.dispose(camera);
+                this._blurVPostProcess.dispose(camera);
+                this._ssaoCombinePostProcess.dispose(camera);
+            }
 
             this._randomTexture.dispose();
 
             if (disableDepthRender)
                 this._scene.disableDepthRenderer();
+
+            this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
         }
 
         // Private Methods
+        private _createBlurPostProcess(ratio: number): void {
+            /*
+            var samplerOffsets = [
+                -8.0, -6.0, -4.0, -2.0,
+                0.0,
+                2.0, 4.0, 6.0, 8.0
+            ];
+            */
+            var samples = 16;
+            var samplerOffsets = [];
+
+            for (var i = -8; i < 8; i++) {
+                samplerOffsets.push(i * 2);
+            }
+
+            this._blurHPostProcess = new PostProcess("BlurH", "ssao", ["outSize", "samplerOffsets"], ["depthSampler"], ratio, null, Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define BILATERAL_BLUR_H\n#define SAMPLES 16");
+            this._blurHPostProcess.onApply = (effect: Effect) => {
+                effect.setFloat("outSize", this._ssaoCombinePostProcess.width);
+                effect.setTexture("depthSampler", this._depthTexture);
+
+                if (this._firstUpdate) {
+                    effect.setArray("samplerOffsets", samplerOffsets);
+                }
+            };
+
+            this._blurVPostProcess = new PostProcess("BlurV", "ssao", ["outSize", "samplerOffsets"], ["depthSampler"], ratio, null, Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define SAMPLES 16");
+            this._blurVPostProcess.onApply = (effect: Effect) => {
+                effect.setFloat("outSize", this._ssaoCombinePostProcess.height);
+                effect.setTexture("depthSampler", this._depthTexture);
+
+                if (this._firstUpdate) {
+                    effect.setArray("samplerOffsets", samplerOffsets);
+                    this._firstUpdate = false;
+                }
+            };
+        }
+
         private _createSSAOPostProcess(ratio: number): void {
             var numSamples = 16;
             var sampleSphere = [
@@ -174,19 +229,20 @@
             this._ssaoPostProcess = new PostProcess("ssao", "ssao",
                                                     [
                                                         "sampleSphere", "samplesFactor", "randTextureTiles", "totalStrength", "radius",
-                                                        "area", "fallOff", "base"
+                                                        "area", "fallOff", "base", "range", "viewport"
                                                     ],
                                                     ["randomSampler"],
                                                     ratio, null, Texture.BILINEAR_SAMPLINGMODE,
                                                     this._scene.getEngine(), false,
-                                                    "#define SAMPLES " + numSamples);
+                                                    "#define SAMPLES " + numSamples + "\n#define SSAO");
+
+            var viewport = new Vector2(0, 0);
 
             this._ssaoPostProcess.onApply = (effect: Effect) => {
                 if (this._firstUpdate) {
                     effect.setArray3("sampleSphere", sampleSphere);
                     effect.setFloat("samplesFactor", samplesFactor);
                     effect.setFloat("randTextureTiles", 4.0);
-                    this._firstUpdate = false;
                 }
 
                 effect.setFloat("totalStrength", this.totalStrength);
@@ -213,7 +269,7 @@
         private _createRandomTexture(): void {
             var size = 512;
 
-            this._randomTexture = new DynamicTexture("SSAORandomTexture", size, this._scene, false, Texture.BILINEAR_SAMPLINGMODE);
+            this._randomTexture = new DynamicTexture("SSAORandomTexture", size, this._scene, false, Texture.TRILINEAR_SAMPLINGMODE);
             this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
             this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
 
@@ -235,6 +291,7 @@
                     context.fillRect(x, y, 1, 1);
                 }
             }
+
             this._randomTexture.update(false);
         }
     }

+ 11 - 6
src/Rendering/babylon.boundingBoxRenderer.js

@@ -6,6 +6,7 @@ var BABYLON;
             this.backColor = new BABYLON.Color3(0.1, 0.1, 0.1);
             this.showBackLines = true;
             this.renderList = new BABYLON.SmartArray(32);
+            this._vertexBuffers = {};
             this._scene = scene;
         }
         BoundingBoxRenderer.prototype._prepareRessources = function () {
@@ -13,13 +14,13 @@ var BABYLON;
                 return;
             }
             this._colorShader = new BABYLON.ShaderMaterial("colorShader", this._scene, "color", {
-                attributes: ["position"],
+                attributes: [BABYLON.VertexBuffer.PositionKind],
                 uniforms: ["worldViewProjection", "color"]
             });
             var engine = this._scene.getEngine();
             var boxdata = BABYLON.VertexData.CreateBox(1.0);
-            this._vb = new BABYLON.VertexBuffer(engine, boxdata.positions, BABYLON.VertexBuffer.PositionKind, false);
-            this._ib = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
+            this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = new BABYLON.VertexBuffer(engine, boxdata.positions, BABYLON.VertexBuffer.PositionKind, false);
+            this._indexBuffer = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
         };
         BoundingBoxRenderer.prototype.reset = function () {
             this.renderList.reset();
@@ -45,7 +46,7 @@ var BABYLON;
                     .multiply(BABYLON.Matrix.Translation(median.x, median.y, median.z))
                     .multiply(boundingBox.getWorldMatrix());
                 // VBOs
-                engine.bindBuffers(this._vb.getBuffer(), this._ib, [3], 3 * 4, this._colorShader.getEffect());
+                engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._colorShader.getEffect());
                 if (this.showBackLines) {
                     // Back
                     engine.setDepthFunctionToGreaterOrEqual();
@@ -72,8 +73,12 @@ var BABYLON;
                 return;
             }
             this._colorShader.dispose();
-            this._vb.dispose();
-            this._scene.getEngine()._releaseBuffer(this._ib);
+            var buffer = this._vertexBuffers[BABYLON.VertexBuffer.PositionKind];
+            if (buffer) {
+                buffer.dispose();
+                this._vertexBuffers[BABYLON.VertexBuffer.PositionKind] = null;
+            }
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
         };
         return BoundingBoxRenderer;
     })();

+ 13 - 8
src/Rendering/babylon.boundingBoxRenderer.ts

@@ -7,8 +7,8 @@
 
         private _scene: Scene;
         private _colorShader: ShaderMaterial;
-        private _vb: VertexBuffer;
-        private _ib: WebGLBuffer;
+        private _vertexBuffers: { [key: string]: VertexBuffer } = {};
+        private _indexBuffer: WebGLBuffer;
 
         constructor(scene: Scene) {
             this._scene = scene;
@@ -21,15 +21,15 @@
 
             this._colorShader = new ShaderMaterial("colorShader", this._scene, "color",
                 {
-                    attributes: ["position"],
+                    attributes: [VertexBuffer.PositionKind],
                     uniforms: ["worldViewProjection", "color"]
                 });
 
 
             var engine = this._scene.getEngine();
             var boxdata = VertexData.CreateBox(1.0);
-            this._vb = new VertexBuffer(engine, boxdata.positions, VertexBuffer.PositionKind, false);
-            this._ib = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
+            this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, boxdata.positions, VertexBuffer.PositionKind, false);
+            this._indexBuffer = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
         }
 
         public reset(): void {
@@ -62,7 +62,7 @@
                     .multiply(boundingBox.getWorldMatrix());
 
                 // VBOs
-                engine.bindBuffers(this._vb.getBuffer(), this._ib, [3], 3 * 4, this._colorShader.getEffect());
+                engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._colorShader.getEffect());
 
                 if (this.showBackLines) {
                     // Back
@@ -95,8 +95,13 @@
             }
 
             this._colorShader.dispose();
-            this._vb.dispose();
-            this._scene.getEngine()._releaseBuffer(this._ib);
+
+            var buffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (buffer) {
+                buffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
+            }
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
         }
     }
 } 

+ 14 - 8
src/Rendering/babylon.edgesRenderer.js

@@ -17,7 +17,7 @@ var BABYLON;
             this._linesPositions = new Array();
             this._linesNormals = new Array();
             this._linesIndices = new Array();
-            this._buffers = new Array();
+            this._buffers = {};
             this._checkVerticesInsteadOfIndices = false;
             this._source = source;
             this._checkVerticesInsteadOfIndices = checkVerticesInsteadOfIndices;
@@ -37,8 +37,16 @@ var BABYLON;
             this._lineShader.backFaceCulling = false;
         };
         EdgesRenderer.prototype.dispose = function () {
-            this._vb0.dispose();
-            this._vb1.dispose();
+            var buffer = this._buffers[BABYLON.VertexBuffer.PositionKind];
+            if (buffer) {
+                buffer.dispose();
+                this._buffers[BABYLON.VertexBuffer.PositionKind] = null;
+            }
+            buffer = this._buffers[BABYLON.VertexBuffer.NormalKind];
+            if (buffer) {
+                buffer.dispose();
+                this._buffers[BABYLON.VertexBuffer.NormalKind] = null;
+            }
             this._source.getScene().getEngine()._releaseBuffer(this._ib);
             this._lineShader.dispose();
         };
@@ -208,10 +216,8 @@ var BABYLON;
             }
             // Merge into a single mesh
             var engine = this._source.getScene().getEngine();
-            this._vb0 = new BABYLON.VertexBuffer(engine, this._linesPositions, BABYLON.VertexBuffer.PositionKind, false);
-            this._vb1 = new BABYLON.VertexBuffer(engine, this._linesNormals, BABYLON.VertexBuffer.NormalKind, false, false, 4);
-            this._buffers[BABYLON.VertexBuffer.PositionKind] = this._vb0;
-            this._buffers[BABYLON.VertexBuffer.NormalKind] = this._vb1;
+            this._buffers[BABYLON.VertexBuffer.PositionKind] = new BABYLON.VertexBuffer(engine, this._linesPositions, BABYLON.VertexBuffer.PositionKind, false);
+            this._buffers[BABYLON.VertexBuffer.NormalKind] = new BABYLON.VertexBuffer(engine, this._linesNormals, BABYLON.VertexBuffer.NormalKind, false, false, 4);
             this._ib = engine.createIndexBuffer(this._linesIndices);
             this._indicesCount = this._linesIndices.length;
         };
@@ -223,7 +229,7 @@ var BABYLON;
             var engine = scene.getEngine();
             this._lineShader._preBind();
             // VBOs
-            engine.bindMultiBuffers(this._buffers, this._ib, this._lineShader.getEffect());
+            engine.bindBuffers(this._buffers, this._ib, this._lineShader.getEffect());
             scene.resetCachedMaterial();
             this._lineShader.setColor4("color", this._source.edgesColor);
             if (scene.activeCamera.mode === BABYLON.Camera.ORTHOGRAPHIC_CAMERA) {

+ 0 - 0
src/Rendering/babylon.edgesRenderer.ts


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.