nockawa 8 лет назад
Родитель
Сommit
59f8362aeb
79 измененных файлов с 8474 добавлено и 5385 удалено
  1. 1 1
      Babylon.csproj
  2. 12 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonAnimation.cs
  3. 30 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs
  4. 10 2
      Exporters/3ds Max/BabylonExport.Entities/BabylonPBRMaterial.cs
  5. 1 24
      Exporters/3ds Max/BabylonExport.Entities/BabylonUniversalCamera.cs
  6. BIN
      Exporters/Blender/Blender2Babylon-5.1.zip
  7. 1 1
      Exporters/Blender/src/babylon-js/__init__.py
  8. 11 0
      Exporters/Blender/src/babylon-js/mesh.py
  9. 0 10
      Exporters/Blender/zip_media.xml
  10. 2 2
      Playground/frame.js
  11. 69 0
      Tools/CompressedTextured/ktx-files.bat
  12. 9 0
      Tools/CompressedTextured/make-ktx-batch.bat
  13. 2 0
      Tools/Gulp/config.json
  14. 10 1
      Tools/Gulp/gulpfile.js
  15. 2 1
      Tools/Gulp/package.json
  16. 5 0
      Tools/Gulp/readme.md
  17. 1 0
      assets/meshes/Cat.babylon
  18. 49 0
      canvas2D/src/Engine/babylon.bounding2d.ts
  19. 151 84
      canvas2D/src/Engine/babylon.canvas2d.ts
  20. 2 1
      canvas2D/src/Engine/babylon.ellipse2d.ts
  21. 1 1
      canvas2D/src/Engine/babylon.fontTexture.ts
  22. 2 1
      canvas2D/src/Engine/babylon.group2d.ts
  23. 2 1
      canvas2D/src/Engine/babylon.lines2d.ts
  24. 47 12
      canvas2D/src/Engine/babylon.prim2dBase.ts
  25. 2 1
      canvas2D/src/Engine/babylon.rectangle2d.ts
  26. 8 5
      canvas2D/src/Engine/babylon.smartPropertyPrim.ts
  27. 2 1
      canvas2D/src/Engine/babylon.sprite2d.ts
  28. 301 24
      canvas2D/src/Engine/babylon.text2d.ts
  29. 26 25
      dist/preview release/babylon.core.js
  30. 4361 4214
      dist/preview release/babylon.d.ts
  31. 36 36
      dist/preview release/babylon.js
  32. 982 300
      dist/preview release/babylon.max.js
  33. 35 35
      dist/preview release/babylon.noworker.js
  34. 101 13
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  35. 554 137
      dist/preview release/canvas2D/babylon.canvas2d.js
  36. 10 10
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  37. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  38. 31 0
      dist/preview release/inspector/babylon.inspector.css
  39. 23 0
      dist/preview release/inspector/babylon.inspector.d.ts
  40. 142 0
      dist/preview release/inspector/babylon.inspector.js
  41. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  42. 8 1
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  43. 65 31
      dist/preview release/loaders/babylon.glTFFileLoader.js
  44. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  45. 18 3
      dist/preview release/what's new.md
  46. 6 0
      inspector/sass/main.scss
  47. 51 0
      inspector/sass/tabs/_consoleTab.scss
  48. 8 0
      inspector/src/Inspector.ts
  49. 149 0
      inspector/src/tabs/ConsoleTab.ts
  50. 2 1
      inspector/src/tabs/TabBar.ts
  51. 0 1
      inspector/test/index.js
  52. 7 0
      loaders/src/glTF/README.md
  53. 20 40
      loaders/src/glTF/babylon.glTFFileLoader.ts
  54. 54 0
      loaders/src/glTF/babylon.glTFFileLoaderUtils.ts
  55. 38 13
      src/Animations/babylon.animatable.ts
  56. 13 0
      src/Animations/babylon.animation.ts
  57. 2 2
      src/Cameras/VR/babylon.vrCameraMetrics.ts
  58. 13 0
      src/Cameras/VR/babylon.vrDeviceOrientationCamera.ts
  59. 13 5
      src/Cameras/babylon.camera.ts
  60. 44 32
      src/Culling/babylon.ray.ts
  61. 146 0
      src/Culling/babylon.rayHelper.ts
  62. 2 0
      src/Lights/Shadows/babylon.shadowGenerator.ts
  63. 2 1
      src/Materials/Textures/babylon.hdrCubeTexture.ts
  64. 1 1
      src/Materials/babylon.pbrMaterial.ts
  65. 1 1
      src/Math/babylon.math.ts
  66. 51 15
      src/Mesh/babylon.geometry.ts
  67. 25 23
      src/Mesh/babylon.groundMesh.ts
  68. 315 12
      src/Mesh/babylon.mesh.ts
  69. 159 47
      src/Mesh/babylon.mesh.vertexData.ts
  70. 24 21
      src/Mesh/babylon.meshBuilder.ts
  71. 23 17
      src/Particles/babylon.solidParticleSystem.ts
  72. 17 9
      src/PostProcess/babylon.hdrRenderingPipeline.ts
  73. 2 0
      src/PostProcess/babylon.ssaoRenderingPipeline.ts
  74. 1 1
      src/Shaders/ShadersInclude/fogVertex.fx
  75. 1 1
      src/Shaders/sprites.vertex.fx
  76. 20 37
      src/Tools/babylon.filesInput.ts
  77. 27 13
      src/Tools/babylon.tools.ts
  78. 5 5
      src/Tools/babylon.virtualJoystick.ts
  79. 98 100
      src/babylon.engine.ts

+ 1 - 1
Babylon.csproj

@@ -543,7 +543,7 @@
           <AutoAssignPort>True</AutoAssignPort>
           <DevelopmentServerPort>0</DevelopmentServerPort>
           <DevelopmentServerVPath>/</DevelopmentServerVPath>
-          <IISUrl>http://localhost:51793/</IISUrl>
+          <IISUrl>http://localhost:10683/</IISUrl>
           <NTLMAuthentication>False</NTLMAuthentication>
           <UseCustomServer>False</UseCustomServer>
           <CustomServerUrl>

+ 12 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonAnimation.cs

@@ -15,6 +15,12 @@ namespace BabylonExport.Entities
         public int dataType { get; set; }
 
         [DataMember]
+        public bool enableBlending { get; set; }
+
+        [DataMember]
+        public float blendingSpeed { get; set; }
+
+        [DataMember]
         public int loopBehavior { get; set; }
 
         [DataMember]
@@ -38,5 +44,11 @@ namespace BabylonExport.Entities
             Cycle = 1,
             Constant = 2
         }
+
+        public BabylonAnimation()
+        {
+            this.enableBlending = false;
+            this.blendingSpeed = 0.01f;
+        }
     }
 }

+ 30 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs

@@ -45,6 +45,9 @@ namespace BabylonExport.Entities
         public float inertia { get; set; }
 
         [DataMember]
+        public float interaxialDistance { get; set; }
+
+        [DataMember]
         public bool checkCollisions { get; set; }
 
         [DataMember]
@@ -69,6 +72,24 @@ namespace BabylonExport.Entities
         public BabylonAnimation[] animations { get; set; }
 
         [DataMember]
+        public int mode { get; set; }
+
+        [DataMember]
+        public float? orthoLeft { get; set; }
+
+        [DataMember]
+        public float? orthoRight { get; set; }
+
+        [DataMember]
+        public float? orthoBottom { get; set; }
+
+        [DataMember]
+        public float? orthoTop { get; set; }
+
+        [DataMember]
+        public bool isStereoscopicSideBySide;
+
+        [DataMember]
         public object metadata { get; set; }
 
         [DataMember]
@@ -85,6 +106,15 @@ namespace BabylonExport.Entities
             maxZ = 5000.0f;
             speed = 1.0f;
             inertia = 0.9f;
+            interaxialDistance = 0.0637f;
+
+            mode = 0;
+            orthoLeft = null;
+            orthoRight = null;
+            orthoBottom = null;
+            orthoTop = null;
+
+            type = "FreeCamera";
         }
     }
 }

+ 10 - 2
Exporters/3ds Max/BabylonExport.Entities/BabylonPBRMaterial.cs

@@ -95,6 +95,9 @@ namespace BabylonExport.Entities
         public BabylonTexture lightmapTexture { get; set; }
 
         [DataMember]
+        public BabylonTexture metallicTexture { get; set; }
+
+        [DataMember]
         public bool useLightmapAsShadowmap { get; set; }
 
         [DataMember]
@@ -116,7 +119,10 @@ namespace BabylonExport.Entities
         public float[] emissive { get; set; }
 
         [DataMember]
-        public float roughness { get; set; }
+        public float? roughness { get; set; }
+
+        [DataMember]
+        public float? metallic { get; set; }
 
         [DataMember]
         public bool useRoughnessFromMetallicTextureAlpha { get; set; }
@@ -166,7 +172,9 @@ namespace BabylonExport.Entities
             cameraContrast = 1.0f;
             useEmissiveAsIllumination = false;
 
-            roughness = 1.0f;
+            // Default Null Metallic Workflow
+            metallic = null;
+            roughness = null;
             useRoughnessFromMetallicTextureAlpha = false;
             useRoughnessFromMetallicTextureGreen = false;
 

+ 1 - 24
Exporters/3ds Max/BabylonExport.Entities/BabylonUniversalCamera.cs

@@ -4,32 +4,9 @@ namespace BabylonExport.Entities
     [DataContract]
     public class BabylonUniversalCamera : BabylonCamera
     {
-        [DataMember]
-        public int mode { get; set; }
-
-        [DataMember]
-        public float? orthoLeft { get; set; }
-
-        [DataMember]
-        public float? orthoRight { get; set; }
-
-        [DataMember]
-        public float? orthoBottom { get; set; }
-
-        [DataMember]
-        public float? orthoTop { get; set; }
-
-        [DataMember]
-        public bool isStereoscopicSideBySide;
-
         public BabylonUniversalCamera()
         {
-            this.mode = 0;
-            this.orthoLeft = null;
-            this.orthoRight = null;
-            this.orthoBottom = null;
-            this.orthoTop = null;
-            this.type = "BABYLON.UniversalCamera";
+            this.type = "UniversalCamera";
         }
     }
 }

BIN
Exporters/Blender/Blender2Babylon-5.1.zip


+ 1 - 1
Exporters/Blender/src/babylon-js/__init__.py

@@ -1,7 +1,7 @@
 bl_info = {
     'name': 'Babylon.js',
     'author': 'David Catuhe, Jeff Palmer',
-    'version': (5, 1, 2),
+    'version': (5, 2, 0),
     'blender': (2, 76, 0),
     'location': 'File > Export > Babylon.js (.babylon)',
     'description': 'Export Babylon.js scenes (.babylon)',

+ 11 - 0
Exporters/Blender/src/babylon-js/mesh.py

@@ -46,6 +46,7 @@ class Mesh(FCurveAnimatable):
         self.castShadows = object.data.castShadows
         self.freezeWorldMatrix = object.data.freezeWorldMatrix
         self.layer = getLayer(object) # used only for lights with 'This Layer Only' checked, not exported
+        self.tags = object.data.tags
 
         # hasSkeleton detection & skeletonID determination
         hasSkeleton = False
@@ -537,6 +538,7 @@ class Mesh(FCurveAnimatable):
         write_bool(file_handler, 'isEnabled', self.isEnabled)
         write_bool(file_handler, 'checkCollisions', self.checkCollisions)
         write_bool(file_handler, 'receiveShadows', self.receiveShadows)
+        write_string(file_handler, 'tags', self.tags)
 
         if hasattr(self, 'physicsImpostor'):
             write_int(file_handler, 'physicsImpostor', self.physicsImpostor)
@@ -649,6 +651,7 @@ class Node(FCurveAnimatable):
         self.billboardMode = BILLBOARDMODE_NONE
         self.castShadows = False
         self.receiveShadows = False
+        self.tags = ''
         self.layer = -1 # nodes do not have layers attribute
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     def to_scene_file(self, file_handler):
@@ -668,6 +671,7 @@ class Node(FCurveAnimatable):
         write_bool(file_handler, 'checkCollisions', self.checkCollisions)
         write_int(file_handler, 'billboardMode', self.billboardMode)
         write_bool(file_handler, 'receiveShadows', self.receiveShadows)
+        write_string(file_handler, 'tags', self.tags)
 
         super().to_scene_file(file_handler) # Animations
         file_handler.write('}')
@@ -714,6 +718,11 @@ bpy.types.Mesh.receiveShadows = bpy.props.BoolProperty(
     description='',
     default = False
 )
+bpy.types.Mesh.tags = bpy.props.StringProperty(
+    name='Tags',
+    description='Add meta-data to mesh (space delimited for multiples)',
+    default = ''
+)
 # not currently in use
 bpy.types.Mesh.forceBaking = bpy.props.BoolProperty(
     name='Combine Multi-textures / resize',
@@ -821,6 +830,8 @@ class MeshPanel(bpy.types.Panel):
         
         layout.prop(ob.data, 'autoAnimate')
         
+        layout.prop(ob.data, 'tags')
+        
         box = layout.box()
         box.label(text='Skeleton:')
         box.prop(ob.data, 'ignoreSkeleton')

+ 0 - 10
Exporters/Blender/zip_media.xml

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="zipper">
-	<property name="version" value="5.1"/>
-	
-	<!-- this target assumes it is run from Babylon.js/Exporters/Blender directory.  -->
-    <target name="make-zip">
-    	<zip destfile="Blender2Babylon-${version}.zip" basedir="src" level="9"></zip>
-    </target>
-
-</project>

+ 2 - 2
Playground/frame.js

@@ -1,5 +1,5 @@
 (function () {
-    var snippetUrl = "http://babylonjs-api.azurewebsites.net/api/snippet";
+    var snippetUrl = "//babylonjs-api.azurewebsites.net/api/snippet";
     var currentSnippetToken;
     var engine;
     var fpsLabel = document.getElementById("fpsLabel");
@@ -149,7 +149,7 @@
                 xmlHttp.open("GET", snippetUrl + "/" + hash.replace("#", "/"));
                 xmlHttp.send();
 
-                document.getElementById("link").href = "http://www.babylonjs-playground.com/#" + hash;
+                document.getElementById("link").href = "//www.babylonjs-playground.com/#" + hash;
             } catch (e) {
 
             }

+ 69 - 0
Tools/CompressedTextured/ktx-files.bat

@@ -0,0 +1,69 @@
+echo off
+REM Create a series of compressed textures versions of the file name passed in. Skips files which already
+REM exist, since script can be very long running.
+REM arg 1: file
+REM arg 2: file without extension
+REM arg 3: Y when need alpha, else N
+REM arg 4: Q when best image required, else D for developer quality
+
+REM -i specifies the input file passed as the first arg (full path) dos use 1%
+REM -pot + indicates force power of 2
+REM -m indicates to generate mipmaps
+REM -f is the format, variable type (UBN unsigned byte normalized), colorspace
+REM -q indicates how much time to spend, varies by encoding type
+REM -o specifies output file name, uses arg without extension adds -family.ktx
+REM - - - - - - - - - - - - - - - ASTC  - - - - - - - - - - - - - - - 
+echo working with %1
+REM all ASTC formats have alpha
+IF EXIST %2-astc.ktx GOTO PVRTC
+
+SET quality=astcveryfast
+if %4 == 'Q' SET quality=astcexhaustive
+echo compressing...
+PVRTexToolCLI.exe -i %1 -flip y -pot + -m -f ASTC_8x8,UBN,lRGB -q %quality% -shh -o %2-astc.ktx >junk.txt
+echo Saved texture to %2-astc.ktx
+REM - - - - - - - - - - - - - - - PVRTC - - - - - - - - - - - - - - - 
+:PVRTC
+REM PVRTC must be square on iOS
+IF EXIST %2-pvrtc.ktx GOTO DXT
+
+SET format=PVRTC1_2_RGB
+if %3 == 'Y' SET format=PVRTC1_2
+
+SET quality=pvrtcfastest
+if %4 == 'Q' SET quality=pvrtcbest
+
+PVRTexToolCLI.exe -i %1 -flip y -pot + -square + -m -f %format%,UBN,lRGB -q %quality% -o %2-pvrtc.ktx
+
+REM - - - - - - - - - - - - - - -  DXT  - - - - - - - - - - - - - - -
+:DXT
+IF EXIST %2-dxt.ktx GOTO ETC1
+
+SET format=BC1
+if %3 == 'Y' SET format=BC2
+
+PVRTexToolCLI.exe -i %1 -flip y -pot + -m -f %format%,UBN,lRGB -o %2-dxt.ktx
+
+REM - - - - - - - - - - - - - - - ETC1  - - - - - - - - - - - - - - - 
+REM ETC1 does not have an alpha capable format
+:ETC1
+IF EXIST %2-etc1.ktx GOTO ETC2
+
+SET quality=etcfast
+if %4 == 'Q' SET quality=etcslowperceptual
+
+if %3 == 'N' PVRTexToolCLI.exe -i %1 -flip y -pot + -m -f ETC1,UBN,lRGB -q %quality% -o %2-etc1.ktx
+
+REM - - - - - - - - - - - - - - - ETC2  - - - - - - - - - - - - - - - 
+:ETC2
+IF EXIST %2-etc2.ktx GOTO END
+
+SET format=ETC2_RGB
+if %3 == 'Y' SET format=ETC2_RGBA
+
+SET quality=etcfast
+if %4 == 'Q' SET quality=etcslowperceptual
+
+PVRTexToolCLI.exe -i %1 -flip y -pot + -m -f %format%,UBN,lRGB -q %quality% -o %2-etc2.ktx
+REM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
+:END

+ 9 - 0
Tools/CompressedTextured/make-ktx-batch.bat

@@ -0,0 +1,9 @@
+echo off
+REM create compressed textures for each .jpg & .png in the current directory
+REM arg 1: Q when best image required, else D for developer quality
+
+REM erasing of any previous file
+erase ktx-batch.bat
+
+for %%f in (*.jpg) do echo call ktx-files.bat %%f %%~nf 'N' '%1' >>ktx-batch.bat
+for %%f in (*.png) do echo call ktx-files.bat %%f %%~nf 'Y' '%1' >>ktx-batch.bat

+ 2 - 0
Tools/Gulp/config.json

@@ -34,6 +34,7 @@
       "../../src/Culling/babylon.boundingBox.js",
       "../../src/Culling/babylon.boundingInfo.js",
       "../../src/Culling/babylon.ray.js",
+      "../../src/Culling/babylon.rayHelper.js",
       "../../src/Mesh/babylon.abstractMesh.js",
       "../../src/Lights/babylon.light.js",
       "../../src/Lights/babylon.pointLight.js",
@@ -539,6 +540,7 @@
         "../../inspector/src/tabs/MeshTab.ts",
         "../../inspector/src/tabs/SceneTab.ts",
         "../../inspector/src/tabs/ShaderTab.ts",
+        "../../inspector/src/tabs/ConsoleTab.ts",
         "../../inspector/src/tabs/StatsTab.ts",
         "../../inspector/src/tabs/TabBar.ts",
         "../../inspector/src/tools/AbstractTool.ts",

+ 10 - 1
Tools/Gulp/gulpfile.js

@@ -21,6 +21,8 @@ var path = require('path');
 var sass = require('gulp-sass');
 var webpack = require('webpack-stream');
 
+var zip = require('gulp-zip');
+
 var config = require("./config.json");
 var customConfig = require("./custom.config.json");
 
@@ -380,4 +382,11 @@ gulp.task('webserver', function () {
  * Combine Webserver and Watch as long as vscode does not handle multi tasks.
  */
 gulp.task('run', ['watch', 'webserver'], function () {
-});
+});
+
+
+gulp.task("zip-blender" , function() {
+    return gulp.src('../../Exporters/Blender/src/**')
+    .pipe(zip('Blender2Babylon-5.2.zip'))
+    .pipe(gulp.dest('../../Exporters/Blender'));
+});

+ 2 - 1
Tools/Gulp/package.json

@@ -31,7 +31,8 @@
     "css-loader": "^0.25.0",
     "style-loader": "^0.13.1",
     "exports-loader": "^0.6.3",
-    "imports-loader": "^0.7.0"
+    "imports-loader": "^0.7.0",
+    "gulp-zip": "~3.2.0"
   },
   "scripts": {
     "install": "npm --prefix ../../Playground/ install ../../Playground/ && gulp typescript-compile && gulp typescript-libraries && gulp deployLocalDev"

+ 5 - 0
Tools/Gulp/readme.md

@@ -89,3 +89,8 @@ Be aware that all js files content will be overwritten.
 ### Compile all the typscript and the library
 ```
 gulp typescript-all
+```
+### Zip individual Blender python files for distribute-able
+```
+gulp zip-blender
+```

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
assets/meshes/Cat.babylon


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

@@ -163,6 +163,16 @@
         }
 
         /**
+         * return the min/max extend of the bounding info.
+         * x, y, z, w are left, bottom, right and top
+         */
+        public minMax(): Vector4 {
+            let r = Vector4.Zero();
+            this.minMaxToRef(r);
+            return r;
+        }
+
+        /**
          * Update a vector2 with the max extend of the bounding info
          * @param result must be a valid/allocated vector2 that will contain the result of the operation
          */
@@ -172,6 +182,45 @@
         }
 
         /**
+         * Update a vector4 with the min/max extend of the bounding info
+         * x, y, z, w are left, bottom, right and top
+         * @param result must be a valid/allocated vector4 that will contain the result of the operation
+         */
+        public minMaxToRef(result: Vector4) {
+            result.x = this.center.x - this.extent.x;
+            result.y = this.center.y - this.extent.y;
+            result.z = this.center.x + this.extent.x;
+            result.w = this.center.y + this.extent.y;
+        }
+
+        /**
+         * Return the size of the boundingInfo rect surface
+         */
+        public size(): Size {
+            let r = Size.Zero();
+            this.sizeToRef(r);
+            return r;
+        }
+
+        /**
+         * Stores in the result object the size of the boundingInfo rect surface
+         * @param result
+         */
+        public sizeToRef(result: Size) {
+            result.width  = this.extent.x * 2;
+            result.height = this.extent.y * 2;
+        }
+
+        /**
+         * Inflate the boundingInfo with the given vector
+         * @param offset the extent will be incremented with offset and the radius will be computed again
+         */
+        public inflate(offset: Vector2) {
+            this.extent.addInPlace(offset);
+            this.radius = this.extent.length();
+        }
+
+        /**
          * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D

+ 151 - 84
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -352,7 +352,7 @@
 
         /**
          * If you set your own WorldSpaceNode to display the Canvas2D you have to provide your own implementation of this method which computes the local position in the Canvas based on the given 3D World one.
-         * Beware that you have to take under consideration the origin in your calculations! Good luck!
+         * Beware that you have to take under consideration the origin and unitScaleFactor in your calculations! Good luck!
          */
         public worldSpaceToNodeLocal = (worldPos: Vector3): Vector2 => {
             let node = this._worldSpaceNode;
@@ -362,11 +362,14 @@
 
             let mtx = node.getWorldMatrix().clone();
             mtx.invert();
+            let usf = this.unitScaleFactor;
             let v = Vector3.TransformCoordinates(worldPos, mtx);
             let res = new Vector2(v.x, v.y);
             let size = this.actualSize;
-            res.x += size.width * 0.5;  // res is centered, make it relative to bottom/left
-            res.y += size.height * 0.5;
+            res.x += (size.width/usf) * 0.5;  // res is centered, make it relative to bottom/left
+            res.y += (size.height/usf) * 0.5;
+            res.x *= usf; // multiply by the unitScaleFactor, which defines if the canvas is nth time bigger than the original world plane
+            res.y *= usf;
             return res;
         }
 
@@ -583,6 +586,9 @@
             } else {
                 // The pointer is inside the Canvas, do an intersection test
                 this.intersect(ii);
+
+                // Sort primitives to get them from top to bottom
+                ii.intersectedPrimitives = ii.intersectedPrimitives.sort((a, b) => a.prim.actualZOffset - b.prim.actualZOffset);
             }
 
             {
@@ -604,10 +610,14 @@
 
         // Based on the previousIntersectionList and the actualInstersectionList we can determined which primitives are being hover state or loosing it
         private _updateOverStatus(force: boolean) {
-            if ((!force && (this.scene.getRenderId() === this._hoverStatusRenderId)) || !this._previousIntersectionList || !this._actualIntersectionList) {
+            if ((!force && (this.scene.getRenderId() === this._hoverStatusRenderId)) || !this._actualIntersectionList) {
                 return;
             }
 
+            if (this._previousIntersectionList == null) {
+                this._previousIntersectionList = [];
+            }
+
             // Detect a change of over
             let prevPrim = this._previousOverPrimitive ? this._previousOverPrimitive.prim : null;
             let actualPrim = this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
@@ -616,16 +626,31 @@
                 // Detect if the current pointer is captured, only fire event if they belong to the capture primitive
                 let capturedPrim = this.getCapturedPrimitive(this._primPointerInfo.pointerId);
 
-                // Notify the previous "over" prim that the pointer is no longer over it
-                if ((capturedPrim && capturedPrim === prevPrim) || (!capturedPrim && prevPrim && !prevPrim.isDisposed)) {
-                    this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
-                    this._bubbleNotifyPrimPointerObserver(prevPrim, PrimitivePointerInfo.PointerOut, null);
-                }
+                // See the NOTE section of: https://www.w3.org/TR/pointerevents/#setting-pointer-capture
+                if (capturedPrim) {
+                    if (capturedPrim === prevPrim) {
+                        this._primPointerInfo.updateRelatedTarget(prevPrim, this._previousOverPrimitive.intersectionLocation);
+                        this._bubbleNotifyPrimPointerObserver(prevPrim, PrimitivePointerInfo.PointerOut, null);
+                    } else if (capturedPrim === actualPrim) {
+                        this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
+                        this._bubbleNotifyPrimPointerObserver(actualPrim, PrimitivePointerInfo.PointerOver, null);
+                    }
+                } else {
+                    // Check for Out & Leave
+                    for (let prev of this._previousIntersectionList) {
+                        if (!Tools.first(this._actualIntersectionList, (pii) => pii.prim === prev.prim)) {
+                            this._primPointerInfo.updateRelatedTarget(prev.prim, prev.intersectionLocation);
+                            this._bubbleNotifyPrimPointerObserver(prev.prim, PrimitivePointerInfo.PointerOut, null);
+                        }
+                    }
 
-                // Notify the new "over" prim that the pointer is over it
-                if ((capturedPrim && capturedPrim === actualPrim) || (!capturedPrim && actualPrim)) {
-                    this._primPointerInfo.updateRelatedTarget(actualPrim, this._actualOverPrimitive.intersectionLocation);
-                    this._bubbleNotifyPrimPointerObserver(actualPrim, PrimitivePointerInfo.PointerOver, null);
+                    // Check for Over & Enter
+                    for (let actual of this._actualIntersectionList) {
+                        if (!Tools.first(this._previousIntersectionList, (pii) => pii.prim === actual.prim)) {
+                            this._primPointerInfo.updateRelatedTarget(actual.prim, actual.intersectionLocation);
+                            this._bubbleNotifyPrimPointerObserver(actual.prim, PrimitivePointerInfo.PointerOver, null);
+                        }
+                    }
                 }
             }
 
@@ -657,7 +682,7 @@
             }
 
             let pii = this._primPointerInfo;
-            debug += `[RID:${this.scene.getRenderId()}] [${prim.hierarchyDepth}] event:${PrimitivePointerInfo.getEventTypeName(mask)}, id: ${prim.id} (${Tools.getClassName(prim)}), primPos: ${pii.primitivePointerPos.toString()}, canvasPos: ${pii.canvasPointerPos.toString()}`;
+            debug += `[RID:${this.scene.getRenderId()}] [${prim.hierarchyDepth}] event:${PrimitivePointerInfo.getEventTypeName(mask)}, id: ${prim.id} (${Tools.getClassName(prim)}), primPos: ${pii.primitivePointerPos.toString()}, canvasPos: ${pii.canvasPointerPos.toString()}, relatedTarget: ${pii.relatedTarget.id}`;
             console.log(debug);
         }
 
@@ -665,56 +690,40 @@
             let ppi = this._primPointerInfo;
             let event = eventData ? eventData.event : null;
 
-            // In case of PointerOver/Out we will first notify the parent with PointerEnter/Leave
-            if ((mask & (PrimitivePointerInfo.PointerOver | PrimitivePointerInfo.PointerOut)) !== 0) {
-                this._notifParents(prim, mask);
-            }
-
-            let bubbleCancelled = false;
             let cur = prim;
             while (cur && !cur.isDisposed) {
-                // Only trigger the observers if the primitive is intersected (except for out)
-                if (!bubbleCancelled) {
-                    this._updatePrimPointerPos(cur);
-
-                    // Exec the observers
-                    this._debugExecObserver(cur, mask);
-                    if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof PointerInfoPre) {
-                        eventData.skipOnPointerObservable = true;
-                        return false;
+                this._updatePrimPointerPos(cur);
+
+                // For the first level we have to fire Enter or Leave for corresponding Over or Out
+                if (cur === prim) {
+                    // Fire the proper notification
+                    if (mask === PrimitivePointerInfo.PointerOver) {
+                        this._debugExecObserver(prim, PrimitivePointerInfo.PointerEnter);
+                        prim._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerEnter);
                     }
 
-                    this._triggerActionManager(cur, ppi, mask, event);
-
-                    // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
-                    // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
-                    if (ppi.cancelBubble) {
-                        if ((mask & (PrimitivePointerInfo.PointerOver | PrimitivePointerInfo.PointerOut)) === 0) {
-                            return false;
-                        }
-
-                        // We're dealing with PointerOver/Out, let's keep looping to fire PointerEnter/Leave, but not Over/Out anymore
-                        bubbleCancelled = true;
+                    // Trigger a PointerLeave corresponding to the PointerOut
+                    else if (mask === PrimitivePointerInfo.PointerOut) {
+                        this._debugExecObserver(prim, PrimitivePointerInfo.PointerLeave);
+                        prim._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerLeave);
                     }
                 }
 
-                // If bubble is cancel we didn't update the Primitive Pointer Pos yet, let's do it
-                if (bubbleCancelled) {
-                    this._updatePrimPointerPos(cur);
+                // Exec the observers
+                this._debugExecObserver(cur, mask);
+                if (!cur._pointerEventObservable.notifyObservers(ppi, mask) && eventData instanceof PointerInfoPre) {
+                    eventData.skipOnPointerObservable = true;
+                    return false;
                 }
 
-                // NOTE TO MYSELF, this is commented right now because it doesn't seemed needed but I can't figure out why I put this code in the first place
-                //// Trigger a PointerEnter corresponding to the PointerOver
-                //if (mask === PrimitivePointerInfo.PointerOver) {
-                //    this._debugExecObserver(cur, PrimitivePointerInfo.PointerEnter);
-                //    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerEnter);
-                //}
+                this._triggerActionManager(cur, ppi, mask, event);
 
-                //// Trigger a PointerLeave corresponding to the PointerOut
-                //else if (mask === PrimitivePointerInfo.PointerOut) {
-                //    this._debugExecObserver(cur, PrimitivePointerInfo.PointerLeave);
-                //    cur._pointerEventObservable.notifyObservers(ppi, PrimitivePointerInfo.PointerLeave);
-                //}
+                // Bubble canceled? If we're not executing PointerOver or PointerOut, quit immediately
+                // If it's PointerOver/Out we have to trigger PointerEnter/Leave no matter what
+                if (ppi.cancelBubble) {
+                    return false;
+
+                }
 
                 // Loop to the parent
                 cur = cur.parent;
@@ -816,29 +825,6 @@
             }
         }
 
-        _notifParents(prim: Prim2DBase, mask: number) {
-            let pii = this._primPointerInfo;
-
-            let curPrim: Prim2DBase = this;
-
-            while (curPrim) {
-                this._updatePrimPointerPos(curPrim);
-
-                // Fire the proper notification
-                if (mask === PrimitivePointerInfo.PointerOver) {
-                    this._debugExecObserver(curPrim, PrimitivePointerInfo.PointerEnter);
-                    curPrim._pointerEventObservable.notifyObservers(pii, PrimitivePointerInfo.PointerEnter);
-                }
-
-                // Trigger a PointerLeave corresponding to the PointerOut
-                else if (mask === PrimitivePointerInfo.PointerOut) {
-                    this._debugExecObserver(curPrim, PrimitivePointerInfo.PointerLeave);
-                    curPrim._pointerEventObservable.notifyObservers(pii, PrimitivePointerInfo.PointerLeave);
-                }
-                curPrim = curPrim.parent;
-            }
-        }
-
         /**
          * Don't forget to call the dispose method when you're done with the Canvas instance.
          * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
@@ -1066,7 +1052,10 @@
          * Return 
          */
         public get overPrim(): Prim2DBase {
-            return this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
+            if (this._actualIntersectionList && this._actualIntersectionList.length>0) {
+                return this._actualIntersectionList[0].prim;
+            }
+            return null;
         }
 
         /**
@@ -1077,6 +1066,10 @@
             return this.__engineData;
         }
 
+        public get unitScaleFactor(): number {
+            return this._unitScaleFactor;
+        }
+
         public createCanvasProfileInfoCanvas(): Canvas2D {
             if (this._profilingCanvas) {
                 return this._profilingCanvas;
@@ -1242,7 +1235,11 @@
         private _beforeRenderObserver: Observer<Scene>;
         private _afterRenderObserver: Observer<Scene>;
         private _supprtInstancedArray: boolean;
+        protected _unitScaleFactor: number;
         private _trackedGroups: Array<Group2D>;
+        protected _trackNode: Node;
+        protected _trackNodeOffset :Vector3;
+        protected _trackNodeBillboard : boolean;
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         private _designSize: Size;
         private _designUseHorizAxis: boolean;
@@ -1269,14 +1266,20 @@
         private static _v = Vector3.Zero(); // Must stay zero
         private static _m = Matrix.Identity();
         private static _mI = Matrix.Identity(); // Must stay identity
+        private static tS = Vector3.Zero();
+        private static tT = Vector3.Zero();
+        private static tR = Quaternion.Identity();
 
         private _updateTrackedNodes() {
+            // Get the used camera
             let cam = this.scene.cameraToUseForPointers || this.scene.activeCamera;
 
+            // Compute some matrix stuff
             cam.getViewMatrix().multiplyToRef(cam.getProjectionMatrix(), Canvas2D._m);
             let rh = this.engine.getRenderHeight();
             let v = cam.viewport.toGlobal(this.engine.getRenderWidth(), rh);
 
+            // Compute the screen position of each group that track a given scene node
             for (let group of this._trackedGroups) {
                 if (group.isDisposed) {
                     continue;
@@ -1294,6 +1297,43 @@
                 group.x = Math.round(proj.x/s);
                 group.y = Math.round((rh - proj.y)/s);
             }
+
+            // If it's a WorldSpaceCanvas and it's tracking a node, let's update the WSC transformation data
+            if (this._trackNode) {
+                let rot: Quaternion = null;
+                let scale: Vector3 = null;
+
+                let worldmtx = this._trackNode.getWorldMatrix();
+                let pos = worldmtx.getTranslation().add(this._trackNodeOffset);
+                let wsc = <WorldSpaceCanvas2D><any>this;
+                let wsn = wsc.worldSpaceCanvasNode;
+
+                if (this._trackNodeBillboard) {
+                    let viewMtx = cam.getViewMatrix().clone().invert();
+                    viewMtx.decompose(Canvas2D.tS, Canvas2D.tR, Canvas2D.tT);
+                    rot = Canvas2D.tR.clone();
+                }
+
+                worldmtx.decompose(Canvas2D.tS, Canvas2D.tR, Canvas2D.tT);
+                let mtx = Matrix.Compose(Canvas2D.tS, Canvas2D.tR, Vector3.Zero());
+                pos = worldmtx.getTranslation().add(Vector3.TransformCoordinates(this._trackNodeOffset, mtx));
+
+                if (Canvas2D.tS.lengthSquared() !== 1) {
+                    scale = Canvas2D.tS.clone();
+                }
+
+                if (!this._trackNodeBillboard) {
+                    rot = Canvas2D.tR.clone();
+                }
+
+                if (wsn instanceof AbstractMesh) {
+                    wsn.position = pos;
+                    wsn.rotationQuaternion = rot;
+                    if (scale) {
+                        wsn.scaling = scale;
+                    }
+                }
+            }
         }
 
         /**
@@ -1717,10 +1757,14 @@
          * @param scene the Scene that owns the Canvas
          * @param size the dimension of the Canvas in World Space
          * @param settings a combination of settings, possible ones are
-         *  - children: an array of direct children primitives
-         *  - id: a text identifier, for information purpose only, default is null.
-         *  - worldPosition the position of the Canvas in World Space, default is [0,0,0]
-         *  - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - children: an array of direct children primitives
+         * - id: a text identifier, for information purpose only, default is null.
+         * - unitScaleFactor: if specified the created canvas will be with a width of size.width*unitScaleFactor and a height of size.height.unitScaleFactor. If not specified, the unit of 1 is used. You can use this setting when you're dealing with a 3D world with small coordinates and you need a Canvas having bigger coordinates (typically to display text with better quality).
+         * - worldPosition the position of the Canvas in World Space, default is [0,0,0]
+         * - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - trackNode: if you want the WorldSpaceCanvas to track the position/rotation/scale of a given Scene Node, use this setting to specify the Node to track
+         * - trackNodeOffset: if you use trackNode you may want to specify a 3D Offset to apply to shift the Canvas
+         * - trackNodeBillboard: if true the WorldSpaceCanvas will always face the screen
          * - 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.
          * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
@@ -1741,8 +1785,12 @@
 
             children                 ?: Array<Prim2DBase>,
             id                       ?: string,
+            unitScaleFactor          ?: number,
             worldPosition            ?: Vector3,
             worldRotation            ?: Quaternion,
+            trackNode                ?: Node,
+            trackNodeOffset          ?: Vector3,
+            trackNodeBillboard       ?: boolean,
             sideOrientation          ?: number,
             cachingStrategy          ?: number,
             enableInteraction        ?: boolean,
@@ -1763,7 +1811,11 @@
             Prim2DBase._isCanvasInit = true;
             let s = <any>settings;
             s.isScreenSpace = false;
-            s.size = size.clone();
+            if (settings.unitScaleFactor != null) {
+                s.size = size.multiplyByFloats(settings.unitScaleFactor, settings.unitScaleFactor);
+            } else {
+                s.size = size.clone();
+            }
             settings.cachingStrategy = (settings.cachingStrategy == null) ? Canvas2D.CACHESTRATEGY_CANVAS : settings.cachingStrategy;
 
             if (settings.cachingStrategy !== Canvas2D.CACHESTRATEGY_CANVAS) {
@@ -1773,6 +1825,8 @@
             super(scene, settings);
             Prim2DBase._isCanvasInit = false;
 
+            this._unitScaleFactor = (settings.unitScaleFactor != null) ? settings.unitScaleFactor : 1;
+
             this._renderableData._useMipMap = true;
             this._renderableData._anisotropicLevel = 8;
 
@@ -1780,13 +1834,23 @@
             //    throw new Error("CACHESTRATEGY_DONTCACHE cache Strategy can't be used for WorldSpace Canvas");
             //}
 
+            if (settings.trackNode != null) {
+                this._trackNode = settings.trackNode;
+                this._trackNodeOffset = (settings.trackNodeOffset != null) ? settings.trackNodeOffset : Vector3.Zero();
+                this._trackNodeBillboard = (settings.trackNodeBillboard != null) ? settings.trackNodeBillboard : false;
+            } else {
+                this._trackNode = null;
+                this._trackNodeOffset = null;
+                this._trackNodeBillboard = false;
+            }
+
             let createWorldSpaceNode = !settings || (settings.customWorldSpaceNode == null);
             this._customWorldSpaceNode = !createWorldSpaceNode;
             let id = settings ? settings.id || null : null;
 
             // Set the max size of texture allowed for the adaptive render of the world space canvas cached bitmap
             let capMaxTextSize = this.engine.getCaps().maxRenderTextureSize;
-            let defaultTextSize = (Math.min(capMaxTextSize, 1024));     // Default is 4K if allowed otherwise the max allowed
+            let defaultTextSize = (Math.min(capMaxTextSize, 1024));     // Default is 1K if allowed otherwise the max allowed
             if (settings.maxAdaptiveCanvasSize == null) {
                 this._maxAdaptiveWorldSpaceCanvasSize = defaultTextSize;
             } else {
@@ -1809,6 +1873,9 @@
                 mtl.specularColor = new Color3(0, 0, 0);
                 mtl.disableLighting = true;
                 mtl.useAlphaFromDiffuseTexture = true;
+                if (settings && settings.sideOrientation) {
+                    mtl.backFaceCulling = (settings.sideOrientation === Mesh.DEFAULTSIDE || settings.sideOrientation === Mesh.FRONTSIDE);
+                }
                 plane.position = settings && settings.worldPosition || Vector3.Zero();
                 plane.rotationQuaternion = settings && settings.worldRotation || Quaternion.Identity();
                 plane.material = mtl;

+ 2 - 1
canvas2D/src/Engine/babylon.ellipse2d.ts

@@ -203,8 +203,9 @@
             return ((x * x) / (w * w) + (y * y) / (h * h)) <= 1;
         }
 
-        protected updateLevelBoundingInfo() {
+        protected updateLevelBoundingInfo(): boolean {
             BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo);
+            return true;
         }
 
         /**

+ 1 - 1
canvas2D/src/Engine/babylon.fontTexture.ts

@@ -414,7 +414,7 @@
             this._context.textBaseline = "top";
 
             var res = this.getFontHeight(font);
-            this._lineHeightSuper = res.height+4;
+            this._lineHeightSuper = res.height; //+4;
             this._lineHeight = this._superSample ? (Math.ceil(this._lineHeightSuper / 2)) : this._lineHeightSuper;
             this._offset = res.offset - 1;
             this._xMargin = 1 + Math.ceil(this._lineHeightSuper / 15);    // Right now this empiric formula seems to work...

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

@@ -344,7 +344,7 @@
             return true;
         }
 
-        protected updateLevelBoundingInfo() {
+        protected updateLevelBoundingInfo(): boolean {
             let size: Size;
 
             // If the size is set by the user, the boundingInfo is computed from this value
@@ -357,6 +357,7 @@
             }
 
             BoundingInfo2D.CreateFromSizeToRef(size, this._levelBoundingInfo);
+            return true;
         }
 
         // Method called only on renderable groups to prepare the rendering

+ 2 - 1
canvas2D/src/Engine/babylon.lines2d.ts

@@ -369,11 +369,12 @@
             return res;
         }
 
-        protected updateLevelBoundingInfo() {
+        protected updateLevelBoundingInfo(): boolean {
             if (!this._boundingMin) {
                 this._computeLines2D();
             }
             BoundingInfo2D.CreateFromMinMaxToRef(this._boundingMin.x, this._boundingMax.x, this._boundingMin.y, this._boundingMax.y, this._levelBoundingInfo);
+            return true;
         }
 
         /**

+ 47 - 12
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1517,7 +1517,7 @@
                     this.addChild(child);
 
                     // Good time to patch the hierarchy, it won't go very far if there's no need to
-                    if (this.owner != null) {
+                    if (this.owner != null && this._hierarchyDepth != null) {
                         child._patchHierarchy(this.owner);
                     }
                 }
@@ -1975,7 +1975,6 @@
                 Prim2DBase.boundinbBoxReentrency = false;
 
                 return this._boundingSize;
-
             }
             return this._size;
         }
@@ -2562,7 +2561,9 @@
             return this._localTransform;
         }
 
+        private static _bMinMax = Vector4.Zero();
         private static _bMax = Vector2.Zero();
+        private static _bSize = Size.Zero();
         private static _tpsBB = new BoundingInfo2D();
         /**
          * Get the boundingInfo associated to the primitive and its children.
@@ -2573,10 +2574,15 @@
                 if (this.owner) {
                     this.owner.boundingInfoRecomputeCounter.addCount(1, false);
                 }
-                if (this.isSizedByContent) {
+                let sizedByContent = this.isSizedByContent;
+
+                if (sizedByContent) {
                     this._boundingInfo.clear();
                 } else {
                     this._boundingInfo.copyFrom(this.levelBoundingInfo);
+                    if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
+                        return this._boundingInfo;
+                    }
                 }
                 let bi = this._boundingInfo;
 
@@ -2587,10 +2593,28 @@
                     bi.unionToRef(tps, bi);
                 }
 
-                this._boundingInfo.maxToRef(Prim2DBase._bMax);
+                // If the size is determined by the content we have to update the contentArea
+                //  and compute the size considering the padding (if any)
+                if (sizedByContent) {
+                    bi.maxToRef(Prim2DBase._bMax);
+                    this._contentArea.width = Prim2DBase._bMax.x;
+                    this._contentArea.height = Prim2DBase._bMax.y;
+
+                    if (this._hasPadding) {
+                        let padding = this.padding;
+                        let mm = Prim2DBase._bMinMax;
+                        bi.minMaxToRef(mm);
+                        mm.z += padding.leftPixels + padding.rightPixels;
+                        mm.w += padding.bottomPixels + padding.topPixels;
+                        this._paddingOffset.copyFromFloats(padding.leftPixels, padding.bottomPixels, padding.rightPixels, padding.topPixels);
+                        BoundingInfo2D.CreateFromMinMaxToRef(mm.x, mm.z, mm.y, mm.w, bi);
+                    }
+                }
+
+                this._boundingInfo.sizeToRef(Prim2DBase._bSize);
                 this._boundingSize.copyFromFloats(
-                    (!this._size || this._size.width == null) ? Math.ceil(Prim2DBase._bMax.x) : this._size.width,
-                    (!this._size || this._size.height == null) ? Math.ceil(Prim2DBase._bMax.y) : this._size.height);
+                    (!this._size || this._size.width == null) ? Math.ceil(Prim2DBase._bSize.width) : this._size.width,
+                    (!this._size || this._size.height == null) ? Math.ceil(Prim2DBase._bSize.height) : this._size.height);
 
                 this._clearFlags(SmartPropertyPrim.flagBoundingInfoDirty);
             }
@@ -3536,12 +3560,6 @@
             if (this._hasPadding) {
                 // Two cases from here: the size of the Primitive is Auto, its content can't be shrink, so we resize the primitive itself
                 if (isSizeAuto) {
-                    let content = this.size.clone();
-                    this._getActualSizeFromContentToRef(content, Prim2DBase._icArea);
-                    this.padding.enlarge(Prim2DBase._icArea, this._paddingOffset, Prim2DBase._size);
-                    this._contentArea.copyFrom(content);
-                    this.actualSize = Prim2DBase._size.clone();
-
                     // Changing the padding has resize the prim, which forces us to recompute margin again
                     if (this._hasMargin) {
                         this.margin.computeWithAlignment(this.layoutArea, Prim2DBase._size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
@@ -3587,6 +3605,23 @@
          * Children of this primitive will be positioned relative to the bottom/left corner of this area.
          */
         public get contentArea(): Size {
+            if (!this._size || this._size.width == null || this._size.height == null) {
+
+                if (Prim2DBase.boundinbBoxReentrency) {
+                    return Prim2DBase.nullSize;
+                }
+
+                if (!this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
+                    return this._boundingSize;
+                }
+
+                Prim2DBase.boundinbBoxReentrency = true;
+                let b = this.boundingInfo;
+                Prim2DBase.boundinbBoxReentrency = false;
+
+                return this._contentArea;
+            } else
+
             // Check for positioning update
             if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
                 this._updatePositioning();

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

@@ -288,8 +288,9 @@
             return true;
         }
 
-        protected updateLevelBoundingInfo() {
+        protected updateLevelBoundingInfo(): boolean {
             BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo);
+            return true;
         }
 
         /**

+ 8 - 5
canvas2D/src/Engine/babylon.smartPropertyPrim.ts

@@ -1192,9 +1192,12 @@
          */
         public get levelBoundingInfo(): BoundingInfo2D {
             if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
-                this.updateLevelBoundingInfo();
-                this._boundingInfo.dirtyWorldAABB();
-                this._clearFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
+                if (this.updateLevelBoundingInfo()) {
+                    this._boundingInfo.dirtyWorldAABB();
+                    this._clearFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
+                } else {
+                    this._levelBoundingInfo.clear();
+                }
             }
             return this._levelBoundingInfo;
         }
@@ -1202,8 +1205,8 @@
         /**
          * This method must be overridden by a given Primitive implementation to compute its boundingInfo
          */
-        protected updateLevelBoundingInfo() {
-
+        protected updateLevelBoundingInfo(): boolean {
+            return false;
         }
 
         /**

+ 2 - 1
canvas2D/src/Engine/babylon.sprite2d.ts

@@ -241,8 +241,9 @@
             this._alignToPixel = value;
         }
 
-        protected updateLevelBoundingInfo() {
+        protected updateLevelBoundingInfo(): boolean {
             BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo);
+            return true;
         }
 
         /**

+ 301 - 24
canvas2D/src/Engine/babylon.text2d.ts

@@ -148,6 +148,37 @@
         public static fontSuperSampleProperty: Prim2DPropInfo;
         public static fontSignedDistanceFieldProperty: Prim2DPropInfo;
 
+        /**
+         * Alignment is made relative to the left edge of the Content Area. Valid for horizontal alignment only.
+         */
+        public static get AlignLeft(): number { return Text2D._AlignLeft; }
+
+        /**
+         * Alignment is made relative to the top edge of the Content Area. Valid for vertical alignment only.
+         */
+        public static get AlignTop(): number { return Text2D._AlignTop; }
+
+        /**
+         * Alignment is made relative to the right edge of the Content Area. Valid for horizontal alignment only.
+         */
+        public static get AlignRight(): number { return Text2D._AlignRight; }
+
+        /**
+         * Alignment is made relative to the bottom edge of the Content Area. Valid for vertical alignment only.
+         */
+        public static get AlignBottom(): number { return Text2D._AlignBottom; }
+
+        /**
+         * Alignment is made to center the text from equal distance to the opposite edges of the Content Area
+         */
+        public static get AlignCenter(): number { return Text2D._AlignCenter; }
+
+        private static _AlignLeft = 1;
+        private static _AlignTop = 1;   // Same as left
+        private static _AlignRight = 2;
+        private static _AlignBottom = 2;   // Same as right
+        private static _AlignCenter = 3;
+
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Text2D.fontProperty = pi, false, true)
         /**
          * Get/set the font name to use, using HTML CSS notation.
@@ -296,7 +327,23 @@
         }
 
         protected updateLevelBoundingInfo() {
+            if (!this.owner || !this._text) {
+                return false;
+            }
             BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo);
+            return true;
+        }
+
+        /**
+         * You can get/set the text alignment through this property
+         */
+        public get textAlignment(): string {
+            return this._textAlignment;
+        }
+
+        public set textAlignment(value: string) {
+            this._textAlignment = value;
+            this._setTextAlignmentfromString(value);
         }
 
         /**
@@ -338,6 +385,10 @@
          * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         * - textAlignmentH: align text horizontally (Text2D.AlignLeft, Text2D.AlignCenter, Text2D.AlignRight)
+         * - textAlignmentV: align text vertically (Text2D.AlignTop, Text2D.AlignCenter, Text2D.AlignBottom)
+         * - textAlignment: a string defining the text alignment, text can be: [<h:|horizontal:><left|right|center>], [<v:|vertical:><top|bottom|center>]
+         * - wordWrap: if true the text will wrap inside content area
          */
         constructor(text: string, settings?: {
 
@@ -381,6 +432,10 @@
             paddingRight            ?: number | string,
             paddingBottom           ?: number | string,
             padding                 ?: string,
+            textAlignmentH          ?: number,
+            textAlignmentV          ?: number,
+            textAlignment           ?: string,
+            wordWrap                ?: boolean
         }) {
 
             if (!settings) {
@@ -404,7 +459,11 @@
             this._textSize           = null;
             this.text                = text;
             this.size                = (settings.size==null) ? null : settings.size;
-
+            this.textAlignmentH      = (settings.textAlignmentH==null) ? Text2D.AlignLeft : settings.textAlignmentH;
+            this.textAlignmentV      = (settings.textAlignmentV==null) ? Text2D.AlignTop : settings.textAlignmentV;
+            this.textAlignment       = (settings.textAlignment==null) ? "" : settings.textAlignment;
+            this._wordWrap           = (settings.wordWrap==null) ? false : settings.wordWrap;
+            
             this._updateRenderMode();
         }
 
@@ -490,56 +549,206 @@
             }
 
             if (part.id === Text2D.TEXT2D_MAINPARTID) {
+
                 let d = <Text2DInstanceData>part;
                 let texture = this.fontTexture;
                 let superSampleFactor = texture.isSuperSampled ? 0.5 : 1;
                 let ts = texture.getSize();
                 let offset = Vector2.Zero();
                 let lh = this.fontTexture.lineHeight;
-                offset.y = ((this.textSize.height/lh)-1) * lh;  // Origin is bottom, not top, so the offset is starting with a y that is the top location of the text
-                let charxpos = 0;
+
                 d.dataElementCount = this._charCount;
                 d.curElement = 0;
-                for (let char of this.text) {
+
+                let lineLengths = [];
+                let charWidths = [];
+                let charsPerLine = [];
+                let numCharsCurrenLine = 0;
+                let contentAreaWidth = this.contentArea.width;
+                let contentAreaHeight = this.contentArea.height;
+                let numCharsCurrentWord = 0;
+                let widthCurrentWord = 0;
+                let numWordsPerLine = 0;
+                let text = this.text;
+                let tabWidth = this._tabulationSize * texture.spaceWidth;
+
+                for (let i = 0; i < text.length; i++) {
+                    let char = text[i];
+                    numCharsCurrenLine++;
+                   
+                    charWidths[i] = 0;
 
                     // Line feed
-                    if (char === "\n") {
+                    if (this._isWhiteSpaceCharVert(char)) {
+                        lineLengths.push(offset.x);
+                        charsPerLine.push(numCharsCurrenLine - 1);
+                        numCharsCurrenLine = 1;
                         offset.x = 0;
-                        offset.y -= texture.lineHeight;
+
+                        if (widthCurrentWord > 0) {
+                            numWordsPerLine++;
+                        }
+
+                        numWordsPerLine = 0;
+                        numCharsCurrentWord = 0;
+                        widthCurrentWord = 0;
+                        
+                        continue;
                     }
 
-                    // Tabulation ?
+                    let ci = texture.getChar(char);
+                    let charWidth = 0;
+
                     if (char === "\t") {
-                        let nextPos = charxpos + this._tabulationSize;
-                        nextPos = nextPos - (nextPos % this._tabulationSize);
+                        charWidth = tabWidth;
+                    }else{
+                        charWidth = ci.charWidth;
+                    }
 
-                        offset.x += (nextPos - charxpos) * texture.spaceWidth;
-                        charxpos = nextPos;
-                        continue;
+                    offset.x += charWidth;
+                    charWidths[i] = charWidth;
+
+                    if (this._isWhiteSpaceCharHoriz(char)) {
+                        if (widthCurrentWord > 0) {
+                            numWordsPerLine++;
+                        }
+                        numCharsCurrentWord = 0;
+                        widthCurrentWord = 0;
+                    }else {
+                        widthCurrentWord += ci.charWidth;
+                        numCharsCurrentWord++;
                     }
 
-                    if (char < " ") {
-                        continue;
+                    if (this._wordWrap && numWordsPerLine > 0 && offset.x > contentAreaWidth) {
+                        lineLengths.push(offset.x - widthCurrentWord);
+                        numCharsCurrenLine -= numCharsCurrentWord;
+                        let j = i - numCharsCurrentWord;
+                        //skip white space at the end of this line
+                        while (this._isWhiteSpaceCharHoriz(text[j])) {
+                            lineLengths[lineLengths.length - 1] -= charWidths[j];
+                            j--;
+                        }
+
+                        charsPerLine.push(numCharsCurrenLine);
+
+                        if(this._isWhiteSpaceCharHoriz(text[i])){
+                            
+                            //skip white space at the beginning of next line
+                            let numSpaces = 0;
+                            while (this._isWhiteSpaceCharHoriz(text[i+numSpaces])) {
+                                numSpaces++;
+                                charWidths[i+numSpaces] = 0;
+                            }
+                           
+                            i += numSpaces-1;
+                            
+                            offset.x = 0;
+                            numCharsCurrenLine = numSpaces-1;
+                        }else{
+                            numCharsCurrenLine = numCharsCurrentWord;
+                            offset.x = widthCurrentWord;
+                        }
+                        
+                        numWordsPerLine = 0;
                     }
+                }
+                lineLengths.push(offset.x);
+                charsPerLine.push(numCharsCurrenLine);
+
+                //skip white space at the end
+                let i = text.length - 1;
+                while (this._isWhiteSpaceCharHoriz(text[i])) {
+                    lineLengths[lineLengths.length - 1] -= charWidths[i];
+                    i--;
+                }
 
-                    this.updateInstanceDataPart(d, offset);
+                let charNum = 0;
+                let maxLineLen = 0;
+                let alignH = this.textAlignmentH;
+                let alignV = this.textAlignmentV;
 
-                    let ci = texture.getChar(char);
-                    offset.x += ci.charWidth;
+                offset.x = 0;
 
-                    d.topLeftUV = ci.topLeftUV;
-                    let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
-                    d.sizeUV = suv;
-                    d.textureSize = new Vector2(ts.width, ts.height);
-                    d.color = this.defaultFontColor;
-                    d.superSampleFactor = superSampleFactor;
+                if (alignH == Text2D.AlignRight || alignH == Text2D.AlignCenter) {
+                    for (let i = 0; i < lineLengths.length; i++) {
+                        if (lineLengths[i] > maxLineLen) {
+                            maxLineLen = lineLengths[i];
+                        }
+                    }
+                }
+
+                let textHeight = lineLengths.length * lh;
+                let offsetX = this.padding.leftPixels;
+                
+                if (alignH == Text2D.AlignRight) {
+                    offsetX += contentAreaWidth - maxLineLen;
+                } else if (alignH == Text2D.AlignCenter) {
+                    offsetX += (contentAreaWidth - maxLineLen) * .5;
+                }
+
+                offset.x += offsetX;
+
+                offset.y += contentAreaHeight + textHeight - lh;
+                offset.y += this.padding.bottomPixels;
 
-                    ++d.curElement;
+                if (alignV == Text2D.AlignBottom) {
+                    offset.y -= contentAreaHeight;
+                }else if (alignV == Text2D.AlignCenter) {
+                    offset.y -= (contentAreaHeight - textHeight) * .5 + lineLengths.length * lh;
+                }else {
+                    offset.y -= lineLengths.length * lh;
                 }
+
+                for (let i = 0; i < lineLengths.length; i++) {
+                    let numChars = charsPerLine[i];
+                    let lineLength = lineLengths[i];
+
+                    if (alignH == Text2D.AlignRight) {
+                        offset.x += maxLineLen - lineLength;
+                    }else if (alignH == Text2D.AlignCenter) {
+                        offset.x += (maxLineLen - lineLength) * .5;
+                    }
+
+                    for (let j = 0; j < numChars; j++) {
+                        let char = text[charNum];
+                        let charWidth = charWidths[charNum];
+
+                        this.updateInstanceDataPart(d, offset);
+                        offset.x += charWidth;
+
+                        if (!this._isWhiteSpaceCharHoriz(char)) {
+                            let ci = texture.getChar(char);
+                            d.topLeftUV = ci.topLeftUV;
+                            let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
+                            d.sizeUV = suv;
+                            d.textureSize = new BABYLON.Vector2(ts.width, ts.height);
+                            d.color = this.defaultFontColor;
+                            d.superSampleFactor = superSampleFactor;
+                            ++d.curElement;
+                        }
+                        charNum++;
+                    }
+
+                    offset.x = offsetX;
+                    offset.y -= texture.lineHeight;
+                }
+
             }
             return true;
         }
 
+        private _isWhiteSpaceCharHoriz(char): boolean {
+            if(char === " " || char === "\t"){
+                return true;
+            }
+        }
+
+        private _isWhiteSpaceCharVert(char): boolean {
+            if(char === "\n" || char === "\r"){
+                return true;
+            }
+        }
+
         private _updateCharCount() {
             let count = 0;
             for (let char of this._text) {
@@ -551,6 +760,68 @@
             this._charCount = count;
         }
 
+        private _setTextAlignmentfromString(value: string) {
+            let m = value.trim().split(",");
+
+            for (let v of m) {
+                v = v.toLocaleLowerCase().trim();
+
+                // Horizontal
+                let i = v.indexOf("h:");
+                if (i === -1) {
+                    i = v.indexOf("horizontal:");
+                }
+
+                if (i !== -1) {
+                    v = v.substr(v.indexOf(":") + 1);
+                    this._setTextAlignmentHorizontal(v);
+                    continue;
+                }
+
+                // Vertical
+                i = v.indexOf("v:");
+                if (i === -1) {
+                    i = v.indexOf("vertical:");
+                }
+
+                if (i !== -1) {
+                    v = v.substr(v.indexOf(":") + 1);
+                    this._setTextAlignmentVertical(v);
+                    continue;
+                }
+            }
+        }
+
+        private _setTextAlignmentHorizontal(text: string) {
+            let v = text.trim().toLocaleLowerCase();
+            switch (v) {
+                case "left":
+                    this.textAlignmentH = Text2D.AlignLeft;
+                    return;
+                case "right":
+                    this.textAlignmentH = Text2D.AlignRight;
+                    return;
+                case "center":
+                    this.textAlignmentH = Text2D.AlignCenter;
+                    return;
+            }
+        }
+
+        private _setTextAlignmentVertical(text: string) {
+            let v = text.trim().toLocaleLowerCase();
+            switch (v) {
+                case "top":
+                    this.textAlignmentV = Text2D.AlignTop;
+                    return;
+                case "bottom":
+                    this.textAlignmentV = Text2D.AlignBottom;
+                    return;
+                case "center":
+                    this.textAlignmentV = Text2D.AlignCenter;
+                    return;
+            }
+        }
+
         protected _useTextureAlpha(): boolean {
             return this._fontSDF;
         }
@@ -568,5 +839,11 @@
         private _defaultFontColor: Color4;
         private _text: string;
         private _textSize: Size;
+        private _wordWrap: boolean;
+        private _textAlignment: string;
+
+        public textAlignmentH: number;
+        public textAlignmentV: number;
+        
     }
 }

Разница между файлами не показана из-за своего большого размера
+ 26 - 25
dist/preview release/babylon.core.js


Разница между файлами не показана из-за своего большого размера
+ 4361 - 4214
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 36 - 36
dist/preview release/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 982 - 300
dist/preview release/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 35 - 35
dist/preview release/babylon.noworker.js


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

@@ -678,11 +678,36 @@ declare module BABYLON {
          */
         max(): Vector2;
         /**
+         * return the min/max extend of the bounding info.
+         * x, y, z, w are left, bottom, right and top
+         */
+        minMax(): Vector4;
+        /**
          * Update a vector2 with the max extend of the bounding info
          * @param result must be a valid/allocated vector2 that will contain the result of the operation
          */
         maxToRef(result: Vector2): void;
         /**
+         * Update a vector4 with the min/max extend of the bounding info
+         * x, y, z, w are left, bottom, right and top
+         * @param result must be a valid/allocated vector4 that will contain the result of the operation
+         */
+        minMaxToRef(result: Vector4): void;
+        /**
+         * Return the size of the boundingInfo rect surface
+         */
+        size(): Size;
+        /**
+         * Stores in the result object the size of the boundingInfo rect surface
+         * @param result
+         */
+        sizeToRef(result: Size): void;
+        /**
+         * Inflate the boundingInfo with the given vector
+         * @param offset the extent will be incremented with offset and the radius will be computed again
+         */
+        inflate(offset: Vector2): void;
+        /**
          * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
          * @param matrix the transformation matrix to apply
          * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
@@ -1127,7 +1152,7 @@ declare module BABYLON {
         /**
          * This method must be overridden by a given Primitive implementation to compute its boundingInfo
          */
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
         /**
          * Property method called when the Primitive becomes dirty
          */
@@ -2008,7 +2033,9 @@ declare module BABYLON {
          * Get the local transformation of the primitive
          */
         readonly localTransform: Matrix;
+        private static _bMinMax;
         private static _bMax;
+        private static _bSize;
         private static _tpsBB;
         /**
          * Get the boundingInfo associated to the primitive and its children.
@@ -2615,7 +2642,7 @@ declare module BABYLON {
          */
         trackedNode: Node;
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
         protected _prepareGroupRender(context: PrepareRender2DContext): void;
         protected _groupRender(): void;
         _setCacheGroupDirty(): void;
@@ -2705,7 +2732,7 @@ declare module BABYLON {
         private static _i1;
         private static _i2;
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
         /**
          * Create an Rectangle 2D Shape primitive. May be a sharp rectangle (with sharp corners), or a rounded one.
          * @param settings a combination of settings, possible ones are
@@ -2823,7 +2850,7 @@ declare module BABYLON {
         actualSize: Size;
         subdivisions: number;
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
         /**
          * Create an Ellipse 2D Shape primitive
          * @param settings a combination of settings, possible ones are
@@ -2944,7 +2971,7 @@ declare module BABYLON {
          * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
          */
         alignToPixel: boolean;
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
         /**
          * Get the animatable array (see http://doc.babylonjs.com/tutorials/Animations)
          */
@@ -3254,6 +3281,31 @@ declare module BABYLON {
         static sizeProperty: Prim2DPropInfo;
         static fontSuperSampleProperty: Prim2DPropInfo;
         static fontSignedDistanceFieldProperty: Prim2DPropInfo;
+        /**
+         * Alignment is made relative to the left edge of the Content Area. Valid for horizontal alignment only.
+         */
+        static readonly AlignLeft: number;
+        /**
+         * Alignment is made relative to the top edge of the Content Area. Valid for vertical alignment only.
+         */
+        static readonly AlignTop: number;
+        /**
+         * Alignment is made relative to the right edge of the Content Area. Valid for horizontal alignment only.
+         */
+        static readonly AlignRight: number;
+        /**
+         * Alignment is made relative to the bottom edge of the Content Area. Valid for vertical alignment only.
+         */
+        static readonly AlignBottom: number;
+        /**
+         * Alignment is made to center the text from equal distance to the opposite edges of the Content Area
+         */
+        static readonly AlignCenter: number;
+        private static _AlignLeft;
+        private static _AlignTop;
+        private static _AlignRight;
+        private static _AlignBottom;
+        private static _AlignCenter;
         fontName: string;
         defaultFontColor: Color4;
         text: string;
@@ -3274,7 +3326,11 @@ declare module BABYLON {
          * Dispose the primitive, remove it from its parent
          */
         dispose(): boolean;
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
+        /**
+         * You can get/set the text alignment through this property
+         */
+        textAlignment: string;
         /**
          * Create a Text primitive
          * @param text the text to display
@@ -3312,6 +3368,10 @@ declare module BABYLON {
          * - paddingRight: right padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - paddingBottom: bottom padding, can be a number (will be pixels) or a string (see PrimitiveThickness.fromString)
          * - padding: top, left, right and bottom padding formatted as a single string (see PrimitiveThickness.fromString)
+         * - textAlignmentH: align text horizontally (Text2D.AlignLeft, Text2D.AlignCenter, Text2D.AlignRight)
+         * - textAlignmentV: align text vertically (Text2D.AlignTop, Text2D.AlignCenter, Text2D.AlignBottom)
+         * - textAlignment: a string defining the text alignment, text can be: [<h:|horizontal:><left|right|center>], [<v:|vertical:><top|bottom|center>]
+         * - wordWrap: if true the text will wrap inside content area
          */
         constructor(text: string, settings?: {
             parent?: Prim2DBase;
@@ -3352,6 +3412,10 @@ declare module BABYLON {
             paddingRight?: number | string;
             paddingBottom?: number | string;
             padding?: string;
+            textAlignmentH?: number;
+            textAlignmentV?: number;
+            textAlignment?: string;
+            wordWrap?: boolean;
         });
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
         protected createModelRenderCache(modelKey: string): ModelRenderCache;
@@ -3361,7 +3425,12 @@ declare module BABYLON {
         protected afterRefreshForLayoutConstruction(part: InstanceDataBase, obj: any): void;
         protected getUsedShaderCategories(dataPart: InstanceDataBase): string[];
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean;
+        private _isWhiteSpaceCharHoriz(char);
+        private _isWhiteSpaceCharVert(char);
         private _updateCharCount();
+        private _setTextAlignmentfromString(value);
+        private _setTextAlignmentHorizontal(text);
+        private _setTextAlignmentVertical(text);
         protected _useTextureAlpha(): boolean;
         protected _shouldUseAlphaFromTexture(): boolean;
         private _fontTexture;
@@ -3373,6 +3442,10 @@ declare module BABYLON {
         private _defaultFontColor;
         private _text;
         private _textSize;
+        private _wordWrap;
+        private _textAlignment;
+        textAlignmentH: number;
+        textAlignmentV: number;
     }
 }
 
@@ -3447,7 +3520,7 @@ declare module BABYLON {
         protected readonly boundingMin: Vector2;
         protected readonly boundingMax: Vector2;
         protected getUsedShaderCategories(dataPart: InstanceDataBase): string[];
-        protected updateLevelBoundingInfo(): void;
+        protected updateLevelBoundingInfo(): boolean;
         /**
          * Create an 2D Lines Shape primitive. The defined lines may be opened or closed (see below)
          * @param points an array that describe the points to use to draw the line, must contain at least two entries.
@@ -3653,7 +3726,7 @@ declare module BABYLON {
         private _setupInteraction(enable);
         /**
          * If you set your own WorldSpaceNode to display the Canvas2D you have to provide your own implementation of this method which computes the local position in the Canvas based on the given 3D World one.
-         * Beware that you have to take under consideration the origin in your calculations! Good luck!
+         * Beware that you have to take under consideration the origin and unitScaleFactor in your calculations! Good luck!
          */
         worldSpaceToNodeLocal: (worldPos: Vector3) => Vector2;
         /**
@@ -3685,7 +3758,6 @@ declare module BABYLON {
         private _debugExecObserver(prim, mask);
         private _bubbleNotifyPrimPointerObserver(prim, mask, eventData);
         private _triggerActionManager(prim, ppi, mask, eventData);
-        _notifParents(prim: Prim2DBase, mask: number): void;
         /**
          * Don't forget to call the dispose method when you're done with the Canvas instance.
          * But don't worry, if you dispose its scene, the canvas will be automatically disposed too.
@@ -3769,6 +3841,7 @@ declare module BABYLON {
          * @returns {}
          */
         readonly _engineData: Canvas2DEngineBoundData;
+        readonly unitScaleFactor: number;
         createCanvasProfileInfoCanvas(): Canvas2D;
         /**
          * Instanced Array will be create if there's at least this number of parts/prim that can fit into it
@@ -3820,7 +3893,11 @@ declare module BABYLON {
         private _beforeRenderObserver;
         private _afterRenderObserver;
         private _supprtInstancedArray;
+        protected _unitScaleFactor: number;
         private _trackedGroups;
+        protected _trackNode: Node;
+        protected _trackNodeOffset: Vector3;
+        protected _trackNodeBillboard: boolean;
         protected _maxAdaptiveWorldSpaceCanvasSize: number;
         private _designSize;
         private _designUseHorizAxis;
@@ -3842,6 +3919,9 @@ declare module BABYLON {
         private static _v;
         private static _m;
         private static _mI;
+        private static tS;
+        private static tT;
+        private static tR;
         private _updateTrackedNodes();
         /**
          * Call this method change you want to have layout related data computed and up to date (layout area, primitive area, local/global transformation matrices)
@@ -3919,10 +3999,14 @@ declare module BABYLON {
          * @param scene the Scene that owns the Canvas
          * @param size the dimension of the Canvas in World Space
          * @param settings a combination of settings, possible ones are
-         *  - children: an array of direct children primitives
-         *  - id: a text identifier, for information purpose only, default is null.
-         *  - worldPosition the position of the Canvas in World Space, default is [0,0,0]
-         *  - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - children: an array of direct children primitives
+         * - id: a text identifier, for information purpose only, default is null.
+         * - unitScaleFactor: if specified the created canvas will be with a width of size.width*unitScaleFactor and a height of size.height.unitScaleFactor. If not specified, the unit of 1 is used. You can use this setting when you're dealing with a 3D world with small coordinates and you need a Canvas having bigger coordinates (typically to display text with better quality).
+         * - worldPosition the position of the Canvas in World Space, default is [0,0,0]
+         * - worldRotation the rotation of the Canvas in World Space, default is Quaternion.Identity()
+         * - trackNode: if you want the WorldSpaceCanvas to track the position/rotation/scale of a given Scene Node, use this setting to specify the Node to track
+         * - trackNodeOffset: if you use trackNode you may want to specify a 3D Offset to apply to shift the Canvas
+         * - trackNodeBillboard: if true the WorldSpaceCanvas will always face the screen
          * - 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.
          * - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property. Default is false (the opposite of ScreenSpace).
@@ -3942,8 +4026,12 @@ declare module BABYLON {
         constructor(scene: Scene, size: Size, settings?: {
             children?: Array<Prim2DBase>;
             id?: string;
+            unitScaleFactor?: number;
             worldPosition?: Vector3;
             worldRotation?: Quaternion;
+            trackNode?: Node;
+            trackNodeOffset?: Vector3;
+            trackNodeBillboard?: boolean;
             sideOrientation?: number;
             cachingStrategy?: number;
             enableInteraction?: boolean;

Разница между файлами не показана из-за своего большого размера
+ 554 - 137
dist/preview release/canvas2D/babylon.canvas2d.js


Разница между файлами не показана из-за своего большого размера
+ 10 - 10
dist/preview release/canvas2D/babylon.canvas2d.min.js


Разница между файлами не показана из-за своего большого размера
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


+ 31 - 0
dist/preview release/inspector/babylon.inspector.css

@@ -28,6 +28,8 @@
     display: flex;
     flex-direction: column;
     flex-shrink: 0; }
+    .insp-wrapper .insp-right-panel.popupmode {
+      width: 100% !important; }
     .insp-wrapper .insp-right-panel .top-panel {
       width: 100%;
       height: 100%;
@@ -167,6 +169,35 @@
       text-transform: uppercase;
       line-height: 25px;
       margin-bottom: 10px; }
+  .insp-wrapper .tab-panel .console-panel {
+    min-height: 100px;
+    user-select: text;
+    box-sizing: border-box;
+    padding: 0 15px; }
+    .insp-wrapper .tab-panel .console-panel .console-panel-title {
+      height: 25px;
+      border-bottom: 1px solid #383838;
+      text-transform: uppercase;
+      line-height: 25px;
+      margin-bottom: 10px; }
+    .insp-wrapper .tab-panel .console-panel .console-panel-content {
+      overflow-y: auto;
+      overflow-x: hidden;
+      height: calc(100% - 30px); }
+    .insp-wrapper .tab-panel .console-panel .defaut-line, .insp-wrapper .tab-panel .console-panel .log, .insp-wrapper .tab-panel .console-panel .warn, .insp-wrapper .tab-panel .console-panel .error, .insp-wrapper .tab-panel .console-panel .object {
+      word-wrap: break-word;
+      padding: 3px 0 3px 5px; }
+    .insp-wrapper .tab-panel .console-panel .caller {
+      padding: 3px 0 3px 0;
+      color: #349ccd; }
+    .insp-wrapper .tab-panel .console-panel .log {
+      color: white; }
+    .insp-wrapper .tab-panel .console-panel .warn {
+      color: orange; }
+    .insp-wrapper .tab-panel .console-panel .error {
+      color: orangered; }
+    .insp-wrapper .tab-panel .console-panel .object {
+      color: #5db0d7; }
   .insp-wrapper .tab-panel.stats-panel {
     overflow-y: auto; }
   .insp-wrapper .tab-panel .stats-fps {

+ 23 - 0
dist/preview release/inspector/babylon.inspector.d.ts

@@ -707,6 +707,29 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    /**
+     * The console tab will have two features :
+     * - hook all console.log call and display them in this panel (and in the browser console as well)
+     * - display all Babylon logs (called with Tools.Log...)
+     */
+    class ConsoleTab extends Tab {
+        private _inspector;
+        private _consolePanelContent;
+        private _bjsPanelContent;
+        private _oldConsoleLog;
+        private _oldConsoleWarn;
+        private _oldConsoleError;
+        constructor(tabbar: TabBar, insp: Inspector);
+        /** Overrides super.dispose */
+        dispose(): void;
+        private _message(type, message, caller);
+        private _addConsoleLog(...params);
+        private _addConsoleWarn(...params);
+        private _addConsoleError(...params);
+    }
+}
+
+declare module INSPECTOR {
     class StatsTab extends Tab {
         private _inspector;
         /**

+ 142 - 0
dist/preview release/inspector/babylon.inspector.js

@@ -108,6 +108,7 @@ var INSPECTOR;
                 this._buildInspector(inspector);
                 // Send resize event to the window
                 INSPECTOR.Helpers.SEND_EVENT('resize');
+                this._tabbar.updateWidth();
             }
             // Refresh the inspector if the browser is not edge
             if (!INSPECTOR.Helpers.IsBrowserEdge()) {
@@ -219,6 +220,7 @@ var INSPECTOR;
          * Set 'firstTime' to true if there is no inspector created beforehands
          */
         Inspector.prototype.openPopup = function (firstTime) {
+            var _this = this;
             if (INSPECTOR.Helpers.IsBrowserEdge()) {
                 console.warn('Inspector - Popup mode is disabled in Edge, as the popup DOM cannot be updated from the main window for security reasons');
             }
@@ -251,10 +253,16 @@ var INSPECTOR;
                 this._c2diwrapper = INSPECTOR.Helpers.CreateDiv('insp-wrapper', popup.document.body);
                 // add inspector     
                 var inspector = INSPECTOR.Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                inspector.classList.add('popupmode');
                 // and build it in the popup  
                 this._buildInspector(inspector);
                 // Rebuild it
                 this.refresh();
+                popup.addEventListener('resize', function () {
+                    if (_this._tabbar) {
+                        _this._tabbar.updateWidth();
+                    }
+                });
             }
         };
         return Inspector;
@@ -2665,6 +2673,139 @@ var __extends = (this && this.__extends) || function (d, b) {
 };
 var INSPECTOR;
 (function (INSPECTOR) {
+    /**
+     * The console tab will have two features :
+     * - hook all console.log call and display them in this panel (and in the browser console as well)
+     * - display all Babylon logs (called with Tools.Log...)
+     */
+    var ConsoleTab = (function (_super) {
+        __extends(ConsoleTab, _super);
+        function ConsoleTab(tabbar, insp) {
+            var _this = _super.call(this, tabbar, 'Console') || this;
+            _this._inspector = insp;
+            // Build the shaders panel : a div that will contains the shaders tree and both shaders panels
+            _this._panel = INSPECTOR.Helpers.CreateDiv('tab-panel');
+            var consolePanel = INSPECTOR.Helpers.CreateDiv('console-panel');
+            var bjsPanel = INSPECTOR.Helpers.CreateDiv('console-panel');
+            _this._panel.appendChild(consolePanel);
+            _this._panel.appendChild(bjsPanel);
+            Split([consolePanel, bjsPanel], {
+                blockDrag: _this._inspector.popupMode,
+                sizes: [50, 50],
+                direction: 'vertical'
+            });
+            // Titles
+            var title = INSPECTOR.Helpers.CreateDiv('console-panel-title', consolePanel);
+            title.textContent = 'Console logs';
+            title = INSPECTOR.Helpers.CreateDiv('console-panel-title', bjsPanel);
+            title.textContent = 'Babylon.js logs';
+            // Contents
+            _this._consolePanelContent = INSPECTOR.Helpers.CreateDiv('console-panel-content', consolePanel);
+            _this._bjsPanelContent = INSPECTOR.Helpers.CreateDiv('console-panel-content', bjsPanel);
+            // save old console.log
+            _this._oldConsoleLog = console.log;
+            _this._oldConsoleWarn = console.warn;
+            _this._oldConsoleError = console.error;
+            console.log = _this._addConsoleLog.bind(_this);
+            console.warn = _this._addConsoleWarn.bind(_this);
+            console.error = _this._addConsoleError.bind(_this);
+            // Bjs logs
+            _this._bjsPanelContent.innerHTML = BABYLON.Tools.LogCache;
+            BABYLON.Tools.OnNewCacheEntry = function (entry) {
+                _this._bjsPanelContent.innerHTML += entry;
+            };
+            return _this;
+            // Testing
+            //console.log("This is a console.log message");
+            // console.log("That's right, console.log calls are hooked to be written in this window");
+            // console.log("Object are also stringify-ed", {width:10, height:30, shape:'rectangular'});
+            // console.warn("This is a console.warn message");
+            // console.error("This is a console.error message");
+            // BABYLON.Tools.Log("This is a message");
+            // BABYLON.Tools.Warn("This is a warning");
+            // BABYLON.Tools.Error("This is a error");
+        }
+        /** Overrides super.dispose */
+        ConsoleTab.prototype.dispose = function () {
+            console.log = this._oldConsoleLog;
+            console.warn = this._oldConsoleWarn;
+            console.error = this._oldConsoleError;
+        };
+        ConsoleTab.prototype._message = function (type, message, caller) {
+            var callerLine = INSPECTOR.Helpers.CreateDiv('caller', this._consolePanelContent);
+            callerLine.textContent = caller;
+            var line = INSPECTOR.Helpers.CreateDiv(type, this._consolePanelContent);
+            if (typeof message === "string") {
+                line.textContent += message;
+            }
+            else {
+                line.textContent += JSON.stringify(message);
+                line.classList.add('object');
+            }
+        };
+        ConsoleTab.prototype._addConsoleLog = function () {
+            var params = [];
+            for (var _i = 0; _i < arguments.length; _i++) {
+                params[_i] = arguments[_i];
+            }
+            // Get caller name if not null
+            var callerFunc = this._addConsoleLog.caller;
+            var caller = callerFunc == null ? "Window" : "Function " + callerFunc['name'] + ": ";
+            for (var i = 0; i < params.length; i++) {
+                this._message('log', params[i], caller);
+                // Write again in console does not work on edge, as the console object                 
+                // is not instantiate if debugger tools is not open
+                if (!INSPECTOR.Helpers.IsBrowserEdge()) {
+                    this._oldConsoleLog(params[i]);
+                }
+            }
+        };
+        ConsoleTab.prototype._addConsoleWarn = function () {
+            var params = [];
+            for (var _i = 0; _i < arguments.length; _i++) {
+                params[_i] = arguments[_i];
+            }
+            // Get caller name if not null
+            var callerFunc = this._addConsoleLog.caller;
+            var caller = callerFunc == null ? "Window" : callerFunc['name'];
+            for (var i = 0; i < params.length; i++) {
+                this._message('warn', params[i], caller);
+                // Write again in console does not work on edge, as the console object 
+                // is not instantiate if debugger tools is not open
+                if (!INSPECTOR.Helpers.IsBrowserEdge()) {
+                    this._oldConsoleWarn(params[i]);
+                }
+            }
+        };
+        ConsoleTab.prototype._addConsoleError = function () {
+            var params = [];
+            for (var _i = 0; _i < arguments.length; _i++) {
+                params[_i] = arguments[_i];
+            }
+            // Get caller name if not null
+            var callerFunc = this._addConsoleLog.caller;
+            var caller = callerFunc == null ? "Window" : callerFunc['name'];
+            for (var i = 0; i < params.length; i++) {
+                this._message('error', params[i], caller);
+                // Write again in console does not work on edge, as the console object 
+                // is not instantiate if debugger tools is not open
+                if (!INSPECTOR.Helpers.IsBrowserEdge()) {
+                    this._oldConsoleError(params[i]);
+                }
+            }
+        };
+        return ConsoleTab;
+    }(INSPECTOR.Tab));
+    INSPECTOR.ConsoleTab = ConsoleTab;
+})(INSPECTOR || (INSPECTOR = {}));
+
+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 INSPECTOR;
+(function (INSPECTOR) {
     var StatsTab = (function (_super) {
         __extends(StatsTab, _super);
         function StatsTab(tabbar, insp) {
@@ -2957,6 +3098,7 @@ var INSPECTOR;
             _this._visibleTabs = [];
             _this._inspector = inspector;
             _this._tabs.push(new INSPECTOR.SceneTab(_this, _this._inspector));
+            _this._tabs.push(new INSPECTOR.ConsoleTab(_this, _this._inspector));
             _this._tabs.push(new INSPECTOR.StatsTab(_this, _this._inspector));
             _this._meshTab = new INSPECTOR.MeshTab(_this, _this._inspector);
             _this._tabs.push(_this._meshTab);

Разница между файлами не показана из-за своего большого размера
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 8 - 1
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -345,8 +345,8 @@ declare module BABYLON {
         /**
         * Static members
         */
-        static MakeYUP: boolean;
         static HomogeneousCoordinates: boolean;
+        static IncrementalLoading: boolean;
         static Extensions: {
             [name: string]: GLTFFileLoaderExtension;
         };
@@ -425,6 +425,13 @@ declare module BABYLON {
          * @param view: the buffer view
          */
         static DecodeBufferToText(view: ArrayBufferView): string;
+        /**
+         * Returns the default material of gltf. Related to
+         * https://github.com/KhronosGroup/glTF/tree/master/specification/1.0#appendix-a-default-material
+         * @param scene: the Babylon.js scene
+         */
+        static GetDefaultMaterial(scene: Scene): ShaderMaterial;
+        private static _DefaultMaterial;
     }
 }
 

+ 65 - 31
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -311,10 +311,6 @@ var BABYLON;
                         }
                         else if (targetPath === "rotationQuaternion") {
                             rotationQuaternion = value;
-                            // Y is Up
-                            if (GLTFFileLoader.MakeYUP) {
-                                rotationQuaternion = rotationQuaternion.multiply(new BABYLON.Quaternion(-0.707107, 0, 0, 0.707107));
-                            }
                         }
                         else {
                             scaling = value;
@@ -351,10 +347,6 @@ var BABYLON;
             var scale = BABYLON.Vector3.FromArray(node.scale || [1, 1, 1]);
             var rotation = BABYLON.Quaternion.FromArray(node.rotation || [0, 0, 0, 1]);
             var position = BABYLON.Vector3.FromArray(node.translation || [0, 0, 0]);
-            // Y is Up
-            if (GLTFFileLoader.MakeYUP) {
-                rotation = rotation.multiply(new BABYLON.Quaternion(-0.707107, 0, 0, 0.707107));
-            }
             mat = BABYLON.Matrix.Compose(scale, rotation, position);
         }
         else {
@@ -555,11 +547,6 @@ var BABYLON;
                     }
                 }
             }
-            if (!parentBone && nodesToRoot.length === 0) {
-                var inverseBindMatrix = BABYLON.Matrix.FromArray(buffer, i * 16);
-                var invertMesh = BABYLON.Matrix.Invert(mesh.getWorldMatrix());
-                mat = mat.multiply(mesh.getWorldMatrix());
-            }
             var bone = new BABYLON.Bone(node.jointName, newSkeleton, parentBone, mat);
             bone.id = id;
         }
@@ -688,7 +675,7 @@ var BABYLON;
                 tempVertexData = undefined;
                 // Sub material
                 var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                multiMat.subMaterials.push(material === null ? gltfRuntime.scene.defaultMaterial : material);
+                multiMat.subMaterials.push(material === null ? BABYLON.GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
                 // Update vertices start and index start
                 verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
                 indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
@@ -733,21 +720,15 @@ var BABYLON;
     /**
     * Configures node from transformation matrix
     */
-    var configureNodeFromMatrix = function (newNode, node) {
+    var configureNodeFromMatrix = function (newNode, node, parent) {
         if (node.matrix) {
             var position = new BABYLON.Vector3(0, 0, 0);
             var rotation = new BABYLON.Quaternion();
             var scaling = new BABYLON.Vector3(0, 0, 0);
             var mat = BABYLON.Matrix.FromArray(node.matrix);
             mat.decompose(scaling, rotation, position);
-            // Y is Up
-            if (GLTFFileLoader.MakeYUP) {
-                rotation = rotation.multiply(new BABYLON.Quaternion(-0.707107, 0, 0, 0.707107));
-            }
             configureNode(newNode, position, rotation, scaling);
-            if (newNode instanceof BABYLON.TargetCamera) {
-                newNode.setTarget(BABYLON.Vector3.Zero());
-            }
+            newNode.computeWorldMatrix(true);
         }
         else {
             configureNode(newNode, BABYLON.Vector3.FromArray(node.translation), BABYLON.Quaternion.FromArray(node.rotation), BABYLON.Vector3.FromArray(node.scale));
@@ -756,7 +737,7 @@ var BABYLON;
     /**
     * Imports a node
     */
-    var importNode = function (gltfRuntime, node, id) {
+    var importNode = function (gltfRuntime, node, id, parent) {
         var lastNode = null;
         if (gltfRuntime.importOnlyMeshes && (node.skin || node.meshes)) {
             if (gltfRuntime.importMeshesNames.length > 0 && gltfRuntime.importMeshesNames.indexOf(node.name) === -1) {
@@ -775,8 +756,6 @@ var BABYLON;
                         skin.babylonSkeleton = newMesh.skeleton;
                     }
                 }
-                if (newMesh.skeleton !== null) {
-                }
                 lastNode = newMesh;
             }
         }
@@ -873,8 +852,8 @@ var BABYLON;
             }
         }
         if (lastNode !== null) {
-            if (node.matrix) {
-                configureNodeFromMatrix(lastNode, node);
+            if (node.matrix && lastNode instanceof BABYLON.Mesh) {
+                configureNodeFromMatrix(lastNode, node, parent);
             }
             else {
                 var translation = node.translation || [0, 0, 0];
@@ -905,7 +884,7 @@ var BABYLON;
             meshIncluded = true;
         }
         if (!node.jointName && meshIncluded) {
-            newNode = importNode(gltfRuntime, node, id);
+            newNode = importNode(gltfRuntime, node, id, parent);
             if (newNode !== null) {
                 newNode.id = id;
                 newNode.parent = parent;
@@ -1424,9 +1403,12 @@ var BABYLON;
                     _this._loadShadersAsync(gltfRuntime, function () {
                         importMaterials(gltfRuntime);
                         postLoad(gltfRuntime);
+                        if (!GLTFFileLoader.IncrementalLoading && onSuccess) {
+                            onSuccess(meshes, null, skeletons);
+                        }
                     });
                 });
-                if (onSuccess) {
+                if (GLTFFileLoader.IncrementalLoading && onSuccess) {
                     onSuccess(meshes, null, skeletons);
                 }
             }, onError);
@@ -1446,9 +1428,14 @@ var BABYLON;
                     _this._loadShadersAsync(gltfRuntime, function () {
                         importMaterials(gltfRuntime);
                         postLoad(gltfRuntime);
+                        if (!GLTFFileLoader.IncrementalLoading) {
+                            onSuccess();
+                        }
                     });
                 });
-                onSuccess();
+                if (GLTFFileLoader.IncrementalLoading) {
+                    onSuccess();
+                }
             }, onError);
             return true;
         };
@@ -1542,8 +1529,8 @@ var BABYLON;
     /**
     * Static members
     */
-    GLTFFileLoader.MakeYUP = false;
     GLTFFileLoader.HomogeneousCoordinates = false;
+    GLTFFileLoader.IncrementalLoading = true;
     GLTFFileLoader.Extensions = {};
     BABYLON.GLTFFileLoader = GLTFFileLoader;
     ;
@@ -1748,8 +1735,55 @@ var BABYLON;
             }
             return result;
         };
+        /**
+         * Returns the default material of gltf. Related to
+         * https://github.com/KhronosGroup/glTF/tree/master/specification/1.0#appendix-a-default-material
+         * @param scene: the Babylon.js scene
+         */
+        GLTFUtils.GetDefaultMaterial = function (scene) {
+            if (!GLTFUtils._DefaultMaterial) {
+                BABYLON.Effect.ShadersStore["GLTFDefaultMaterialVertexShader"] = [
+                    "precision highp float;",
+                    "",
+                    "uniform mat4 worldView;",
+                    "uniform mat4 projection;",
+                    "",
+                    "attribute vec3 position;",
+                    "",
+                    "void main(void)",
+                    "{",
+                    "    gl_Position = projection * worldView * vec4(position, 1.0);",
+                    "}"
+                ].join("\n");
+                BABYLON.Effect.ShadersStore["GLTFDefaultMaterialPixelShader"] = [
+                    "precision highp float;",
+                    "",
+                    "uniform vec4 u_emission;",
+                    "",
+                    "void main(void)",
+                    "{",
+                    "    gl_FragColor = u_emission;",
+                    "}"
+                ].join("\n");
+                var shaderPath = {
+                    vertex: "GLTFDefaultMaterial",
+                    fragment: "GLTFDefaultMaterial"
+                };
+                var options = {
+                    attributes: ["position"],
+                    uniforms: ["worldView", "projection", "u_emission"],
+                    samplers: [],
+                    needAlphaBlending: false
+                };
+                GLTFUtils._DefaultMaterial = new BABYLON.ShaderMaterial("GLTFDefaultMaterial", scene, shaderPath, options);
+                GLTFUtils._DefaultMaterial.setColor4("u_emission", new BABYLON.Color4(0.5, 0.5, 0.5, 1.0));
+            }
+            return GLTFUtils._DefaultMaterial;
+        };
         return GLTFUtils;
     }());
+    // The GLTF default material
+    GLTFUtils._DefaultMaterial = null;
     BABYLON.GLTFUtils = GLTFUtils;
 })(BABYLON || (BABYLON = {}));
 

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 18 - 3
dist/preview release/what's new.md

@@ -7,22 +7,36 @@
  - New DebugLayer. [Doc](TODO) - ([temechon](https://github.com/temechon))
  - New `VideoTexture.CreateFromWebCam` to generate video texture using WebRTC. [Demo](https://www.babylonjs-playground.com#1R77YT#2) - (Sebastien Vandenberghe)(https://github.com/sebavanmicrosoft) / ([deltakosh](https://github.com/deltakosh))
  - New `HolographicCamera` to support rendering on Windows Holographic. - ([sebavan](https://github.com/sebavan))
+ - New Facet Data feature ([jerome](https://github.com/jbousquie))
  - babylon.fontTexture.ts was moved from babylon.js to canvas2D ([nockawa](https://github.com/nockawa))
+ - Multi-platform Compressed Textures for Desktops & Mobile Devices with fall back.  Batch (dos) scripts to convert entire directories of .jpg's & .png's ([jcpalmer](https://github.com/Palmer-JC))
 
 ### Updates
- - Added addChild, removeChild, setParent to AbstractMesh.  ([abow](https://github.com/abow))
- - `Effect.getVertexShaderSource()` and `Effect.getFragmentShaderSource()` now returns the effective shader code (including evaluation of #define) ([deltakosh](https://github.com/deltakosh))
-
+- `VertexBuffer.updatable` is now serialized ([deltakosh](https://github.com/deltakosh))
+- Added intersectsMeshes to Ray ([abow](https://github.com/abow))
+- New RayHelper class for easily viewing and attaching a ray to a mesh.  [Demo](http://www.babylonjs-playground.com/#ZHDBJ#34) - ([abow](https://github.com/abow))
+- `Mesh.applyDisplacementMap` now accepts uvScale and uvOffset parameter ([deltakosh](https://github.com/deltakosh))
+- Added addChild, removeChild, setParent to AbstractMesh ([abow](https://github.com/abow))
+- `Effect.getVertexShaderSource()` and `Effect.getFragmentShaderSource()` now returns the effective shader code (including evaluation of #define) ([deltakosh](https://github.com/deltakosh))
+- GroundMesh : `getHeightAtCoordinates()`, `getNormalAtCoordinates()` and `getNormalAtCoordinatesToRef()` can now work with rotated grounds ([jerome](https://github.com/jbousquie))  
+- `GroundMesh`, `facetData` and `SolidParticleSystem` improvement in normal computations ([jerome](https://github.com/jbousquie))   
+ 
 ### Canvas2D
 
 #### Major Updates
+ - Added text alignment and word wrap to Text2D ([abow](https://github.com/abow))
  - Support of [Scale9Sprite](http://doc.babylonjs.com/overviews/Canvas2D_Sprite2D#scale9sprite-feature) feature in Sprite2D ([nockawa](https://github.com/nockawa))
  - Support of [AtlasPicture](http://doc.babylonjs.com/overviews/Canvas2D_AtlasPicture) to store many pictures into a bit one, with the possibility to create one/many Sprite2D out of it. ([nockawa](https://github.com/nockawa))
  - Support of BMFont with the BitmaptFontTexture class, Text2D has now a bitmapFontTexture setting in the constructor to display text using a BitmapFontTexture ([nockawa](https://github.com/nockawa))
 
 #### Minor Updates
+ - WorldSpaceCanvas: TrackNode feature, a WSC can follow a Scene Node with an optional billboarding feature (always facing the camera)[Demo](http://babylonjs-playground.com/#1KYG17#1)
+ - WorldSpaceCanvas: new setting unitScaleFactor to generated a bigger canvas than the world space mesh size. If you create a WSC with a size of 200;100 and a uSF of 3, the 3D Plane displaying the canvas will be 200;100 of scene units, the WSC will be 600;300 of pixels units.
+
+#### Bug Fixing
  - Fix Rotation issue when the Parent's Primitive hadn't a identity scale. ([nockawa](https://github.com/nockawa))
  - Primitive's position computed from TrackedNode are now hidden when the node is out of the Viewing Frustum ([nockawa](https://github.com/nockawa))
+ - WorldSpaceCanvas: sideOrientation is finally working, you can try Mesh.DOUBLESIDE to make you Canvas visible on both sides. ([nockawa](https://github.com/nockawa))
 
 
 ### Exporters
@@ -30,6 +44,7 @@
 ### API doc
 
 ### Bug fixes
+ - Fixed an issue with Mesh.attachToBone when a mesh is moving and an animation is changed ([abow](https://github.com/abow))
  - Fixed an issue withaspect ratio when using CreateScreenshot ([deltakosh](https://github.com/deltakosh))
 
 ### Breaking changes

+ 6 - 0
inspector/sass/main.scss

@@ -22,6 +22,11 @@
   // The panel containing the two subpanel : tree and details
   .insp-right-panel {
     width: 750px;
+
+    &.popupmode {
+      width:100% !important;
+    }
+    
     display:flex; 
     flex-direction: column;
     flex-shrink: 0;
@@ -83,6 +88,7 @@
   @import 'treeTool';
   @import "tabPanel";
   @import "tabs/shaderTab";
+  @import "tabs/consoleTab";
   @import "tabs/statsTab";
 
   @import "tree";

+ 51 - 0
inspector/sass/tabs/_consoleTab.scss

@@ -0,0 +1,51 @@
+.tab-panel {
+
+    .console-panel {
+        min-height              : 100px;
+        user-select             : text;
+        box-sizing              : border-box;
+        padding                 : 0 15px;    
+        
+        .console-panel-title {
+            height              : 25px;
+            border-bottom       : 1px solid $background-lighter2;
+            text-transform      : uppercase;
+            line-height         : 25px;
+            margin-bottom       : 10px; 
+        }
+        
+        .console-panel-content {            
+            overflow-y              : auto;
+            overflow-x              : hidden;    
+            height                  : calc(100% - 30px);
+        }  
+
+        .defaut-line {
+            word-wrap: break-word;
+            padding: 3px 0 3px 5px;
+        }
+
+        .caller {
+            padding: 3px 0 3px 0;
+            color:darken($color-bot, 10%);
+        }
+
+        .log {  
+            @extend .defaut-line;
+            color:white;
+        }   
+        
+        .warn {
+            @extend .defaut-line;
+            color:orange;
+        }
+        .error {
+            @extend .defaut-line;
+            color:orangered; 
+        }
+        .object {
+            @extend .defaut-line;
+            color:$color-bot;
+        }
+    }
+}

+ 8 - 0
inspector/src/Inspector.ts

@@ -148,6 +148,7 @@ module INSPECTOR {
                 this._buildInspector(inspector);   
                 // Send resize event to the window
                 Helpers.SEND_EVENT('resize');
+                this._tabbar.updateWidth();
             }
 
             // Refresh the inspector if the browser is not edge
@@ -297,10 +298,17 @@ module INSPECTOR {
                 this._c2diwrapper  = Helpers.CreateDiv('insp-wrapper', popup.document.body);
                 // add inspector     
                 let inspector      = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                inspector.classList.add('popupmode');
                 // and build it in the popup  
                 this._buildInspector(inspector); 
                 // Rebuild it
                 this.refresh(); 
+
+                popup.addEventListener('resize', () => {                    
+                    if (this._tabbar) {
+                        this._tabbar.updateWidth()
+                    }
+                });
             }             
         }
     }

+ 149 - 0
inspector/src/tabs/ConsoleTab.ts

@@ -0,0 +1,149 @@
+module INSPECTOR {
+
+    /** 
+     * The console tab will have two features : 
+     * - hook all console.log call and display them in this panel (and in the browser console as well)
+     * - display all Babylon logs (called with Tools.Log...)
+     */
+    export class ConsoleTab extends Tab {
+
+        private _inspector : Inspector;
+        
+        private _consolePanelContent : HTMLElement;
+        private _bjsPanelContent : HTMLElement;
+
+        private _oldConsoleLog : any;
+        private _oldConsoleWarn : any;
+        private _oldConsoleError : any;
+        
+
+        constructor(tabbar:TabBar, insp:Inspector) {
+            super(tabbar, 'Console');            
+            this._inspector = insp;
+
+            // Build the shaders panel : a div that will contains the shaders tree and both shaders panels
+            this._panel         = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+
+            let consolePanel = Helpers.CreateDiv('console-panel') as HTMLDivElement;
+            let bjsPanel     = Helpers.CreateDiv('console-panel') as HTMLDivElement;
+
+            this._panel.appendChild(consolePanel);
+            this._panel.appendChild(bjsPanel);
+            
+            
+            Split([consolePanel, bjsPanel], {
+                blockDrag : this._inspector.popupMode,
+                sizes:[50, 50],
+                direction:'vertical'}
+            );  
+
+            // Titles
+            let title = Helpers.CreateDiv('console-panel-title', consolePanel);
+            title.textContent = 'Console logs';
+            title = Helpers.CreateDiv('console-panel-title', bjsPanel);
+            title.textContent = 'Babylon.js logs';
+
+            // Contents
+            this._consolePanelContent = Helpers.CreateDiv('console-panel-content', consolePanel) as HTMLDivElement;
+            this._bjsPanelContent     = Helpers.CreateDiv('console-panel-content', bjsPanel) as HTMLDivElement;
+
+            // save old console.log
+            this._oldConsoleLog       = console.log;
+            this._oldConsoleWarn      = console.warn;
+            this._oldConsoleError     = console.error;
+
+            console.log               = this._addConsoleLog.bind(this);
+            console.warn              = this._addConsoleWarn.bind(this);
+            console.error             = this._addConsoleError.bind(this);
+
+            // Bjs logs
+            this._bjsPanelContent.innerHTML = BABYLON.Tools.LogCache;
+            BABYLON.Tools.OnNewCacheEntry = (entry: string) => {
+                this._bjsPanelContent.innerHTML += entry;
+            };
+
+            // Testing
+            //console.log("This is a console.log message");
+            // console.log("That's right, console.log calls are hooked to be written in this window");
+            // console.log("Object are also stringify-ed", {width:10, height:30, shape:'rectangular'});
+            // console.warn("This is a console.warn message");
+            // console.error("This is a console.error message");
+
+            // BABYLON.Tools.Log("This is a message");
+            // BABYLON.Tools.Warn("This is a warning");
+            // BABYLON.Tools.Error("This is a error");
+
+        }
+
+        /** Overrides super.dispose */
+        public dispose() {
+            console.log = this._oldConsoleLog;
+            console.warn = this._oldConsoleWarn;
+            console.error = this._oldConsoleError;
+
+        }
+
+        private _message(type:string, message:any, caller:string) {
+            let callerLine = Helpers.CreateDiv('caller', this._consolePanelContent);
+            callerLine.textContent = caller;
+
+            let line = Helpers.CreateDiv(type, this._consolePanelContent);
+            if (typeof message === "string") {
+                line.textContent += message ; 
+            } else {
+                line.textContent += JSON.stringify(message) ;
+                line.classList.add('object')
+            }
+        }
+
+        private _addConsoleLog(...params : any[]) {
+            
+            // Get caller name if not null
+            let callerFunc = this._addConsoleLog.caller as Function;
+            let caller = callerFunc==null? "Window" : "Function "+callerFunc['name'] + ": ";
+
+            for (var i = 0; i < params.length; i++) {
+                this._message('log', params[i], caller);
+                // Write again in console does not work on edge, as the console object                 
+                // is not instantiate if debugger tools is not open
+                if (!Helpers.IsBrowserEdge()) {    
+                    this._oldConsoleLog(params[i]);
+                }
+            }
+        }
+
+        private _addConsoleWarn(...params : any[]) {
+            
+            // Get caller name if not null
+            let callerFunc = this._addConsoleLog.caller as Function;
+            let caller = callerFunc==null? "Window" : callerFunc['name'];
+
+            for (var i = 0; i < params.length; i++) {
+                this._message('warn', params[i], caller);
+                // Write again in console does not work on edge, as the console object 
+                // is not instantiate if debugger tools is not open
+                if (!Helpers.IsBrowserEdge()) {    
+                    this._oldConsoleWarn(params[i]);
+                }
+            }
+        }
+
+        private _addConsoleError(...params : any[]) {
+            
+            // Get caller name if not null
+            let callerFunc = this._addConsoleLog.caller as Function;
+            let caller = callerFunc==null? "Window" : callerFunc['name'];
+
+            for (var i = 0; i < params.length; i++) {
+                this._message('error', params[i], caller);
+                // Write again in console does not work on edge, as the console object 
+                // is not instantiate if debugger tools is not open
+                if (!Helpers.IsBrowserEdge()) {    
+                    this._oldConsoleError(params[i]);
+                }
+            }
+        }
+
+    }
+
+}

+ 2 - 1
inspector/src/tabs/TabBar.ts

@@ -25,8 +25,9 @@ module INSPECTOR {
             super();
             this._inspector = inspector;
             this._tabs.push(new SceneTab(this, this._inspector));
+            this._tabs.push(new ConsoleTab(this, this._inspector));
             this._tabs.push(new StatsTab(this, this._inspector));
-            this._meshTab = new MeshTab(this, this._inspector);
+            this._meshTab = new MeshTab(this, this._inspector); 
             this._tabs.push(this._meshTab);
             this._tabs.push(new ShaderTab(this, this._inspector));
             this._tabs.push(new LightTab(this, this._inspector));

+ 0 - 1
inspector/test/index.js

@@ -89,7 +89,6 @@ var Test = (function () {
     };
     Test.prototype._initGame = function () {
         this._createCanvas();
-        BABYLON.SceneLoader.ImportMesh('', 'test/', 'Rabbit.babylon', this.scene);
     };
     /**
      * Create the canvas2D

+ 7 - 0
loaders/src/glTF/README.md

@@ -29,6 +29,13 @@ In order the fix the UP vector (Y with Babylon.js) if you want to play with phys
 BABYLON.GLTFFileLoader.MakeYUP = true; // false by default
 ```
 
+If you want to disable incremental loading (which is the default behavior), you can set the property IncrementalLoading to false.
+Then, you'll be able to be called back with all geometries and shaders loaded.
+For example, you can retrieve the real bounding infos of a mesh loaded using the loader.
+```
+BABYLON.GLTFFileLoader.IncrementalLoading = false; // true by default
+```
+
 In order to work with homogeneous coordinates (that can be available with some converters and exporters):
 ```
 BABYLON.GLTFFileLoader.HomogeneousCoordinates = true; // false by default

+ 20 - 40
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -264,10 +264,6 @@ module BABYLON {
                         }
                         else if (targetPath === "rotationQuaternion") {
                             rotationQuaternion = value;
-                            // Y is Up
-                            if (GLTFFileLoader.MakeYUP) {
-                                rotationQuaternion = rotationQuaternion.multiply(new Quaternion(-0.707107, 0, 0, 0.707107));
-                            }
                         }
                         else {
                             scaling = value;
@@ -312,11 +308,6 @@ module BABYLON {
             var rotation = Quaternion.FromArray(node.rotation || [0, 0, 0, 1]);
             var position = Vector3.FromArray(node.translation || [0, 0, 0]);
 
-            // Y is Up
-            if (GLTFFileLoader.MakeYUP) {
-                rotation = rotation.multiply(new Quaternion(-0.707107, 0, 0, 0.707107));
-            }
-
             mat = Matrix.Compose(scale, rotation, position);
         }
         else {
@@ -564,13 +555,6 @@ module BABYLON {
                 }
             }
 
-            if (!parentBone && nodesToRoot.length === 0) {
-                var inverseBindMatrix = Matrix.FromArray(buffer, i * 16);
-                var invertMesh = Matrix.Invert(mesh.getWorldMatrix());
-
-                mat = mat.multiply(mesh.getWorldMatrix());
-            }
-
             var bone = new Bone(node.jointName, newSkeleton, parentBone, mat);
             bone.id = id;
         }
@@ -727,7 +711,7 @@ module BABYLON {
 
                 // Sub material
                 var material = gltfRuntime.scene.getMaterialByID(primitive.material);
-                multiMat.subMaterials.push(material === null ? gltfRuntime.scene.defaultMaterial : material);
+                multiMat.subMaterials.push(material === null ? GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
 
                 // Update vertices start and index start
                 verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
@@ -737,7 +721,6 @@ module BABYLON {
 
         // Apply geometry
         geometry.setAllVerticesData(vertexData, false);
-
         newMesh.computeWorldMatrix(true);
 
         // Apply submeshes
@@ -785,7 +768,7 @@ module BABYLON {
     /**
     * Configures node from transformation matrix
     */
-    var configureNodeFromMatrix = (newNode: any, node: IGLTFNode) => {
+    var configureNodeFromMatrix = (newNode: Mesh, node: IGLTFNode, parent: Node) => {
         if (node.matrix) {
             var position = new Vector3(0, 0, 0);
             var rotation = new Quaternion();
@@ -793,16 +776,8 @@ module BABYLON {
             var mat = Matrix.FromArray(node.matrix);
             mat.decompose(scaling, rotation, position);
 
-            // Y is Up
-            if (GLTFFileLoader.MakeYUP) {
-                rotation = rotation.multiply(new Quaternion(-0.707107, 0, 0, 0.707107));
-            }
-
             configureNode(newNode, position, rotation, scaling);
-
-            if (newNode instanceof TargetCamera) {
-                (<TargetCamera>newNode).setTarget(Vector3.Zero());
-            }
+            newNode.computeWorldMatrix(true);
         }
         else {
             configureNode(newNode, Vector3.FromArray(node.translation), Quaternion.FromArray(node.rotation), Vector3.FromArray(node.scale));
@@ -812,7 +787,7 @@ module BABYLON {
     /**
     * Imports a node
     */
-    var importNode = (gltfRuntime: IGLTFRuntime, node: IGLTFNode, id: string): Node => {
+    var importNode = (gltfRuntime: IGLTFRuntime, node: IGLTFNode, id: string, parent: Node): Node => {
         var lastNode: Node = null;
 
         if (gltfRuntime.importOnlyMeshes && (node.skin || node.meshes)) {
@@ -837,10 +812,6 @@ module BABYLON {
                     }
                 }
 
-                if (newMesh.skeleton !== null) {
-                    //newMesh.useBones = true;
-                }
-
                 lastNode = newMesh;
             }
         }
@@ -959,8 +930,8 @@ module BABYLON {
         }
 
         if (lastNode !== null) {
-            if (node.matrix) {
-                configureNodeFromMatrix(lastNode, node);
+            if (node.matrix && lastNode instanceof Mesh) {
+                configureNodeFromMatrix(lastNode, node, parent);
             }
             else {
                 var translation = node.translation || [0, 0, 0];
@@ -970,7 +941,6 @@ module BABYLON {
             }
 
             lastNode.updateCache(true);
-
             node.babylonNode = lastNode;
         }
 
@@ -997,7 +967,7 @@ module BABYLON {
         }
 
         if (!node.jointName && meshIncluded) {
-            newNode = importNode(gltfRuntime, node, id);
+            newNode = importNode(gltfRuntime, node, id, parent);
 
             if (newNode !== null) {
                 newNode.id = id;
@@ -1571,8 +1541,8 @@ module BABYLON {
         /**
         * Static members
         */
-        public static MakeYUP: boolean = false;
         public static HomogeneousCoordinates: boolean = false;
+        public static IncrementalLoading: boolean = true;
 
         public static Extensions: { [name: string]: GLTFFileLoaderExtension } = {};
 
@@ -1636,10 +1606,14 @@ module BABYLON {
                     this._loadShadersAsync(gltfRuntime, () => {
                         importMaterials(gltfRuntime);
                         postLoad(gltfRuntime);
+
+                        if (!GLTFFileLoader.IncrementalLoading && onSuccess) {
+                            onSuccess(meshes, null, skeletons);
+                        }
                     });
                 });
 
-                if (onSuccess) {
+                if (GLTFFileLoader.IncrementalLoading && onSuccess) {
                     onSuccess(meshes, null, skeletons);
                 }
             }, onError);
@@ -1662,10 +1636,16 @@ module BABYLON {
                     this._loadShadersAsync(gltfRuntime, () => {
                         importMaterials(gltfRuntime);
                         postLoad(gltfRuntime);
+
+                        if (!GLTFFileLoader.IncrementalLoading) {
+                            onSuccess();
+                        }
                     });
                 });
 
-                onSuccess();
+                if (GLTFFileLoader.IncrementalLoading) {
+                    onSuccess();
+                }
             }, onError);
 
             return true;

+ 54 - 0
loaders/src/glTF/babylon.glTFFileLoaderUtils.ts

@@ -197,5 +197,59 @@ module BABYLON {
 
             return result;
         }
+
+        /**
+         * Returns the default material of gltf. Related to
+         * https://github.com/KhronosGroup/glTF/tree/master/specification/1.0#appendix-a-default-material
+         * @param scene: the Babylon.js scene
+         */
+        public static GetDefaultMaterial(scene: Scene): ShaderMaterial {
+            if (!GLTFUtils._DefaultMaterial) {
+                Effect.ShadersStore["GLTFDefaultMaterialVertexShader"] = [
+                    "precision highp float;",
+                    "",
+                    "uniform mat4 worldView;",
+                    "uniform mat4 projection;",
+                    "",
+                    "attribute vec3 position;",
+                    "",
+                    "void main(void)",
+                    "{",
+                    "    gl_Position = projection * worldView * vec4(position, 1.0);",
+                    "}"
+                ].join("\n");
+
+                Effect.ShadersStore["GLTFDefaultMaterialPixelShader"] = [
+                    "precision highp float;",
+                    "",
+                    "uniform vec4 u_emission;",
+                    "",
+                    "void main(void)",
+                    "{",
+                    "    gl_FragColor = u_emission;",
+                    "}"
+                ].join("\n");
+
+                var shaderPath = {
+                    vertex: "GLTFDefaultMaterial",
+                    fragment: "GLTFDefaultMaterial"
+                };
+
+                var options = {
+                    attributes: ["position"],
+                    uniforms: ["worldView", "projection", "u_emission"],
+                    samplers: [],
+                    needAlphaBlending: false
+                };
+
+                GLTFUtils._DefaultMaterial = new ShaderMaterial("GLTFDefaultMaterial", scene, shaderPath, options);
+                GLTFUtils._DefaultMaterial.setColor4("u_emission", new Color4(0.5, 0.5, 0.5, 1.0));
+            }
+
+            return GLTFUtils._DefaultMaterial;
+        }
+
+        // The GLTF default material
+        private static _DefaultMaterial: ShaderMaterial = null;
     }
 }

+ 38 - 13
src/Animations/babylon.animatable.ts

@@ -99,27 +99,52 @@
         }
 
         public stop(animationName?: string): void {
-            var idx = this._scene._activeAnimatables.indexOf(this);
-
-            if (idx > -1) {
-                var animations = this._animations;
-                var numberOfAnimationsStopped = 0;
-                for (var index = animations.length - 1; index >= 0; index--) {
-                    if (typeof animationName === "string" && animations[index].name != animationName) {
-                        continue;
+            
+            if (animationName) {
+
+                var idx = this._scene._activeAnimatables.indexOf(this);
+
+                if (idx > -1) {
+
+                    var animations = this._animations;
+                    var numberOfAnimationsStopped = 0;
+                    
+                    for (var index = animations.length - 1; index >= 0; index--) {
+                        if (typeof animationName === "string" && animations[index].name != animationName) {
+                            continue;
+                        }
+
+                        animations[index].reset();
+                        animations.splice(index, 1);
+                        numberOfAnimationsStopped++;
+                    }
+
+                    if (animations.length == numberOfAnimationsStopped) {
+                        this._scene._activeAnimatables.splice(idx, 1);
+
+                        if (this.onAnimationEnd) {
+                            this.onAnimationEnd();
+                        }
                     }
-                    animations[index].reset();
-                    animations.splice(index, 1);
-                    numberOfAnimationsStopped ++;
                 }
 
-                if (animations.length == numberOfAnimationsStopped) {
-                    this._scene._activeAnimatables.splice(idx, 1);
+            } else {
+
+                var index = this._scene._activeAnimatables.indexOf(this);
 
+                if (index > -1) {
+                    this._scene._activeAnimatables.splice(index, 1);
+                    var animations = this._animations;
+                    
+                    for (var index = 0; index < animations.length; index++) {
+                        animations[index].reset();
+                    }
+                    
                     if (this.onAnimationEnd) {
                         this.onAnimationEnd();
                     }
                 }
+
             }
         }
 

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

@@ -308,6 +308,9 @@
         public clone(): Animation {
             var clone = new Animation(this.name, this.targetPropertyPath.join("."), this.framePerSecond, this.dataType, this.loopMode);
 
+            clone.enableBlending = this.enableBlending;
+            clone.blendingSpeed = this.blendingSpeed;
+
             if (this._keys) {
                 clone.setKeys(this._keys);
             }
@@ -658,6 +661,8 @@
             serializationObject.framePerSecond = this.framePerSecond;
             serializationObject.dataType = this.dataType;
             serializationObject.loopBehavior = this.loopMode;
+            serializationObject.enableBlending = this.enableBlending;
+            serializationObject.blendingSpeed = this.blendingSpeed;
 
             var dataType = this.dataType;
             serializationObject.keys = [];
@@ -755,6 +760,14 @@
             var data;
             var index: number;
 
+            if (parsedAnimation.enableBlending) {
+                animation.enableBlending = parsedAnimation.enableBlending;
+            }
+
+            if (parsedAnimation.blendingSpeed) {
+                animation.blendingSpeed = parsedAnimation.blendingSpeed;
+            }
+
             for (index = 0; index < parsedAnimation.keys.length; index++) {
                 var key = parsedAnimation.keys[index];
 

+ 2 - 2
src/Cameras/VR/babylon.vrCameraMetrics.ts

@@ -51,8 +51,8 @@
             result.vResolution = 800;
             result.hScreenSize = 0.149759993;
             result.vScreenSize = 0.0935999975;
-            result.vScreenCenter = 0.0467999987,
-                result.eyeToScreenDistance = 0.0410000011;
+            result.vScreenCenter = 0.0467999987;
+            result.eyeToScreenDistance = 0.0410000011;
             result.lensSeparationDistance = 0.0635000020;
             result.interpupillaryDistance = 0.0640000030;
             result.distortionK = [1.0, 0.219999999, 0.239999995, 0.0];

+ 13 - 0
src/Cameras/VR/babylon.vrDeviceOrientationCamera.ts

@@ -14,6 +14,19 @@ module BABYLON {
         }
     }
 
+    export class VRDeviceOrientationGamepadCamera extends VRDeviceOrientationFreeCamera {
+
+        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
+            super(name, position, scene, compensateDistortion, vrCameraMetrics);
+            
+            this.inputs.addGamepad();
+        }
+
+        public getClassName(): string {
+            return "VRDeviceOrientationGamepadCamera";
+        }
+    }
+
     export class VRDeviceOrientationArcRotateCamera extends ArcRotateCamera {
 
         constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {

+ 13 - 5
src/Cameras/babylon.camera.ts

@@ -403,6 +403,10 @@
                 this._markSyncedWithParent();
             }
 
+            if (this._cameraRigParams.vrPreViewMatrix) {
+                this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
+            }
+
             this._currentRenderId = this.getScene().getRenderId();
 
             return this._computedViewMatrix;
@@ -425,11 +429,11 @@
                 this._projectionMatrix = projection;
             }
         };
-        
+
         public unfreezeProjectionMatrix(): void {
             this._doNotComputeProjectionMatrix = false;
         };
-        
+
         public getProjectionMatrix(force?: boolean): Matrix {
             if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
                 return this._projectionMatrix;
@@ -707,15 +711,15 @@
             return SerializationHelper.Clone(Camera.GetConstructorFromName(this.getClassName(), name, this.getScene(), this.interaxialDistance, this.isStereoscopicSideBySide), this);
         }
 
-        public getDirection(localAxis:Vector3): Vector3 {
+        public getDirection(localAxis: Vector3): Vector3 {
             var result = Vector3.Zero();
 
             this.getDirectionToRef(localAxis, result);
-            
+
             return result;
         }
 
-        public getDirectionToRef(localAxis:Vector3, result:Vector3): void {
+        public getDirectionToRef(localAxis: Vector3, result: Vector3): void {
             Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
         }
 
@@ -735,10 +739,14 @@
                     return () => new TouchCamera(name, Vector3.Zero(), scene);
                 case "VirtualJoysticksCamera":
                     return () => new VirtualJoysticksCamera(name, Vector3.Zero(), scene);
+                case "HolographicCamera":
+                    return () => new HolographicCamera(name, scene);
                 case "WebVRFreeCamera":
                     return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
                 case "VRDeviceOrientationFreeCamera":
                     return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
+                case "VRDeviceOrientationGamepadCamera":
+                    return () => new VRDeviceOrientationGamepadCamera(name, Vector3.Zero(), scene);
                 case "AnaglyphArcRotateCamera":
                     return () => new AnaglyphArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), interaxial_distance, scene);
                 case "AnaglyphFreeCamera":

+ 44 - 32
src/Culling/babylon.ray.ts

@@ -6,12 +6,8 @@
         private _tvec: Vector3;
         private _qvec: Vector3;
 
-        private _renderPoints: Vector3[];
-        private _renderLine: LinesMesh;
-        private _renderFunction: () => void;
-        private _scene: Scene;
-        private _show = false;
         private _tmpRay: Ray;
+        private _rayHelper: RayHelper;
 
         constructor(public origin: Vector3, public direction: Vector3, public length: number = Number.MAX_VALUE) {
         }
@@ -216,50 +212,66 @@
 
         }
 
-        public show(scene:Scene, color:Color3): void{
-
-            if(!this._show){
+        public intersectsMeshes(meshes:Array<AbstractMesh>, fastCheck?: boolean, results?:Array<PickingInfo>): Array<PickingInfo> {
 
-                this._renderFunction = this._render.bind(this);
-                this._show = true;
-                this._scene = scene;
-                this._renderPoints = [this.origin, this.origin.add(this.direction.scale(this.length))];
-                this._renderLine = Mesh.CreateLines("ray", this._renderPoints, scene, true);
+            if(results){
+                results.length = 0;
+            }else{
+                results = [];
+            }
 
-                this._scene.registerBeforeRender(this._renderFunction);
+            for(var i = 0; i < meshes.length; i++){
+                var pickInfo = this.intersectsMesh(meshes[i], fastCheck);
 
+                if(pickInfo.hit){
+                    results.push(pickInfo);
+                }
             }
 
-            if (color) {
-                this._renderLine.color.copyFrom(color);
-            }
+            results.sort(this._comparePickingInfo);
+
+            return results;
 
         }
 
-        public hide(): void{
+        private _comparePickingInfo(pickingInfoA:PickingInfo, pickingInfoB:PickingInfo): number{
 
-            if(this._show){
-                this._show = false;
-                this._scene.unregisterBeforeRender(this._renderFunction);
+            if(pickingInfoA.distance < pickingInfoB.distance){
+                return -1;
+            }else if(pickingInfoA.distance > pickingInfoB.distance){
+                return 1;
+            }else{
+                return 0;
             }
 
-            if(this._renderLine){
-                this._renderLine.dispose();
-                this._renderLine = null;
-                this._renderPoints = null;
+        }
+
+        /**
+         *  @Deprecated. Use new RayHelper.show() instead.
+         * */
+        public show(scene:Scene, color:Color3): void{
+
+            console.warn('Ray.show() has been deprecated.  Use new RayHelper.show() instead.');
+
+            if(!this._rayHelper){
+                this._rayHelper = new RayHelper(this);
             }
+            
+            this._rayHelper.show(scene, color);
 
         }
 
-        private _render(): void {
-
-            var point = this._renderPoints[1];
+        /**
+         *  @Deprecated. Use new RayHelper.hide() instead.
+         * */
+        public hide(): void{
 
-            point.copyFrom(this.direction);
-            point.scaleInPlace(this.length);
-            point.addInPlace(this.origin);
+            console.warn('Ray.hide() has been deprecated.  Use new RayHelper.hide() instead.');
 
-            Mesh.CreateLines("ray", this._renderPoints, this._scene, true, this._renderLine);
+            if(this._rayHelper){
+                this._rayHelper.hide();
+                this._rayHelper = null;
+            }
 
         }
 

+ 146 - 0
src/Culling/babylon.rayHelper.ts

@@ -0,0 +1,146 @@
+module BABYLON {
+    export class RayHelper {
+        
+        public ray:Ray;
+
+        private _renderPoints: Vector3[];
+        private _renderLine: LinesMesh;
+        private _renderFunction: () => void;
+        private _scene: Scene;
+        
+        private _updateToMeshFunction: () => void;
+        private _attachedToMesh: AbstractMesh;
+        private _meshSpaceDirection: Vector3;
+        private _meshSpaceOrigin: Vector3;
+
+        constructor(ray:Ray) {
+            this.ray = ray;
+        }
+
+        public show(scene:Scene, color:Color3): void{
+
+            if(!this._renderFunction){
+
+                var ray = this.ray;
+
+                this._renderFunction = this._render.bind(this);
+                this._scene = scene;
+                this._renderPoints = [ray.origin, ray.origin.add(ray.direction.scale(ray.length))];
+                this._renderLine = Mesh.CreateLines("ray", this._renderPoints, scene, true);
+
+                this._scene.registerBeforeRender(this._renderFunction);
+
+            }
+
+            if (color) {
+                this._renderLine.color.copyFrom(color);
+            }
+
+        }
+
+        public hide(): void{
+
+            if(this._renderFunction){
+                this._scene.unregisterBeforeRender(this._renderFunction);
+                this._scene = null;
+                this._renderFunction = null;
+                this._renderLine.dispose();
+                this._renderLine = null;
+                this._renderPoints = null;
+            }
+
+        }
+
+        private _render(): void {
+
+            var ray = this.ray;
+
+            var point = this._renderPoints[1];
+            var len = Math.min(ray.length, 1000000);
+            
+            point.copyFrom(ray.direction);
+            point.scaleInPlace(len);
+            point.addInPlace(ray.origin);
+
+            Mesh.CreateLines("ray", this._renderPoints, this._scene, true, this._renderLine);
+
+        }
+
+        public attachToMesh(mesh:AbstractMesh, meshSpaceDirection?:Vector3, meshSpaceOrigin?:Vector3, length?:number): void{
+
+            this._attachedToMesh = mesh;
+
+            var ray = this.ray;
+
+            if(!ray.direction){
+                ray.direction = Vector3.Zero();
+            }
+
+            if(!ray.origin){
+                ray.origin = Vector3.Zero();
+            }
+
+            if(length){
+                ray.length = length;
+            }
+
+            if(!meshSpaceOrigin){
+                meshSpaceOrigin = Vector3.Zero();
+            }
+
+            if(!meshSpaceDirection){
+                // -1 so that this will work with Mesh.lookAt
+                meshSpaceDirection = new Vector3(0, 0, -1);
+            }
+
+            if(!this._meshSpaceDirection){
+                this._meshSpaceDirection = meshSpaceDirection.clone();
+                this._meshSpaceOrigin = meshSpaceOrigin.clone();
+            }else{
+                this._meshSpaceDirection.copyFrom(meshSpaceDirection);
+                this._meshSpaceOrigin.copyFrom(meshSpaceOrigin);
+            }
+
+            if(!this._updateToMeshFunction){
+                this._updateToMeshFunction = this._updateToMesh.bind(this);
+                this._attachedToMesh.getScene().registerBeforeRender(this._updateToMeshFunction);
+            }
+
+            this._updateToMesh();
+
+        }
+
+        public detachFromMesh(): void{
+
+            if(this._attachedToMesh){
+                this._attachedToMesh.getScene().unregisterBeforeRender(this._updateToMeshFunction);
+                this._attachedToMesh = null;
+                this._updateToMeshFunction = null;
+            }
+
+        }
+
+        private _updateToMesh(): void{
+
+            var ray = this.ray;
+
+            if(this._attachedToMesh._isDisposed){
+                this.detachFromMesh();
+                return;
+            }
+
+            this._attachedToMesh.getDirectionToRef(this._meshSpaceDirection, ray.direction);
+            Vector3.TransformCoordinatesToRef(this._meshSpaceOrigin, this._attachedToMesh.getWorldMatrix(), ray.origin);
+
+        }
+
+        public dispose(): void{
+
+            this.hide();
+            this.detachFromMesh();
+            this.ray = null;
+
+        }
+
+    }
+}

+ 2 - 0
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -436,6 +436,8 @@
             if (this._boxBlurPostprocess) {
                 this._boxBlurPostprocess.dispose();
             }
+
+            this._light._shadowGenerator = null;
         }
 
         public serialize(): any {

+ 2 - 1
src/Materials/Textures/babylon.hdrCubeTexture.ts

@@ -384,7 +384,7 @@ module BABYLON {
             var texture = null;
             if (parsedTexture.name && !parsedTexture.isRenderTarget) {
                 var size = parsedTexture.isBABYLONPreprocessed ? null : parsedTexture.size;
-                texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size,
+                texture = new BABYLON.HDRCubeTexture(rootUrl + parsedTexture.name, scene, size, parsedTexture.noMipmap,
                     parsedTexture.generateHarmonics, parsedTexture.useInGammaSpace, parsedTexture.usePMREMGenerator);
                 texture.name = parsedTexture.name;
                 texture.hasAlpha = parsedTexture.hasAlpha;
@@ -411,6 +411,7 @@ module BABYLON {
             serializationObject.usePMREMGenerator = this._usePMREMGenerator;
             serializationObject.isBABYLONPreprocessed = this._isBABYLONPreprocessed;
             serializationObject.customType = "BABYLON.HDRCubeTexture";
+            serializationObject.noMipmap = this._noMipmap;
             
             return serializationObject;
         }

+ 1 - 1
src/Materials/babylon.pbrMaterial.ts

@@ -938,7 +938,7 @@
                 this._defines.RADIANCEOVERALPHA = true;
             }
 
-            if (this.metallic !== undefined || this.roughness !== undefined) {
+            if ((this.metallic !== undefined && this.metallic !== null) || (this.roughness !== undefined && this.roughness !== null)) {
                 this._defines.METALLICWORKFLOW = true;
             }
 

+ 1 - 1
src/Math/babylon.math.ts

@@ -1790,7 +1790,7 @@
             return r;
         }
 
-        public substract(otherSize: Size): Size {
+        public subtract(otherSize: Size): Size {
             let r = new Size(this.width - otherSize.width, this.height - otherSize.height);
             return r;
         }

+ 51 - 15
src/Mesh/babylon.geometry.ts

@@ -290,6 +290,8 @@
                 this._engine._releaseBuffer(this._indexBuffer);
             }
 
+            this._disposeVertexArrayObjects();
+
             this._indices = indices;
             if (this._meshes.length !== 0 && this._indices) {
                 this._indexBuffer = this._engine.createIndexBuffer(this._indices);
@@ -614,47 +616,80 @@
 
             if (this.isVerticesDataPresent(VertexBuffer.PositionKind)) {
                 serializationObject.positions = this.getVerticesData(VertexBuffer.PositionKind);
+                if (this.getVertexBuffer(VertexBuffer.PositionKind).isUpdatable) {
+                    serializationObject.positions._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                 serializationObject.normals = this.getVerticesData(VertexBuffer.NormalKind);
+                if (this.getVertexBuffer(VertexBuffer.NormalKind).isUpdatable) {
+                    serializationObject.normals._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.UVKind)) {
                 serializationObject.uvs = this.getVerticesData(VertexBuffer.UVKind);
+                if (this.getVertexBuffer(VertexBuffer.UVKind).isUpdatable) {
+                    serializationObject.uvs._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
                 serializationObject.uv2s = this.getVerticesData(VertexBuffer.UV2Kind);
+                if (this.getVertexBuffer(VertexBuffer.UV2Kind).isUpdatable) {
+                    serializationObject.uv2s._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.UV3Kind)) {
                 serializationObject.uv3s = this.getVerticesData(VertexBuffer.UV3Kind);
+                if (this.getVertexBuffer(VertexBuffer.UV3Kind).isUpdatable) {
+                    serializationObject.uv3s._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.UV4Kind)) {
                 serializationObject.uv4s = this.getVerticesData(VertexBuffer.UV4Kind);
+                if (this.getVertexBuffer(VertexBuffer.UV4Kind).isUpdatable) {
+                    serializationObject.uv4s._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.UV5Kind)) {
                 serializationObject.uv5s = this.getVerticesData(VertexBuffer.UV5Kind);
+                if (this.getVertexBuffer(VertexBuffer.UV5Kind).isUpdatable) {
+                    serializationObject.uv5s._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.UV6Kind)) {
                 serializationObject.uv6s = this.getVerticesData(VertexBuffer.UV6Kind);
+                if (this.getVertexBuffer(VertexBuffer.UV6Kind).isUpdatable) {
+                    serializationObject.uv6s._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.ColorKind)) {
                 serializationObject.colors = this.getVerticesData(VertexBuffer.ColorKind);
+                if (this.getVertexBuffer(VertexBuffer.ColorKind).isUpdatable) {
+                    serializationObject.colors._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.MatricesIndicesKind)) {
                 serializationObject.matricesIndices = this.getVerticesData(VertexBuffer.MatricesIndicesKind);
                 serializationObject.matricesIndices._isExpanded = true;
+                if (this.getVertexBuffer(VertexBuffer.MatricesIndicesKind).isUpdatable) {
+                    serializationObject.matricesIndices._updatable = true;
+                }
             }
 
             if (this.isVerticesDataPresent(VertexBuffer.MatricesWeightsKind)) {
                 serializationObject.matricesWeights = this.getVerticesData(VertexBuffer.MatricesWeightsKind);
+                if (this.getVertexBuffer(VertexBuffer.MatricesWeightsKind).isUpdatable) {
+                    serializationObject.matricesWeights._updatable = true;
+                }
             }
 
             serializationObject.indices = this.getIndices();
@@ -772,35 +807,36 @@
                     }
                 }
             } else if (parsedGeometry.positions && parsedGeometry.normals && parsedGeometry.indices) {
-                mesh.setVerticesData(VertexBuffer.PositionKind, parsedGeometry.positions, false);
-                mesh.setVerticesData(VertexBuffer.NormalKind, parsedGeometry.normals, false);
+                mesh.setVerticesData(VertexBuffer.PositionKind, parsedGeometry.positions, parsedGeometry.positions._updatable);
+
+                mesh.setVerticesData(VertexBuffer.NormalKind, parsedGeometry.normals, parsedGeometry.normals._updatable);
 
                 if (parsedGeometry.uvs) {
-                    mesh.setVerticesData(VertexBuffer.UVKind, parsedGeometry.uvs, false);
+                    mesh.setVerticesData(VertexBuffer.UVKind, parsedGeometry.uvs, parsedGeometry.uvs._updatable);
                 }
 
                 if (parsedGeometry.uvs2) {
-                    mesh.setVerticesData(VertexBuffer.UV2Kind, parsedGeometry.uvs2, false);
+                    mesh.setVerticesData(VertexBuffer.UV2Kind, parsedGeometry.uvs2, parsedGeometry.uvs2._updatable);
                 }
 
                 if (parsedGeometry.uvs3) {
-                    mesh.setVerticesData(VertexBuffer.UV3Kind, parsedGeometry.uvs3, false);
+                    mesh.setVerticesData(VertexBuffer.UV3Kind, parsedGeometry.uvs3, parsedGeometry.uvs3._updatable);
                 }
 
                 if (parsedGeometry.uvs4) {
-                    mesh.setVerticesData(VertexBuffer.UV4Kind, parsedGeometry.uvs4, false);
+                    mesh.setVerticesData(VertexBuffer.UV4Kind, parsedGeometry.uvs4, parsedGeometry.uvs4._updatable);
                 }
 
                 if (parsedGeometry.uvs5) {
-                    mesh.setVerticesData(VertexBuffer.UV5Kind, parsedGeometry.uvs5, false);
+                    mesh.setVerticesData(VertexBuffer.UV5Kind, parsedGeometry.uvs5, parsedGeometry.uvs5._updatable);
                 }
 
                 if (parsedGeometry.uvs6) {
-                    mesh.setVerticesData(VertexBuffer.UV6Kind, parsedGeometry.uvs6, false);
+                    mesh.setVerticesData(VertexBuffer.UV6Kind, parsedGeometry.uvs6, parsedGeometry.uvs6._updatable);
                 }
 
                 if (parsedGeometry.colors) {
-                    mesh.setVerticesData(VertexBuffer.ColorKind, Color4.CheckColors4(parsedGeometry.colors, parsedGeometry.positions.length / 3), false);
+                    mesh.setVerticesData(VertexBuffer.ColorKind, Color4.CheckColors4(parsedGeometry.colors, parsedGeometry.positions.length / 3), parsedGeometry.colors._updatable);
                 }
 
                 if (parsedGeometry.matricesIndices) {
@@ -816,10 +852,10 @@
                             floatIndices.push(matricesIndex >> 24);
                         }
 
-                        mesh.setVerticesData(VertexBuffer.MatricesIndicesKind, floatIndices, false);
+                        mesh.setVerticesData(VertexBuffer.MatricesIndicesKind, floatIndices, parsedGeometry.matricesIndices._updatable);
                     } else {
                         delete parsedGeometry.matricesIndices._isExpanded;
-                        mesh.setVerticesData(VertexBuffer.MatricesIndicesKind, parsedGeometry.matricesIndices, false);
+                        mesh.setVerticesData(VertexBuffer.MatricesIndicesKind, parsedGeometry.matricesIndices, parsedGeometry.matricesIndices._updatable);
                     }
                 }
 
@@ -836,19 +872,19 @@
                             floatIndices.push(matricesIndex >> 24);
                         }
 
-                        mesh.setVerticesData(VertexBuffer.MatricesIndicesExtraKind, floatIndices, false);
+                        mesh.setVerticesData(VertexBuffer.MatricesIndicesExtraKind, floatIndices, parsedGeometry.matricesIndicesExtra._updatable);
                     } else {
                         delete parsedGeometry.matricesIndices._isExpanded;
-                        mesh.setVerticesData(VertexBuffer.MatricesIndicesExtraKind, parsedGeometry.matricesIndicesExtra, false);
+                        mesh.setVerticesData(VertexBuffer.MatricesIndicesExtraKind, parsedGeometry.matricesIndicesExtra, parsedGeometry.matricesIndicesExtra._updatable);
                     }
                 }
 
                 if (parsedGeometry.matricesWeights) {
-                    mesh.setVerticesData(VertexBuffer.MatricesWeightsKind, parsedGeometry.matricesWeights, false);
+                    mesh.setVerticesData(VertexBuffer.MatricesWeightsKind, parsedGeometry.matricesWeights, parsedGeometry.matricesWeights._updatable);
                 }
 
                 if (parsedGeometry.matricesWeightsExtra) {
-                    mesh.setVerticesData(VertexBuffer.MatricesWeightsExtraKind, parsedGeometry.matricesWeightsExtra, false);
+                    mesh.setVerticesData(VertexBuffer.MatricesWeightsExtraKind, parsedGeometry.matricesWeightsExtra, parsedGeometry.matricesWeights._updatable);
                 }
 
                 mesh.setIndices(parsedGeometry.indices);

+ 25 - 23
src/Mesh/babylon.groundMesh.ts

@@ -45,14 +45,15 @@
          * Returns a height (y) value in the Worl system :
          * the ground altitude at the coordinates (x, z) expressed in the World system.
          * Returns the ground y position if (x, z) are outside the ground surface.
-         * Not pertinent if the ground is rotated.
          */
         public getHeightAtCoordinates(x: number, z: number): number {
-            // express x and y in the ground local system
-            x -= this.position.x;
-            z -= this.position.z;
-            x /= this.scaling.x;
-            z /= this.scaling.z;
+            var world = this.getWorldMatrix();
+            var invMat = Tmp.Matrix[5];
+            world.invertToRef(invMat);
+            var tmpVect = Tmp.Vector3[8];
+            Vector3.TransformCoordinatesFromFloatsToRef(x, 0.0, z, invMat, tmpVect); // transform x,z in the mesh local space
+            x = tmpVect.x;
+            z = tmpVect.z;
             if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
                 return this.position.y;
             }
@@ -63,17 +64,17 @@
             var facet = this._getFacetAt(x, z);
             var y = -(facet.x * x + facet.z * z + facet.w) / facet.y;
             // return y in the World system
-            return y * this.scaling.y + this.position.y;
+            Vector3.TransformCoordinatesFromFloatsToRef(0.0, y, 0.0, world, tmpVect);
+            return tmpVect.y;
         }
 
         /**
          * Returns a normalized vector (Vector3) orthogonal to the ground
          * at the ground coordinates (x, z) expressed in the World system.
-         * Returns Vector3(0, 1, 0) if (x, z) are outside the ground surface.
-         * Not pertinent if the ground is rotated.
+         * Returns Vector3(0.0, 1.0, 0.0) if (x, z) are outside the ground surface.
          */
         public getNormalAtCoordinates(x: number, z: number): Vector3 {
-            var normal = new Vector3(0, 1, 0);
+            var normal = new Vector3(0.0, 1.0, 0.0);
             this.getNormalAtCoordinatesToRef(x, z, normal);
             return normal;
         }
@@ -81,26 +82,27 @@
         /**
          * Updates the Vector3 passed a reference with a normalized vector orthogonal to the ground
          * at the ground coordinates (x, z) expressed in the World system.
-         * Doesn't uptade the reference Vector3 if (x, z) are outside the ground surface.
-         * Not pertinent if the ground is rotated.
+         * Doesn't uptade the reference Vector3 if (x, z) are outside the ground surface.  
+         * Returns the GroundMesh.  
          */
-        public getNormalAtCoordinatesToRef(x: number, z: number, ref: Vector3): void {
-            // express x and y in the ground local system
-            x -= this.position.x;
-            z -= this.position.z;
-            x /= this.scaling.x;
-            z /= this.scaling.z;
+        public getNormalAtCoordinatesToRef(x: number, z: number, ref: Vector3): GroundMesh {
+            var world = this.getWorldMatrix();
+            var tmpMat = Tmp.Matrix[5];
+            world.invertToRef(tmpMat);
+            var tmpVect = Tmp.Vector3[8];
+            Vector3.TransformCoordinatesFromFloatsToRef(x, 0.0, z, tmpMat, tmpVect); // transform x,z in the mesh local space
+            x = tmpVect.x;
+            z = tmpVect.z;
             if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
-                return;
+                return this;
             }
             if (!this._heightQuads || this._heightQuads.length == 0) {
                 this._initHeightQuads();
                 this._computeHeightQuads();
             }
             var facet = this._getFacetAt(x, z);
-            ref.x = facet.x;
-            ref.y = facet.y;
-            ref.z = facet.z;
+            Vector3.TransformNormalFromFloatsToRef(facet.x, facet.y, facet.z, world, ref);
+            return this;
         }
 
         /**
@@ -143,7 +145,7 @@
             this._heightQuads = new Array();
             for (var row = 0; row < subdivisionsY; row++) {
                 for (var col = 0; col < subdivisionsX; col++) {
-                    var quad = { slope: BABYLON.Vector2.Zero(), facet1: new BABYLON.Vector4(0, 0, 0, 0), facet2: new BABYLON.Vector4(0, 0, 0, 0) };
+                    var quad = { slope: BABYLON.Vector2.Zero(), facet1: new BABYLON.Vector4(0.0, 0.0, 0.0, 0.0), facet2: new BABYLON.Vector4(0.0, 0.0, 0.0, 0.0) };
                     this._heightQuads[row * subdivisionsX + col] = quad;
                 }
             }

+ 315 - 12
src/Mesh/babylon.mesh.ts

@@ -119,7 +119,54 @@
         private _areNormalsFrozen: boolean = false; // Will be used by ribbons mainly
 
         private _sourcePositions: Float32Array; // Will be used to save original positions when using software skinning
-        private _sourceNormals: Float32Array; // Will be used to save original normals when using software skinning
+        private _sourceNormals: Float32Array;   // Will be used to save original normals when using software skinning
+
+        private _facetPositions: Vector3[];             // facet local positions
+        private _facetNormals: Vector3[];               // facet local normals
+        private _facetPartitioning: number[][];           // partitioning array of facet index arrays
+        private _facetNb: number = 0;                   // facet number
+        private _partitioningSubdivisions: number = 10; // number of subdivisions per axis in the partioning space  
+        private _partitioningBBoxRatio: number = 1.01;  // the partioning array space is by default 1% bigger than the bounding box
+        private _facetDataEnabled: boolean = false;     // is the facet data feature enabled on this mesh ?
+        private _facetParameters: any = {};                  // keep a reference to the object parameters to avoid memory re-allocation
+        private _bbSize: Vector3 = Vector3.Zero();      // bbox size approximated for facet data
+        private _subDiv = {                         // actual number of subdivisions per axis for ComputeNormals()
+            max: 1,
+            X: 1,
+            Y: 1,
+            Z: 1
+        };
+        /**
+         * Read-only : the number of facets in the mesh
+         */
+        public get facetNb(): number {
+            return this._facetNb;
+        }
+        /**
+         * The number of subdivisions per axis in the partioning space
+         */
+        public get partitioningSubdivisions(): number {
+            return this._partitioningSubdivisions;
+        }
+        public set partitioningSubdivisions(nb: number) {
+            this._partitioningSubdivisions = nb;
+        } 
+        /**
+         * The ratio to apply to the bouding box size to set to the partioning space.  
+         * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box.
+         */
+        public get partitioningBBoxRatio(): number {
+            return this._partitioningBBoxRatio;
+        }
+        public set partitioningBBoxRatio(ratio: number) {
+            this._partitioningBBoxRatio = ratio;
+        }
+        /**
+         * Read-only : is the feature facetData enabled ?
+         */
+        public get isFacetDataEnabled(): boolean {
+            return this._facetDataEnabled;
+        }
 
         // Will be used to save a source mesh reference, If any
         private _source: BABYLON.Mesh = null; 
@@ -1377,6 +1424,11 @@
                 }
             }
 
+            // facet data
+            if (this._facetDataEnabled) {
+                this.disableFacetData();
+            }
+
             super.dispose(doNotRecurse);
         }
 
@@ -1388,8 +1440,10 @@
          * The parameter `url` is a string, the URL from the image file is to be downloaded.  
          * The parameters `minHeight` and `maxHeight` are the lower and upper limits of the displacement.
          * The parameter `onSuccess` is an optional Javascript function to be called just after the mesh is modified. It is passed the modified mesh and must return nothing.
+         * The parameter `uvOffset` is an optional vector2 used to offset UV.
+         * The parameter `uvScale` is an optional vector2 used to scale UV.
          */
-        public applyDisplacementMap(url: string, minHeight: number, maxHeight: number, onSuccess?: (mesh: Mesh) => void): void {
+        public applyDisplacementMap(url: string, minHeight: number, maxHeight: number, onSuccess?: (mesh: Mesh) => void, uvOffset?: Vector2, uvScale?: Vector2): void {
             var scene = this.getScene();
 
             var onload = img => {
@@ -1407,7 +1461,7 @@
                 //Cast is due to wrong definition in lib.d.ts from ts 1.3 - https://github.com/Microsoft/TypeScript/issues/949
                 var buffer = <Uint8Array>(<any>context.getImageData(0, 0, heightMapWidth, heightMapHeight).data);
 
-                this.applyDisplacementMapFromBuffer(buffer, heightMapWidth, heightMapHeight, minHeight, maxHeight);
+                this.applyDisplacementMapFromBuffer(buffer, heightMapWidth, heightMapHeight, minHeight, maxHeight, uvOffset, uvScale);
                 //execute success callback, if set
                 if (onSuccess) {
                     onSuccess(this);
@@ -1425,8 +1479,10 @@
          * The parameter `buffer` is a `Uint8Array` buffer containing series of `Uint8` lower than 255, the red, green, blue and alpha values of each successive pixel.
          * The parameters `heightMapWidth` and `heightMapHeight` are positive integers to set the width and height of the buffer image.     
          * The parameters `minHeight` and `maxHeight` are the lower and upper limits of the displacement.
+         * The parameter `uvOffset` is an optional vector2 used to offset UV.
+         * The parameter `uvScale` is an optional vector2 used to scale UV.
          */
-        public applyDisplacementMapFromBuffer(buffer: Uint8Array, heightMapWidth: number, heightMapHeight: number, minHeight: number, maxHeight: number): void {
+        public applyDisplacementMapFromBuffer(buffer: Uint8Array, heightMapWidth: number, heightMapHeight: number, minHeight: number, maxHeight: number, uvOffset?: Vector2, uvScale?: Vector2): void {
             if (!this.isVerticesDataPresent(VertexBuffer.PositionKind)
                 || !this.isVerticesDataPresent(VertexBuffer.NormalKind)
                 || !this.isVerticesDataPresent(VertexBuffer.UVKind)) {
@@ -1441,14 +1497,17 @@
             var normal = Vector3.Zero();
             var uv = Vector2.Zero();
 
+            uvOffset = uvOffset || Vector2.Zero();
+            uvScale = uvScale || new Vector2(1, 1);
+
             for (var index = 0; index < positions.length; index += 3) {
                 Vector3.FromArrayToRef(positions, index, position);
                 Vector3.FromArrayToRef(normals, index, normal);
                 Vector2.FromArrayToRef(uvs, (index / 3) * 2, uv);
 
                 // Compute height
-                var u = ((Math.abs(uv.x) * heightMapWidth) % heightMapWidth) | 0;
-                var v = ((Math.abs(uv.y) * heightMapHeight) % heightMapHeight) | 0;
+                var u = ((Math.abs(uv.x * uvScale.x + uvOffset.x) * heightMapWidth) % heightMapWidth) | 0;
+                var v = ((Math.abs(uv.y * uvScale.y + uvOffset.y) * heightMapHeight) % heightMapHeight) | 0;
 
                 var pos = (u + v * heightMapWidth) * 4;
                 var r = buffer[pos] / 255.0;
@@ -1715,10 +1774,10 @@
         }
 
         /**
-         * Optimization of the mesh's indices, in case a mesh has duplicated vertices.
-         * The function will only reorder the indices and will not remove unused vertices to avoid problems with submeshes.
-         * This should be used together with the simplification to avoid disappearing triangles.
-         * @param successCallback an optional success callback to be called after the optimization finished.
+         * Optimization of the mesh's indices, in case a mesh has duplicated vertices.   
+         * The function will only reorder the indices and will not remove unused vertices to avoid problems with submeshes.   
+         * This should be used together with the simplification to avoid disappearing triangles.   
+         * @param successCallback an optional success callback to be called after the optimization finished.   
          */
         public optimizeIndices(successCallback?: (mesh?: Mesh) => void) {
             var indices = this.getIndices();
@@ -1754,10 +1813,254 @@
             });
         }
 
+        // Facet data
+        /** 
+         *  Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning
+         */
+        private _initFacetData(): Mesh {
+            if (!this._facetNormals) {
+                this._facetNormals = new Array<Vector3>();
+            }
+            if (!this._facetPositions) {
+                this._facetPositions = new Array<Vector3>();
+            }
+            if (!this._facetPartitioning) {
+                this._facetPartitioning = new Array<number[]>();
+            }
+            this._facetNb = this.getIndices().length / 3;
+            this._partitioningSubdivisions = (this._partitioningSubdivisions) ? this._partitioningSubdivisions : 10;   // default nb of partitioning subdivisions = 10
+            this._partitioningBBoxRatio = (this._partitioningBBoxRatio) ? this._partitioningBBoxRatio : 1.01;          // default ratio 1.01 = the partitioning is 1% bigger than the bounding box
+            for (var f = 0; f < this._facetNb; f++) {
+                this._facetNormals[f] = Vector3.Zero();
+                this._facetPositions[f] = Vector3.Zero();
+            }
+            this._facetDataEnabled = true;           
+            return this;
+        }
+
+        /**
+         * Updates the mesh facetData arrays and the internal partitioning when the mesh is morphed or updated.  
+         * This method can be called within the render loop.  
+         * You don't need to call this method by yourself in the render loop when you update/morph a mesh with the methods CreateXXX() as they automatically manage this computation.  
+         */
+        public updateFacetData(): Mesh {
+            if (!this._facetDataEnabled) {
+                this._initFacetData();
+            }
+            var positions = this.getVerticesData(VertexBuffer.PositionKind);
+            var indices = this.getIndices();
+            var normals = this.getVerticesData(VertexBuffer.NormalKind);
+            var bInfo = this.getBoundingInfo();
+            this._bbSize.x = (bInfo.maximum.x - bInfo.minimum.x > Epsilon) ? bInfo.maximum.x - bInfo.minimum.x : Epsilon;
+            this._bbSize.y = (bInfo.maximum.y - bInfo.minimum.y > Epsilon) ? bInfo.maximum.y - bInfo.minimum.y : Epsilon;
+            this._bbSize.z = (bInfo.maximum.z - bInfo.minimum.z > Epsilon) ? bInfo.maximum.z - bInfo.minimum.z : Epsilon;
+            var bbSizeMax = (this._bbSize.x > this._bbSize.y) ? this._bbSize.x : this._bbSize.y;
+            bbSizeMax = (bbSizeMax > this._bbSize.z) ? bbSizeMax : this._bbSize.z;
+            this._subDiv.max = this._partitioningSubdivisions;
+            this._subDiv.X = Math.floor(this._subDiv.max * this._bbSize.x / bbSizeMax);   // adjust the number of subdivisions per axis
+            this._subDiv.Y = Math.floor(this._subDiv.max * this._bbSize.y / bbSizeMax);   // according to each bbox size per axis
+            this._subDiv.Z = Math.floor(this._subDiv.max * this._bbSize.z / bbSizeMax);
+            this._subDiv.X = this._subDiv.X < 1 ? 1 : this._subDiv.X;                     // at least one subdivision
+            this._subDiv.Y = this._subDiv.Y < 1 ? 1 : this._subDiv.Y;
+            this._subDiv.Z = this._subDiv.Z < 1 ? 1 : this._subDiv.Z;
+            // set the parameters for ComputeNormals()
+            this._facetParameters.facetNormals = this.getFacetLocalNormals(); 
+            this._facetParameters.facetPositions = this.getFacetLocalPositions();
+            this._facetParameters.facetPartitioning = this.getFacetLocalPartitioning();
+            this._facetParameters.bInfo = bInfo;
+            this._facetParameters.bbSize = this._bbSize;
+            this._facetParameters.subDiv = this._subDiv;
+            this._facetParameters.ratio = this.partitioningBBoxRatio;
+            VertexData.ComputeNormals(positions, indices, normals, this._facetParameters);
+            return this;
+        }
+        /**
+         * Returns the facetLocalNormals array.  
+         * The normals are expressed in the mesh local space.  
+         */
+        public getFacetLocalNormals(): Vector3[] {
+            if (!this._facetNormals) {
+                this.updateFacetData();
+            }
+            return this._facetNormals;
+        }
+        /**
+         * Returns the facetLocalPositions array.  
+         * The facet positions are expressed in the mesh local space.  
+         */
+        public getFacetLocalPositions(): Vector3[] {
+            if (!this._facetPositions) {
+                this.updateFacetData();
+            }
+            return this._facetPositions;           
+        }
+        /**
+         * Returns the facetLocalPartioning array
+         */
+        public getFacetLocalPartitioning(): number[][] {
+            if (!this._facetPartitioning) {
+                this.updateFacetData();
+            }
+            return this._facetPartitioning;
+        }
+        /**
+         * Returns the i-th facet position in the world system.  
+         * This method allocates a new Vector3 per call.  
+         */
+        public getFacetPosition(i: number): Vector3 {
+            var pos = Vector3.Zero();
+            this.getFacetPositionToRef(i, pos);
+            return pos;
+        }
+        /**
+         * Sets the reference Vector3 with the i-th facet position in the world system.  
+         * Returns the mesh.  
+         */
+        public getFacetPositionToRef(i: number, ref: Vector3): Mesh {
+            var localPos = (this.getFacetLocalPositions())[i];
+            var world = this.getWorldMatrix();
+            Vector3.TransformCoordinatesToRef(localPos, world, ref);
+            return this;
+        }
+        /**
+         * Returns the i-th facet normal in the world system.  
+         * This method allocates a new Vector3 per call.  
+         */
+        public getFacetNormal(i: number): Vector3 {
+            var norm = Vector3.Zero();
+            this.getFacetNormalToRef(i, norm);
+            return norm;
+        }
+        /**
+         * Sets the reference Vector3 with the i-th facet normal in the world system.  
+         * Returns the mesh.  
+         */
+        public getFacetNormalToRef(i: number, ref: Vector3) {
+            var localNorm = (this.getFacetLocalNormals())[i];
+            Vector3.TransformNormalToRef(localNorm, this.getWorldMatrix(), ref);
+            return this;
+        }
+        /** 
+         * Returns the facets (in an array) in the same partitioning block than the one the passed coordinates are located (expressed in the mesh local system).
+         */
+        public getFacetsAtLocalCoordinates(x: number, y: number, z: number): number[] {
+            var bInfo = this.getBoundingInfo();
+            var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) * this._subDiv.X * this._partitioningBBoxRatio / this._bbSize.x);
+            var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) * this._subDiv.Y * this._partitioningBBoxRatio / this._bbSize.y);
+            var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) * this._subDiv.Z * this._partitioningBBoxRatio / this._bbSize.z);
+            if (ox < 0 || ox > this._subDiv.max || oy < 0 || oy > this._subDiv.max || oz < 0 || oz > this._subDiv.max) {
+                return null;
+            }
+            return this._facetPartitioning[ox + this._subDiv.max * oy + this._subDiv.max * this._subDiv.max * oz];
+        }
+        /** 
+         * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found.  
+         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) World projection on the facet.  
+         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
+         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
+         * If facing si false and checkFace is true, only the facet "turning their backs" to (x, y, z) are returned : negative dot (x, y, z) * facet position. 
+         */
+        public getClosestFacetAtCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): number {
+            var world = this.getWorldMatrix();
+            var invMat = Tmp.Matrix[5];
+            world.invertToRef(invMat);
+            var invVect = Tmp.Vector3[8];
+            var closest = null;
+            Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect);  // transform (x,y,z) to coordinates in the mesh local space
+            closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, checkFace, facing);
+            if (projected) {
+                // tranform the local computed projected vector to world coordinates
+                Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
+            }
+            return closest;
+        }
+        /** 
+         * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found.   
+         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) local projection on the facet.  
+         * If checkFace is true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned.
+         * If facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position.
+         * If facing si false and checkFace is true, only the facet "turning their backs"  to (x, y, z) are returned : negative dot (x, y, z) * facet position.
+         */
+        public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true) {
+            var closest = null;
+            var tmpx = 0.0;         
+            var tmpy = 0.0;
+            var tmpz = 0.0;
+            var d = 0.0;            // tmp dot facet normal * facet position
+            var t0 = 0.0;
+            var projx = 0.0;
+            var projy = 0.0;
+            var projz = 0.0;
+            // Get all the facets in the same partitioning block than (x, y, z)
+            var facetPositions = this.getFacetLocalPositions();
+            var facetNormals = this.getFacetLocalNormals();
+            var facetsInBlock = this.getFacetsAtLocalCoordinates(x, y, z);
+            if (!facetsInBlock) {
+                return null;
+            }
+            // Get the closest facet to (x, y, z)
+            var shortest = Number.MAX_VALUE;            // init distance vars
+            var tmpDistance = shortest;
+            var fib;                                    // current facet in the block
+            var norm;                                   // current facet normal
+            var p0;                                     // current facet barycenter position
+            // loop on all the facets in the current partitioning block
+            for (var idx = 0; idx < facetsInBlock.length; idx++) {
+                fib = facetsInBlock[idx];           
+                norm = facetNormals[fib];
+                p0 = facetPositions[fib];
+
+                d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
+                if ( !checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0) ) {
+                    // compute (x,y,z) projection on the facet = (projx, projy, projz)
+                    d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z; 
+                    t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
+                    projx = x + norm.x * t0;
+                    projy = y + norm.y * t0;
+                    projz = z + norm.z * t0;
+
+                    tmpx = projx - x;
+                    tmpy = projy - y;
+                    tmpz = projz - z;
+                    tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz;             // compute length between (x, y, z) and its projection on the facet
+                    if (tmpDistance < shortest) {                                      // just keep the closest facet to (x, y, z)
+                        shortest = tmpDistance;
+                        closest = fib; 
+                        if (projected) {
+                            projected.x = projx;
+                            projected.y = projy;
+                            projected.z = projz;
+                        }
+                    }
+                }
+            }
+            return closest;
+        }
+        /**
+         * Returns the object "parameter" set with all the expected parameters for facetData computation by ComputeNormals()  
+         */
+        public getFacetDataParameters(): any {
+            return this._facetParameters;
+        }
+        /** 
+         * Disables the feature FacetData and frees the related memory.  
+         * Returns the mesh.  
+         */
+        public disableFacetData(): Mesh {
+            if (this._facetDataEnabled) {
+                this._facetDataEnabled = false;
+                this._facetPositions = null;
+                this._facetNormals = null;
+                this._facetPartitioning = null;
+                this._facetParameters = null;
+            }
+            return this;
+        }
+
         // Statics
         /**
-         * Returns a new Mesh object what is a deep copy of the passed mesh. 
-         * The parameter `parsedMesh` is the mesh to be copied.
+         * 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
          */
         public static Parse(parsedMesh: any, scene: Scene, rootUrl: string): Mesh {

+ 159 - 47
src/Mesh/babylon.mesh.vertexData.ts

@@ -538,8 +538,8 @@
             var v: number;
             for (p = 0; p < pathArray.length; p++) {
                 for (i = 0; i < minlg + closePathCorr; i++) {
-                    u = us[p][i] / uTotalDistance[p];
-                    v = vs[i][p] / vTotalDistance[i];
+                    u = (uTotalDistance[p] != 0.0) ? us[p][i] / uTotalDistance[p] : 0.0;
+                    v = (vTotalDistance[i] != 0.0) ? vs[i][p] / vTotalDistance[i] : 0.0;
                     if (invertUV) {
                         uvs.push(v, u);
                     } else {
@@ -1199,7 +1199,7 @@
 
                     positions.push(position.x, position.y, position.z);
                     normals.push(normal.x, normal.y, normal.z);
-                    uvs.push(col / subdivisionsX, 1.0 - row / subdivisionsX);
+                    uvs.push(col / subdivisionsX, 1.0 - row / subdivisionsY);
                 }
             }
 
@@ -1961,66 +1961,178 @@
          * @param {any} - positions (number[] or Float32Array)
          * @param {any} - indices   (number[] or Uint16Array)
          * @param {any} - normals   (number[] or Float32Array)
+         * options (optional) :
+         * facetPositions : optional array of facet positions (vector3)
+         * facetNormals : optional array of facet normals (vector3)
+         * facetPartitioning : optional partitioning array. facetPositions is required for facetPartitioning computation
+         * subDiv : optional partitioning data about subdivsions on  each axis (int), required for facetPartitioning computation
+         * ratio : optional partitioning ratio / bounding box, required for facetPartitioning computation
+         * bbSize : optional bounding box size data, required for facetPartitioning computation
+         * bInfo : optional bounding info, required for facetPartitioning computation
          */
-        public static ComputeNormals(positions: any, indices: any, normals: any) {
-            var index = 0;
-
-            var p1p2x = 0.0;
-            var p1p2y = 0.0;
-            var p1p2z = 0.0;
-            var p3p2x = 0.0;
-            var p3p2y = 0.0;
-            var p3p2z = 0.0;
-            var faceNormalx = 0.0;
-            var faceNormaly = 0.0;
-            var faceNormalz = 0.0;
-
-            var length = 0.0;
-
-            var i1 = 0;
-            var i2 = 0;
-            var i3 = 0;
-
+        public static ComputeNormals(positions: any, indices: any, normals: any, 
+            options?: { facetNormals?: any, facetPositions?: any, facetPartitioning?: any, ratio?: number, bInfo?: any, bbSize?: Vector3, subDiv?: any}): void {
+
+            // temporary scalar variables
+            var index = 0;                      // facet index     
+            var p1p2x = 0.0;                    // p1p2 vector x coordinate
+            var p1p2y = 0.0;                    // p1p2 vector y coordinate
+            var p1p2z = 0.0;                    // p1p2 vector z coordinate
+            var p3p2x = 0.0;                    // p3p2 vector x coordinate
+            var p3p2y = 0.0;                    // p3p2 vector y coordinate
+            var p3p2z = 0.0;                    // p3p2 vector z coordinate
+            var faceNormalx = 0.0;              // facet normal x coordinate
+            var faceNormaly = 0.0;              // facet normal y coordinate
+            var faceNormalz = 0.0;              // facet normal z coordinate
+            var length = 0.0;                   // facet normal length before normalization
+            var v1x = 0;                        // vector1 x index in the positions array
+            var v1y = 0;                        // vector1 y index in the positions array
+            var v1z = 0;                        // vector1 z index in the positions array
+            var v2x = 0;                        // vector2 x index in the positions array
+            var v2y = 0;                        // vector2 y index in the positions array
+            var v2z = 0;                        // vector2 z index in the positions array
+            var v3x = 0;                        // vector3 x index in the positions array
+            var v3y = 0;                        // vector3 y index in the positions array
+            var v3z = 0;                        // vector3 z index in the positions array
+            var computeFacetNormals = false;
+            var computeFacetPositions = false;
+            var computeFacetPartitioning = false;
+            if (options) {
+                computeFacetNormals = (options.facetNormals) ? true : false;
+                computeFacetPositions = (options.facetPositions) ? true : false;
+                computeFacetPartitioning = (options.facetPartitioning) ? true : false;
+            }
+
+            // facetPartitioning reinit if needed
+            if (computeFacetPartitioning) {  
+                var ox = 0;                 // X partitioning index for facet position
+                var oy = 0;                 // Y partinioning index for facet position
+                var oz = 0;                 // Z partinioning index for facet position
+                var b1x = 0;                // X partitioning index for facet v1 vertex
+                var b1y = 0;                // Y partitioning index for facet v1 vertex
+                var b1z = 0;                // z partitioning index for facet v1 vertex
+                var b2x = 0;                // X partitioning index for facet v2 vertex
+                var b2y = 0;                // Y partitioning index for facet v2 vertex
+                var b2z = 0;                // Z partitioning index for facet v2 vertex
+                var b3x = 0;                // X partitioning index for facet v3 vertex
+                var b3y = 0;                // Y partitioning index for facet v3 vertex
+                var b3z = 0;                // Z partitioning index for facet v3 vertex
+                var block_idx_o = 0;        // facet barycenter block index
+                var block_idx_v1 = 0;       // v1 vertex block index
+                var block_idx_v2 = 0;       // v2 vertex block index
+                var block_idx_v3 = 0;       // v3 vertex block index  
+
+                var bbSizeMax = (options.bbSize.x > options.bbSize.y) ? options.bbSize.x : options.bbSize.y;
+                bbSizeMax = (bbSizeMax > options.bbSize.z) ? bbSizeMax : options.bbSize.z;
+                var xSubRatio = options.subDiv.X * options.ratio / options.bbSize.x;
+                var ySubRatio = options.subDiv.Y * options.ratio / options.bbSize.y;
+                var zSubRatio = options.subDiv.Z * options.ratio / options.bbSize.z;
+                var subSq = options.subDiv.max * options.subDiv.max;
+                options.facetPartitioning.length = 0;
+            }
+        
+            // reset the normals
             for (index = 0; index < positions.length; index++) {
                 normals[index] = 0.0;
             }
 
-            // indice triplet = 1 face
+            // Loop : 1 indice triplet = 1 facet
             var nbFaces = indices.length / 3;
             for (index = 0; index < nbFaces; index++) {
-                i1 = indices[index * 3];            // get the indexes of each vertex of the face
-                i2 = indices[index * 3 + 1];
-                i3 = indices[index * 3 + 2];
 
-                p1p2x = positions[i1 * 3] - positions[i2 * 3];          // compute two vectors per face
-                p1p2y = positions[i1 * 3 + 1] - positions[i2 * 3 + 1];
-                p1p2z = positions[i1 * 3 + 2] - positions[i2 * 3 + 2];
-
-                p3p2x = positions[i3 * 3] - positions[i2 * 3];
-                p3p2y = positions[i3 * 3 + 1] - positions[i2 * 3 + 1];
-                p3p2z = positions[i3 * 3 + 2] - positions[i2 * 3 + 2];
-
-                faceNormalx = p1p2y * p3p2z - p1p2z * p3p2y;            // compute the face normal with cross product
+                // get the indexes of the coordinates of each vertex of the facet
+                v1x = indices[index * 3] * 3;
+                v1y = v1x + 1;
+                v1z = v1x + 2;
+                v2x = indices[index * 3 + 1] * 3;
+                v2y = v2x + 1;
+                v2z = v2x + 2;
+                v3x = indices[index * 3 + 2] * 3;
+                v3y = v3x + 1;
+                v3z = v3x + 2;        
+
+                p1p2x = positions[v1x] - positions[v2x];          // compute two vectors per facet : p1p2 and p3p2
+                p1p2y = positions[v1y] - positions[v2y];
+                p1p2z = positions[v1z] - positions[v2z];
+
+                p3p2x = positions[v3x] - positions[v2x];
+                p3p2y = positions[v3y] - positions[v2y];
+                p3p2z = positions[v3z] - positions[v2z];
+
+                // compute the face normal with the cross product
+                faceNormalx = p1p2y * p3p2z - p1p2z * p3p2y;            
                 faceNormaly = p1p2z * p3p2x - p1p2x * p3p2z;
                 faceNormalz = p1p2x * p3p2y - p1p2y * p3p2x;
-
+                // normalize this normal and store it in the array facetData
                 length = Math.sqrt(faceNormalx * faceNormalx + faceNormaly * faceNormaly + faceNormalz * faceNormalz);
                 length = (length === 0) ? 1.0 : length;
-                faceNormalx /= length;                                  // normalize this normal
+                faceNormalx /= length;
                 faceNormaly /= length;
                 faceNormalz /= length;
 
-                normals[i1 * 3] += faceNormalx;                         // accumulate all the normals per face
-                normals[i1 * 3 + 1] += faceNormaly;
-                normals[i1 * 3 + 2] += faceNormalz;
-                normals[i2 * 3] += faceNormalx;
-                normals[i2 * 3 + 1] += faceNormaly;
-                normals[i2 * 3 + 2] += faceNormalz;
-                normals[i3 * 3] += faceNormalx;
-                normals[i3 * 3 + 1] += faceNormaly;
-                normals[i3 * 3 + 2] += faceNormalz;
-            }
+                if (computeFacetNormals) {
+                    options.facetNormals[index].x = faceNormalx;                                  
+                    options.facetNormals[index].y = faceNormaly;
+                    options.facetNormals[index].z = faceNormalz;
+                }
+
+                if (computeFacetPositions) {
+                    // compute and the facet barycenter coordinates in the array facetPositions 
+                    options.facetPositions[index].x = (positions[v1x] + positions[v2x] + positions[v3x]) / 3.0;
+                    options.facetPositions[index].y = (positions[v1y] + positions[v2y] + positions[v3y]) / 3.0;
+                    options.facetPositions[index].z = (positions[v1z] + positions[v2z] + positions[v3z]) / 3.0;
+                }
 
+                if (computeFacetPartitioning) {
+                    // store the facet indexes in arrays in the main facetPartitioning array :
+                    // compute each facet vertex (+ facet barycenter) index in the partiniong array
+                    ox = Math.floor((options.facetPositions[index].x - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    oy = Math.floor((options.facetPositions[index].y - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    oz = Math.floor((options.facetPositions[index].z - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    b1x = Math.floor((positions[v1x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    b1y = Math.floor((positions[v1y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    b1z = Math.floor((positions[v1z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    b2x = Math.floor((positions[v2x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    b2y = Math.floor((positions[v2y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    b2z = Math.floor((positions[v2z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    b3x = Math.floor((positions[v3x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    b3y = Math.floor((positions[v3y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    b3z = Math.floor((positions[v3z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    
+                    block_idx_v1 = b1x + options.subDiv.max * b1y + subSq * b1z;
+                    block_idx_v2 = b2x + options.subDiv.max * b2y + subSq * b2z;
+                    block_idx_v3 = b3x + options.subDiv.max * b3y + subSq * b3z;
+                    block_idx_o = ox + options.subDiv.max * oy + subSq * oz;
+
+                    options.facetPartitioning[block_idx_o] = options.facetPartitioning[block_idx_o] ? options.facetPartitioning[block_idx_o] :new Array();
+                    options.facetPartitioning[block_idx_v1] = options.facetPartitioning[block_idx_v1] ? options.facetPartitioning[block_idx_v1] :new Array();
+                    options.facetPartitioning[block_idx_v2] = options.facetPartitioning[block_idx_v2] ? options.facetPartitioning[block_idx_v2] :new Array();
+                    options.facetPartitioning[block_idx_v3] = options.facetPartitioning[block_idx_v3] ? options.facetPartitioning[block_idx_v3] :new Array();
+
+                    // push each facet index in each block containing the vertex
+                    options.facetPartitioning[block_idx_v1].push(index);
+                    if (block_idx_v2 != block_idx_v1) {
+                        options.facetPartitioning[block_idx_v2].push(index);
+                    }
+                    if (!(block_idx_v3 == block_idx_v2 || block_idx_v3 == block_idx_v1)) {
+                        options.facetPartitioning[block_idx_v3].push(index);
+                    }
+                    if (!(block_idx_o == block_idx_v1 || block_idx_o == block_idx_v2 || block_idx_o == block_idx_v3)) {
+                        options.facetPartitioning[block_idx_o].push(index); 
+                    }
+                }
+
+                // compute the normals anyway
+                normals[v1x] += faceNormalx;                         // accumulate all the normals per face
+                normals[v1y] += faceNormaly;
+                normals[v1z] += faceNormalz;
+                normals[v2x] += faceNormalx;
+                normals[v2y] += faceNormaly;
+                normals[v2z] += faceNormalz;
+                normals[v3x] += faceNormalx;
+                normals[v3y] += faceNormaly;
+                normals[v3z] += faceNormalz;
+            }
             // last normalization of each normal
             for (index = 0; index < normals.length / 3; index++) {
                 faceNormalx = normals[index * 3];

+ 24 - 21
src/Mesh/babylon.meshBuilder.ts

@@ -26,7 +26,7 @@
         public static CreateBox(name: string, options: { size?: number, width?: number, height?: number, depth?: number, faceUV?: Vector4[], faceColors?: Color4[], sideOrientation?: number, updatable?: boolean }, scene: Scene): Mesh {
             var box = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             box.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateBox(options);
@@ -51,7 +51,7 @@
         public static CreateSphere(name: string, options: { segments?: number, diameter?: number, diameterX?: number, diameterY?: number, diameterZ?: number, arc?: number, slice?: number, sideOrientation?: number, updatable?: boolean }, scene: any): Mesh {
             var sphere = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             sphere.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateSphere(options);
@@ -74,7 +74,7 @@
         public static CreateDisc(name: string, options: { radius?: number, tessellation?: number, arc?: number, updatable?: boolean, sideOrientation?: number }, scene: Scene): Mesh {
             var disc = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             disc.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateDisc(options);
@@ -98,7 +98,7 @@
         public static CreateIcoSphere(name: string, options: { radius?: number, radiusX?: number, radiusY?: number, radiusZ?: number, flat?: boolean, subdivisions?: number, sideOrientation?: number, updatable?: boolean }, scene: Scene): Mesh {
             var sphere = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             sphere.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateIcoSphere(options);
@@ -129,7 +129,7 @@
             var closeArray = options.closeArray;
             var closePath = options.closePath;
             var offset = options.offset;
-            var sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            var sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             var instance = options.instance;
             var updatable = options.updatable;
 
@@ -187,10 +187,11 @@
                 instance._boundingInfo = new BoundingInfo(Tmp.Vector3[0], Tmp.Vector3[1]);
                 instance._boundingInfo.update(instance._worldMatrix);
                 instance.updateVerticesData(VertexBuffer.PositionKind, positions, false, false);
-                if (!(instance.areNormalsFrozen)) {
+                if (!instance.areNormalsFrozen || instance.isFacetDataEnabled) {
                     var indices = instance.getIndices();
                     var normals = instance.getVerticesData(VertexBuffer.NormalKind);
-                    VertexData.ComputeNormals(positions, indices, normals);
+                    var params = instance.isFacetDataEnabled ? instance.getFacetDataParameters() : null;
+                    VertexData.ComputeNormals(positions, indices, normals, params);
 
                     if ((<any>instance)._closePath) {
                         var indexFirst: number = 0;
@@ -211,8 +212,9 @@
                             normals[indexLast + 2] = normals[indexFirst + 2];
                         }
                     }
-
-                    instance.updateVerticesData(VertexBuffer.NormalKind, normals, false, false);
+                    if (!(instance.areNormalsFrozen)) {
+                        instance.updateVerticesData(VertexBuffer.NormalKind, normals, false, false);
+                    }
                 }
 
                 return instance;
@@ -261,7 +263,7 @@
         public static CreateCylinder(name: string, options: { height?: number, diameterTop?: number, diameterBottom?: number, diameter?: number, tessellation?: number, subdivisions?: number, arc?: number, faceColors?: Color4[], faceUV?: Vector4[], updatable?: boolean, hasRings?: boolean, enclose?: boolean, sideOrientation?: number }, scene: any): Mesh {
             var cylinder = new Mesh(name, scene);
             
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             cylinder.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateCylinder(options);
@@ -284,7 +286,7 @@
         public static CreateTorus(name: string, options: { diameter?: number, thickness?: number, tessellation?: number, updatable?: boolean, sideOrientation?: number }, scene: any): Mesh {
             var torus = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             torus.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateTorus(options);
@@ -308,7 +310,7 @@
         public static CreateTorusKnot(name: string, options: { radius?: number, tube?: number, radialSegments?: number, tubularSegments?: number, p?: number, q?: number, updatable?: boolean, sideOrientation?: number }, scene: any): Mesh {
             var torusKnot = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             torusKnot.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreateTorusKnot(options);
@@ -473,7 +475,7 @@
             var rotation = options.rotation || 0;
             var cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
             var updatable = options.updatable;
-            var sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            var sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             var instance = options.instance;
             var invertUV = options.invertUV || false;
 
@@ -524,7 +526,7 @@
             var ribbonClosePath = options.ribbonClosePath || false;
             var cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
             var updatable = options.updatable;
-            var sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            var sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             var instance = options.instance;
             var invertUV = options.invertUV || false;
             return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, instance, invertUV);
@@ -554,7 +556,7 @@
             var radius = options.radius || 1;
             var tessellation = options.tessellation || 64;
             var updatable = options.updatable;
-            var sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            var sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             var cap = options.cap || Mesh.NO_CAP;
             var pi2 = Math.PI * 2;
             var paths = new Array();
@@ -600,7 +602,7 @@
         public static CreatePlane(name: string, options: { size?: number, width?: number, height?: number, sideOrientation?: number, updatable?: boolean, sourcePlane?: Plane }, scene: Scene): Mesh {
             var plane = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             plane.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreatePlane(options);
@@ -774,7 +776,7 @@
             var cap = options.cap || Mesh.NO_CAP;
             var invertUV = options.invertUV || false;
             var updatable = options.updatable;
-            var sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            var sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             var instance = options.instance;
             options.arc = (options.arc <= 0.0 || options.arc > 1.0) ? 1.0 : options.arc || 1.0;
 
@@ -887,7 +889,7 @@
         public static CreatePolyhedron(name: string, options: { type?: number, size?: number, sizeX?: number, sizeY?: number, sizeZ?: number, custom?: any, faceUV?: Vector4[], faceColors?: Color4[], flat?: boolean, updatable?: boolean, sideOrientation?: number }, scene: Scene): Mesh {
             var polyhedron = new Mesh(name, scene);
 
-            options.sideOrientation = this.updateSideOrientation(options.sideOrientation, scene);
+            options.sideOrientation = MeshBuilder.updateSideOrientation(options.sideOrientation, scene);
             polyhedron.sideOrientation = options.sideOrientation;
             
             var vertexData = VertexData.CreatePolyhedron(options);
@@ -952,6 +954,7 @@
 
                 // Get normal
                 result.normal = new Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
+                result.normal = Vector3.TransformNormal(result.normal, transformMatrix);
 
                 return result;
             }; // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
@@ -1152,7 +1155,7 @@
                     for (i = 0; i < shapePath.length; i++) {
                         barycenter.addInPlace(shapePath[i]);
                     }
-                    barycenter.scaleInPlace(1 / shapePath.length);
+                    barycenter.scaleInPlace(1.0 / shapePath.length);
                     for (i = 0; i < shapePath.length; i++) {
                         pointCap.push(barycenter);
                     }
@@ -1163,7 +1166,7 @@
                         break;
                     case Mesh.CAP_START:
                         shapePaths[0] = capPath(shapePaths[2]);
-                        shapePaths[1] = shapePaths[2].slice(0);
+                        shapePaths[1] = shapePaths[2];
                         break;
                     case Mesh.CAP_END:
                         shapePaths[index] = shapePaths[index - 1];
@@ -1171,7 +1174,7 @@
                         break;
                     case Mesh.CAP_ALL:
                         shapePaths[0] = capPath(shapePaths[2]);
-                        shapePaths[1] = shapePaths[2].slice(0);
+                        shapePaths[1] = shapePaths[2];
                         shapePaths[index] = shapePaths[index - 1];
                         shapePaths[index + 1] = capPath(shapePaths[index - 1]);
                         break;

+ 23 - 17
src/Particles/babylon.solidParticleSystem.ts

@@ -562,13 +562,17 @@
             }
 
             Matrix.IdentityToRef(this._rotMatrix);
-            var idx = 0;
-            var index = 0;
-            var colidx = 0;
-            var colorIndex = 0;
-            var uvidx = 0;
-            var uvIndex = 0;
-            var pt = 0;
+            var idx = 0;            // current position index in the global array positions32
+            var index = 0;          // position start index in the global array positions32 of the current particle
+            var colidx = 0;         // current color index in the global array colors32
+            var colorIndex = 0;     // color start index in the global array colors32 of the current particle
+            var uvidx = 0;          // current uv index in the global array uvs32
+            var uvIndex = 0;        // uv start index in the global array uvs32 of the current particle
+            var pt = 0;             // current index in the particle model shape
+
+            if (this.mesh.isFacetDataEnabled) {
+                this._computeBoundingBox = true;
+            }
 
             if (this._computeBoundingBox) {
                 Vector3.FromFloatsToRef(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, this._minimum);
@@ -653,20 +657,19 @@
                             }
                         }
 
-                        // normals : if the particles can't be morphed then just rotate the normals, what if much more faster than ComputeNormals()
+                        // normals : if the particles can't be morphed then just rotate the normals, what is much more faster than ComputeNormals()
                         if (!this._computeParticleVertex) {
                             this._normal.x = this._fixedNormal32[idx];
                             this._normal.y = this._fixedNormal32[idx + 1];
                             this._normal.z = this._fixedNormal32[idx + 2];
 
-                            this._w = (this._normal.x * this._rotMatrix.m[3]) + (this._normal.y * this._rotMatrix.m[7]) + (this._normal.z * this._rotMatrix.m[11]) + this._rotMatrix.m[15];
-                            this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]) / this._w;
-                            this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]) / this._w;
-                            this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]) / this._w;
+                            this._rotated.x = ((this._normal.x * this._rotMatrix.m[0]) + (this._normal.y * this._rotMatrix.m[4]) + (this._normal.z * this._rotMatrix.m[8]) + this._rotMatrix.m[12]);
+                            this._rotated.y = ((this._normal.x * this._rotMatrix.m[1]) + (this._normal.y * this._rotMatrix.m[5]) + (this._normal.z * this._rotMatrix.m[9]) + this._rotMatrix.m[13]);
+                            this._rotated.z = ((this._normal.x * this._rotMatrix.m[2]) + (this._normal.y * this._rotMatrix.m[6]) + (this._normal.z * this._rotMatrix.m[10]) + this._rotMatrix.m[14]);
 
                             this._normals32[idx] = this._cam_axisX.x * this._rotated.x + this._cam_axisY.x * this._rotated.y + this._cam_axisZ.x * this._rotated.z;
                             this._normals32[idx + 1] = this._cam_axisX.y * this._rotated.x + this._cam_axisY.y * this._rotated.y + this._cam_axisZ.y * this._rotated.z;
-                            this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;
+                            this._normals32[idx + 2] = this._cam_axisX.z * this._rotated.x + this._cam_axisY.z * this._rotated.y + this._cam_axisZ.z * this._rotated.z;                          
                         }
 
                         if (this._computeParticleColor) {
@@ -757,15 +760,18 @@
                     this.mesh.updateVerticesData(VertexBuffer.UVKind, this._uvs32, false, false);
                 }
                 this.mesh.updateVerticesData(VertexBuffer.PositionKind, this._positions32, false, false);
-                if (!this.mesh.areNormalsFrozen) {
-                    if (this._computeParticleVertex) {
+                if (!this.mesh.areNormalsFrozen || this.mesh.isFacetDataEnabled) {
+                    if (this._computeParticleVertex || this.mesh.isFacetDataEnabled) {
                         // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
-                        VertexData.ComputeNormals(this._positions32, this._indices, this._normals32);
+                        var params = this.mesh.isFacetDataEnabled ? this.mesh.getFacetDataParameters() : null;
+                        VertexData.ComputeNormals(this._positions32, this._indices, this._normals32, params);
                         for (var i = 0; i < this._normals32.length; i++) {
                             this._fixedNormal32[i] = this._normals32[i];
                         }
                     }
-                    this.mesh.updateVerticesData(VertexBuffer.NormalKind, this._normals32, false, false);
+                    if (!this.mesh.areNormalsFrozen) {
+                        this.mesh.updateVerticesData(VertexBuffer.NormalKind, this._normals32, false, false);
+                    }
                 }
             }
             if (this._computeBoundingBox) {

+ 17 - 9
src/PostProcess/babylon.hdrRenderingPipeline.ts

@@ -183,18 +183,26 @@
         * Releases the rendering pipeline and its internal effects. Detaches pipeline from cameras
         */
         public dispose(): void {
-            this._originalPostProcess = undefined;
-            this._brightPassPostProcess = undefined;
-            this._downSampleX4PostProcess = undefined;
-            this._guassianBlurHPostProcess = undefined;
-            this._guassianBlurVPostProcess = undefined;
-            this._textureAdderPostProcess = undefined;
-            for (var i = HDRRenderingPipeline.LUM_STEPS - 1; i >= 0; i--) {
-                this._downSamplePostProcesses[i] = undefined;
+            for (var i = 0; i < this._scene.cameras.length; i++) {
+                var camera = this._scene.cameras[i];
+
+                this._originalPostProcess.dispose(camera);
+                this._brightPassPostProcess.dispose(camera);
+                this._downSampleX4PostProcess.dispose(camera);
+                this._guassianBlurHPostProcess.dispose(camera);
+                this._guassianBlurVPostProcess.dispose(camera);
+                this._textureAdderPostProcess.dispose(camera);
+
+                for (var j = HDRRenderingPipeline.LUM_STEPS - 1; j >= 0; j--) {
+                    this._downSamplePostProcesses[j].dispose(camera);
+                }
+
+                this._hdrPostProcess.dispose(camera);
             }
-            this._hdrPostProcess = undefined;
 
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
+
+            super.dispose();
         }
 
         /**

+ 2 - 0
src/PostProcess/babylon.ssaoRenderingPipeline.ts

@@ -164,6 +164,8 @@
                 this._scene.disableDepthRenderer();
 
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
+
+            super.dispose();
         }
 
         // Private Methods

+ 1 - 1
src/Shaders/ShadersInclude/fogVertex.fx

@@ -1,3 +1,3 @@
 #ifdef FOG
-fFogDistance = (view * worldPos).z;
+fFogDistance = abs((view * worldPos).z);
 #endif

+ 1 - 1
src/Shaders/sprites.vertex.fx

@@ -46,6 +46,6 @@ void main(void) {
 
 	// Fog
 #ifdef FOG
-	fFogDistance = viewPos.z;
+	fFogDistance = abs(viewPos.z);
 #endif
 }

+ 20 - 37
src/Tools/babylon.filesInput.ts

@@ -9,8 +9,8 @@
         private _textureLoadingCallback;
         private _startingProcessingFilesCallback;
         private _elementToMonitor: HTMLElement;
-        public static FilesTextures: any[] = new Array();
-        public static FilesToLoad: any[] = new Array();
+        public static FilesTextures: File[] = new Array();
+        public static FilesToLoad: File[] = new Array();
 
         private _sceneFileToLoad: File;
         private _filesToLoad: File[];
@@ -55,12 +55,12 @@
             }
         }
 
-        private drag(e): void {
+        private drag(e: DragEvent): void {
             e.stopPropagation();
             e.preventDefault();
         }
 
-        private drop(eventDrop): void {
+        private drop(eventDrop: DragEvent): void {
             eventDrop.stopPropagation();
             eventDrop.preventDefault();
 
@@ -82,39 +82,22 @@
 
             if (this._filesToLoad && this._filesToLoad.length > 0) {
                 for (var i = 0; i < this._filesToLoad.length; i++) {
-                    switch (this._filesToLoad[i].type) {
-                        case "image/jpeg":
-                        case "image/png":
-                        case "image/bmp":
-                            FilesInput.FilesTextures[this._filesToLoad[i].name.toLowerCase()] = this._filesToLoad[i];
-                            break;
-                        case "image/targa":
-                        case "image/vnd.ms-dds":
-                        case "audio/wav":
-                        case "audio/x-wav":
-                        case "audio/mp3":
-                        case "audio/mpeg":
-                        case "audio/mpeg3":
-                        case "audio/x-mpeg-3":
-                        case "audio/ogg":
-                            FilesInput.FilesToLoad[this._filesToLoad[i].name.toLowerCase()] = this._filesToLoad[i];
-                            break;
-                        default:
-                            if (this._filesToLoad[i].name.indexOf(".mtl") !== -1) {
-                                FilesInput.FilesToLoad[this._filesToLoad[i].name.toLowerCase()] = this._filesToLoad[i];
-                            }
-                            else if ((
-                                this._filesToLoad[i].name.indexOf(".babylon") !== -1 || 
-                                this._filesToLoad[i].name.indexOf(".stl") !== -1 ||
-                                this._filesToLoad[i].name.indexOf(".obj") !== -1
-                                )   
-                                && this._filesToLoad[i].name.indexOf(".manifest") === -1
-                                && this._filesToLoad[i].name.indexOf(".incremental") === -1 && this._filesToLoad[i].name.indexOf(".babylonmeshdata") === -1
-                                && this._filesToLoad[i].name.indexOf(".babylongeometrydata") === -1 && this._filesToLoad[i].name.indexOf(".babylonbinarymeshdata") === -1 && 
-                                this._filesToLoad[i].name.indexOf(".binary.babylon") === -1) {
-                                this._sceneFileToLoad = this._filesToLoad[i];
-                            }
-                            break;
+                    let name = this._filesToLoad[i].name.toLowerCase();
+                    let extension = name.split('.').pop();
+                    let type = this._filesToLoad[i].type;
+                    
+                    if (extension === "jpg" || extension === "png" || extension === "bmp" || extension === "jpeg" || 
+                        type === "image/jpeg" || type === "image/png" || type === "image/bmp") {
+                           FilesInput.FilesTextures[name] = this._filesToLoad[i]; 
+                        }
+                    else {
+                        if ((extension === "babylon" || extension === "stl" || extension === "obj") 
+                            && name.indexOf(".binary.babylon") === -1 && name.indexOf(".incremental.babylon") === -1) {
+                            this._sceneFileToLoad = this._filesToLoad[i];
+                        }
+                        else {
+                            FilesInput.FilesToLoad[name] = this._filesToLoad[i];
+                        }
                     }
                 }
 

Разница между файлами не показана из-за своего большого размера
+ 27 - 13
src/Tools/babylon.tools.ts


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

@@ -298,8 +298,8 @@ module BABYLON {
 
         private _drawVirtualJoystick() {
             if (this.pressed) {
-                this._touches.forEach((touch: any) => {
-                    if (touch.pointerId === this._joystickPointerID) {
+                this._touches.forEach((key, touch) => {
+                    if ((<PointerEvent>touch).pointerId === this._joystickPointerID) {
                         VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);                       
                         VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
                         VirtualJoystick.vjCanvasContext.beginPath();
@@ -322,7 +322,7 @@ module BABYLON {
                         this._joystickPreviousPointerPos = this._joystickPointerPos.clone();
                     }
                     else {
-                        VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
+                        VirtualJoystick.vjCanvasContext.clearRect((<any>touch).prevX - 43, (<any>touch).prevY - 43, 86, 86);
                         VirtualJoystick.vjCanvasContext.beginPath();
                         VirtualJoystick.vjCanvasContext.fillStyle = "white";
                         VirtualJoystick.vjCanvasContext.beginPath();
@@ -331,8 +331,8 @@ module BABYLON {
                         VirtualJoystick.vjCanvasContext.arc(touch.x, touch.y, 40, 0, Math.PI * 2, true);
                         VirtualJoystick.vjCanvasContext.stroke();
                         VirtualJoystick.vjCanvasContext.closePath();
-                        touch.prevX = touch.x;
-                        touch.prevY = touch.y;
+                        (<any>touch).prevX = touch.x;
+                        (<any>touch).prevY = touch.y;
                     };
                 });
             }

+ 98 - 100
src/babylon.engine.ts

@@ -618,12 +618,12 @@
             // Checks if some of the format renders first to allow the use of webgl inspector.
             this._caps.colorBufferFloat = this._webGLVersion > 1 && this._gl.getExtension('EXT_color_buffer_float')
 
-            this._caps.textureFloat = this._webGLVersion > 1 || (this._gl.getExtension('OES_texture_float') !== null);
-            this._caps.textureFloatLinearFiltering = this._gl.getExtension('OES_texture_float_linear');
+            this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
+            this._caps.textureFloatLinearFiltering = this._caps.textureFloat && this._gl.getExtension('OES_texture_float_linear');
             this._caps.textureFloatRender = this._caps.textureFloat && this._canRenderToFloatFramebuffer();            
 
-            this._caps.textureHalfFloat = this._webGLVersion > 1 || (this._gl.getExtension('OES_texture_half_float') !== null);
-            this._caps.textureHalfFloatLinearFiltering = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_half_float_linear');
+            this._caps.textureHalfFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_half_float');
+            this._caps.textureHalfFloatLinearFiltering = this._webGLVersion > 1 || (this._caps.textureHalfFloat && this._gl.getExtension('OES_texture_half_float_linear'));
             this._caps.textureHalfFloatRender = this._caps.textureHalfFloat && this._canRenderToHalfFloatFramebuffer();
             
             this._caps.textureLOD = this._webGLVersion > 1 || this._gl.getExtension('EXT_shader_texture_lod');
@@ -1385,6 +1385,9 @@
         }
 
         private _bindIndexBufferWithCache(indexBuffer: WebGLBuffer): void {            
+            if (indexBuffer == null) {
+                return;
+            }
             if (this._cachedIndexBuffer !== indexBuffer) {
                 this._cachedIndexBuffer = indexBuffer;
                 this.bindIndexBuffer(indexBuffer);
@@ -2020,29 +2023,6 @@
             this.bindArrayBuffer(null);
         }
 
-        public setSamplingMode(texture: WebGLTexture, samplingMode: number): void {
-            var gl = this._gl;
-
-            this._bindTextureDirectly(gl.TEXTURE_2D, texture);
-
-            var magFilter = gl.NEAREST;
-            var minFilter = gl.NEAREST;
-
-            if (samplingMode === Texture.BILINEAR_SAMPLINGMODE) {
-                magFilter = gl.LINEAR;
-                minFilter = gl.LINEAR;
-            } else if (samplingMode === Texture.TRILINEAR_SAMPLINGMODE) {
-                magFilter = gl.LINEAR;
-                minFilter = gl.LINEAR_MIPMAP_LINEAR;
-            }
-
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
-
-            this._bindTextureDirectly(gl.TEXTURE_2D, null);
-
-            texture.samplingMode = samplingMode;
-        }
         /**
          * Set the compressed texture format to use, based on the formats you have, and the formats
          * supported by the hardware / browser.
@@ -2055,8 +2035,7 @@
          * for description see https://www.khronos.org/opengles/sdk/tools/KTX/
          * for file layout see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
          * 
-         * Note: The result of this call is not taken into account when a texture is base64 or when
-         * using a database / manifest.
+         * Note: The result of this call is not taken into account when a texture is base64.
          * 
          * @param {Array<string>} formatsAvailable- The list of those format families you have created
          * on your server.  Syntax: '-' + format family + '.ktx'.  (Case and order do not matter.)
@@ -2077,12 +2056,13 @@
             return this._textureFormatInUse = null;
         }
 
-        public createTexture(url: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: () => void = null, onError: () => void = null, buffer: any = null): WebGLTexture {
-            var texture = this._gl.createTexture();
+        public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: () => void = null, onError: () => void = null, buffer: any = null, fallBack?: WebGLTexture): WebGLTexture {
+            var texture = fallBack ? fallBack : this._gl.createTexture();
 
             var extension: string;
             var isKTX = false;
             var fromData: any = false;
+            var url = String(urlArg);
             if (url.substr(0, 5) === "data:") {
                 fromData = true;
             }
@@ -2090,10 +2070,11 @@
             if (!fromData) {
                 var lastDot = url.lastIndexOf('.')
                 extension = url.substring(lastDot).toLowerCase();
-                if (this._textureFormatInUse && !fromData && !scene.database) {
+                if (this._textureFormatInUse && !fromData && !fallBack) {
                     extension = this._textureFormatInUse;
                     url = url.substring(0, lastDot) + this._textureFormatInUse;
                     isKTX = true;
+                    
                 }
             } else {
                 var oldUrl = url;
@@ -2102,7 +2083,7 @@
                 extension = fromData[1].substr(fromData[1].length - 4, 4).toLowerCase();
             }
 
-            var isDDS = (extension === ".dds");
+            var isDDS = this.getCaps().s3tc && (extension === ".dds");
             var isTGA = (extension === ".tga");
 
             scene._addPendingData(texture);
@@ -2114,12 +2095,15 @@
             if (onLoad) {
                 texture.onLoadedCallbacks.push(onLoad);
             }
-            this._loadedTexturesCache.push(texture);
+            if (!fallBack) this._loadedTexturesCache.push(texture);
 
             var onerror = () => {
                 scene._removePendingData(texture);
 
-                if (onError) {
+                // fallback for when compressed file not found to try again.  For instance, etc1 does not have an alpha capable type
+                if (isKTX) {
+                    this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer, texture);
+                } else if (onError) {
                     onError();
                 }
             };
@@ -2308,7 +2292,7 @@
 
         public updateTextureSamplingMode(samplingMode: number, texture: WebGLTexture): void {
             var filters = getSamplingParameters(samplingMode, texture.generateMipMaps, this._gl);
-
+ 
             if (texture.isCube) {
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
 
@@ -2322,6 +2306,8 @@
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             }
+
+             texture.samplingMode = samplingMode;
         }
 
         public updateDynamicTexture(texture: WebGLTexture, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha: boolean = false): void {
@@ -2604,12 +2590,12 @@
             var isKTX = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = rootUrl.substring(lastDot).toLowerCase();
-            if (this._textureFormatInUse && !scene.database) {
+            if (this._textureFormatInUse) {
                 extension = this._textureFormatInUse;
                 rootUrl = rootUrl.substring(0, lastDot) + this._textureFormatInUse;
                 isKTX = true;
             }
-            var isDDS = (extension === ".dds");
+            var isDDS = this.getCaps().s3tc && (extension === ".dds");
 
             if (isKTX) {
                 Tools.LoadFile(rootUrl, data => {
@@ -2741,6 +2727,11 @@
             }
 
             var internalFormat = this._getInternalFormat(format);
+            var needConversion = false;
+            if (internalFormat === gl.RGB) {
+                internalFormat = gl.RGBA;
+                needConversion = true;
+            }
             var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
 
             var width = size;
@@ -2769,74 +2760,50 @@
                 this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
 
-                if (!noMipmap && isPot) {
-                    if (mipmmapGenerator) {
-
-                        var arrayTemp: ArrayBufferView[] = [];
-                        // Data are known to be in +X +Y +Z -X -Y -Z
-                        // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
-                        arrayTemp.push(rgbeDataArrays[0]); // +X
-                        arrayTemp.push(rgbeDataArrays[3]); // -X
-                        arrayTemp.push(rgbeDataArrays[1]); // +Y
-                        arrayTemp.push(rgbeDataArrays[4]); // -Y
-                        arrayTemp.push(rgbeDataArrays[2]); // +Z
-                        arrayTemp.push(rgbeDataArrays[5]); // -Z
-
-                        var mipData = mipmmapGenerator(arrayTemp);
-                        for (var level = 0; level < mipData.length; level++) {
-                            var mipSize = width >> level;
-
-                            // mipData is order in +X -X +Y -Y +Z -Z
-                            gl.texImage2D(facesIndex[0], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][0]);
-                            gl.texImage2D(facesIndex[1], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][2]);
-                            gl.texImage2D(facesIndex[2], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][4]);
-                            gl.texImage2D(facesIndex[3], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][1]);
-                            gl.texImage2D(facesIndex[4], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][3]);
-                            gl.texImage2D(facesIndex[5], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipData[level][5]);
-                        }
-                    }
-                    else {
-                        if (internalFormat === gl.RGB) {
-                            internalFormat = gl.RGBA;
-
-                             // Data are known to be in +X +Y +Z -X -Y -Z
-                            for (let index = 0; index < facesIndex.length; index++) {
-                                let faceData = <Float32Array>rgbeDataArrays[index];
-
-                                // Create a new RGBA Face.
-                                let newFaceData = new Float32Array(width * height * 4);
-                                for (let x = 0; x < width; x++) {
-                                    for (let y = 0; y < height; y++) {
-                                        let index = (y * width + x) * 3;
-                                        let newIndex = (y * width + x) * 4;
-
-                                        // Map Old Value to new value.
-                                        newFaceData[newIndex + 0] = faceData[index + 0];
-                                        newFaceData[newIndex + 1] = faceData[index + 1];
-                                        newFaceData[newIndex + 2] = faceData[index + 2];
-
-                                        // Add fully opaque alpha channel.
-                                        newFaceData[newIndex + 3] = 1;
-                                    }
-                                }
-
-                                // Reupload the face.
-                                gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, newFaceData);
+                if (mipmmapGenerator) {
+
+                    var arrayTemp: ArrayBufferView[] = [];
+                    // Data are known to be in +X +Y +Z -X -Y -Z
+                    // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
+                    arrayTemp.push(rgbeDataArrays[0]); // +X
+                    arrayTemp.push(rgbeDataArrays[3]); // -X
+                    arrayTemp.push(rgbeDataArrays[1]); // +Y
+                    arrayTemp.push(rgbeDataArrays[4]); // -Y
+                    arrayTemp.push(rgbeDataArrays[2]); // +Z
+                    arrayTemp.push(rgbeDataArrays[5]); // -Z
+
+                    var mipData = mipmmapGenerator(arrayTemp);
+                    // mipData is order in +X -X +Y -Y +Z -Z
+                    var mipFaces = [0, 2, 4, 1, 3, 5];
+                    for (var level = 0; level < mipData.length; level++) {
+                        var mipSize = width >> level;
+
+                        for (let mipIndex in mipFaces) {
+                            let mipFaceData = mipData[level][mipFaces[mipIndex]];
+                            if (needConversion) {
+                                mipFaceData = this._convertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
                             }
+                            gl.texImage2D(facesIndex[mipIndex], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData);
                         }
-                        else {
-                            // Data are known to be in +X +Y +Z -X -Y -Z
-                            for (let index = 0; index < facesIndex.length; index++) {
-                                let faceData = rgbeDataArrays[index];
-                                gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, faceData);
-                            }
+                    }
+                }
+                else {                    
+                    // Data are known to be in +X +Y +Z -X -Y -Z
+                    for (let index = 0; index < facesIndex.length; index++) {
+                        let faceData = rgbeDataArrays[index];
+                        if (needConversion) {
+                            faceData = this._convertRGBtoRGBATextureData(faceData, width, height, type);
                         }
 
+                        gl.texImage2D(facesIndex[index], 0, internalSizedFomat, width, height, 0, internalFormat, textureType, faceData);
+                    }
+
+                    if (!noMipmap && isPot) {
                         gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                     }
-                }
-                else {
-                    noMipmap = true;
+                    else {
+                        noMipmap = true;
+                    }
                 }
 
                 if (textureType === gl.FLOAT && !this._caps.textureFloatLinearFiltering) {
@@ -2869,6 +2836,35 @@
             return texture;
         };
 
+        private _convertRGBtoRGBATextureData(rgbData: ArrayBufferView, width: number, height: number, textureType: number): ArrayBufferView {
+            // Create new RGBA data container.
+            var rgbaData: ArrayBufferView;
+            if (textureType === Engine.TEXTURETYPE_FLOAT) {
+                rgbaData = new Float32Array(width * height * 4);
+            }
+            else {
+                rgbaData = new Uint32Array(width * height * 4);
+            }
+
+            // Convert each pixel.
+            for (let x = 0; x < width; x++) {
+                for (let y = 0; y < height; y++) {
+                    let index = (y * width + x) * 3;
+                    let newIndex = (y * width + x) * 4;
+
+                    // Map Old Value to new value.
+                    rgbaData[newIndex + 0] = rgbData[index + 0];
+                    rgbaData[newIndex + 1] = rgbData[index + 1];
+                    rgbaData[newIndex + 2] = rgbData[index + 2];
+
+                    // Add fully opaque alpha channel.
+                    rgbaData[newIndex + 3] = 1;
+                }
+            }
+
+            return rgbaData;
+        }
+
         public _releaseTexture(texture: WebGLTexture): void {
             var gl = this._gl;
 
@@ -3140,6 +3136,7 @@
                 for (var i = 0; i < this._caps.maxVertexAttribs; i++) {
                     this._gl.disableVertexAttribArray(i);
                     this._vertexAttribArraysEnabled[i] = false;
+                    this._currentBufferPointers[i] = null;
                 }
                 return;
             }
@@ -3151,6 +3148,7 @@
 
                 this._gl.disableVertexAttribArray(i);
                 this._vertexAttribArraysEnabled[i] = false;
+                this._currentBufferPointers[i] = null;
             }
         }