Prechádzať zdrojové kódy

Merge remote-tracking branch 'refs/remotes/BabylonJS/master'

DESKTOP-QJU4N0L\mityh 8 rokov pred
rodič
commit
235cad0384
100 zmenil súbory, kde vykonal 25159 pridanie a 20352 odobranie
  1. 11 2
      Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs
  2. 3 2
      Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj
  3. 6 4
      Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs
  4. 12 29
      Exporters/3ds Max/BabylonExport.Entities/BabylonVector3.cs
  5. 1 1
      Exporters/3ds Max/BabylonExport.Entities/packages.config
  6. 10 1
      Exporters/3ds Max/GltfExport.Entities/GLTF.cs
  7. 25 0
      Exporters/3ds Max/GltfExport.Entities/GLTFCamera.cs
  8. 20 0
      Exporters/3ds Max/GltfExport.Entities/GLTFCameraOrthographic.cs
  9. 20 0
      Exporters/3ds Max/GltfExport.Entities/GLTFCameraPerspective.cs
  10. 3 0
      Exporters/3ds Max/GltfExport.Entities/GLTFExport.Entities.csproj
  11. 114 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Camera.cs
  12. 49 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Light.cs
  13. 23 24
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Material.cs
  14. 273 188
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Mesh.cs
  15. 4 4
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Texture.cs
  16. 93 34
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.cs
  17. 42 0
      Exporters/3ds Max/Max2Babylon/2017/JsonTextWriterBounded.cs
  18. 3 0
      Exporters/3ds Max/Max2Babylon/2017/Max2Babylon2017.csproj
  19. 16 6
      Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.Designer.cs
  20. 4 0
      Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.resx
  21. BIN
      Exporters/3ds Max/Max2Babylon/2017/Resources/Logo_Exporter_v3.jpg
  22. 19 6
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Camera.cs
  23. 4 4
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs
  24. 1 1
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs
  25. 2 3
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.Designer.cs
  26. 0 197
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx
  27. 1 1
      Playground/frame.html
  28. 0 0
      Playground/js/frame.js
  29. 158 0
      Playground/js/perf.js
  30. 77 0
      Playground/perf.html
  31. 9 3
      Tools/Gulp/config.json
  32. 1124 988
      dist/preview release/babylon.d.ts
  33. 43 43
      dist/preview release/babylon.js
  34. 1228 522
      dist/preview release/babylon.max.js
  35. 1124 988
      dist/preview release/babylon.module.d.ts
  36. 44 44
      dist/preview release/babylon.worker.js
  37. 8229 8093
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  38. 32 31
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  39. 1145 459
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  40. 8229 8093
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  41. 2 1
      dist/preview release/gui/babylon.gui.js
  42. 3 3
      dist/preview release/gui/babylon.gui.min.js
  43. 263 263
      dist/preview release/inspector/babylon.inspector.bundle.js
  44. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  45. 1 0
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  46. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  47. 8 1
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  48. 79 12
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  49. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  50. 9 1
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  51. 79 12
      dist/preview release/loaders/babylon.glTFFileLoader.js
  52. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  53. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  54. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  55. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  56. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  57. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  58. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  59. 8 1
      dist/preview release/what's new.md
  60. 1 0
      gui/src/advancedDynamicTexture.ts
  61. 1 1
      gui/src/controls/control.ts
  62. 1 0
      loaders/src/glTF/1.0/babylon.glTFLoaderInterfaces.ts
  63. 104 11
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  64. 1 1
      loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts
  65. 9 5
      localDev/index.html
  66. 1191 0
      localDev/src/webgl-debug.js
  67. 4 3
      src/Behaviors/Cameras/babylon.autoRotationBehavior.ts
  68. 4 3
      src/Behaviors/Cameras/babylon.bouncingBehavior.ts
  69. 5 3
      src/Behaviors/Cameras/babylon.framingBehavior.ts
  70. 1 1
      src/Behaviors/babylon.behavior.ts
  71. 37 7
      src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts
  72. 8 7
      src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts
  73. 4 2
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  74. 8 15
      src/Cameras/VR/babylon.webVRCamera.ts
  75. 43 0
      src/Cameras/babylon.arcRotateCamera.ts
  76. 30 2
      src/Cameras/babylon.camera.ts
  77. 6 6
      src/Cameras/babylon.freeCamera.ts
  78. 40 0
      src/Cameras/babylon.targetCamera.ts
  79. 26 0
      src/Gamepad/Controllers/babylon.genericController.ts
  80. 130 0
      src/Gamepad/Controllers/babylon.oculusTouchController.ts
  81. 166 0
      src/Gamepad/Controllers/babylon.poseEnabledController.ts
  82. 82 0
      src/Gamepad/Controllers/babylon.viveController.ts
  83. 93 0
      src/Gamepad/Controllers/babylon.webVRController.ts
  84. 125 0
      src/Gamepad/babylon.gamepad.ts
  85. 3 8
      src/Tools/Gamepad/babylon.gamepadManager.ts
  86. 0 112
      src/Tools/Gamepad/babylon.gamepads.ts
  87. 28 13
      src/Layer/babylon.highlightlayer.ts
  88. 21 10
      src/Layer/babylon.layer.ts
  89. 16 0
      src/Lights/Shadows/babylon.shadowGenerator.ts
  90. 3 3
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  91. 16 1
      src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts
  92. 13 23
      src/Materials/Textures/babylon.baseTexture.ts
  93. 1 1
      src/Materials/Textures/babylon.colorGradingTexture.ts
  94. 1 1
      src/Materials/Textures/babylon.cubeTexture.ts
  95. 4 0
      src/Materials/Textures/babylon.dynamicTexture.ts
  96. 1 1
      src/Materials/Textures/babylon.hdrCubeTexture.ts
  97. 221 0
      src/Materials/Textures/babylon.internalTexture.ts
  98. 31 15
      src/Materials/Textures/babylon.multiRenderTarget.ts
  99. 12 17
      src/Materials/Textures/babylon.renderTargetTexture.ts
  100. 0 0
      src/Materials/Textures/babylon.texture.ts

+ 11 - 2
Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs

@@ -5,6 +5,12 @@ namespace BabylonExport.Entities
     [DataContract]
     public class BabylonCamera : BabylonNode
     {
+        public enum CameraMode
+        {
+            PERSPECTIVE_CAMERA = 0,
+            ORTHOGRAPHIC_CAMERA = 1
+        }
+
         [DataMember]
         public string lockedTargetId { get; set; }
 
@@ -18,6 +24,9 @@ namespace BabylonExport.Entities
         public float[] rotation { get; set; }
 
         [DataMember]
+        public float[] rotationQuaternion { get; set; }
+
+        [DataMember]
         public float[] target { get; set; }
 
         [DataMember]
@@ -48,7 +57,7 @@ namespace BabylonExport.Entities
         public float[] ellipsoid { get; set; }
 
         [DataMember]
-        public int mode { get; set; }
+        public CameraMode mode { get; set; }
 
         [DataMember]
         public float? orthoLeft { get; set; }
@@ -84,7 +93,7 @@ namespace BabylonExport.Entities
             inertia = 0.9f;
             interaxialDistance = 0.0637f;
 
-            mode = 0;
+            mode = CameraMode.PERSPECTIVE_CAMERA;
             orthoLeft = null;
             orthoRight = null;
             orthoBottom = null;

+ 3 - 2
Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj

@@ -9,7 +9,7 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>BabylonExport.Entities</RootNamespace>
     <AssemblyName>BabylonExport.Entities</AssemblyName>
-    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <SccProjectName>SAK</SccProjectName>
     <SccLocalPath>SAK</SccLocalPath>
@@ -91,13 +91,14 @@
     <None Include="packages.config" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!--
   <Import Project=".\packages\SharpDX.2.6.3\build\SharpDX.targets" Condition="Exists('.\packages\SharpDX.2.6.3\build\SharpDX.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
     <Error Condition="!Exists('.\packages\SharpDX.2.6.3\build\SharpDX.targets')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\SharpDX.2.6.3\build\SharpDX.targets'))" />
-  </Target>
+  </Target> -->
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 6 - 4
Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs

@@ -46,11 +46,11 @@ namespace BabylonExport.Entities
 
         [DataMember]
         public float[] physicsGravity { get; set; }
-
-        [DataMember]
+        
+        [DataMember(EmitDefaultValue = false)]
         public BabylonCamera[] cameras { get; set; }
-
-        [DataMember]
+        
+        [DataMember(EmitDefaultValue = false)]
         public string activeCameraID { get; set; }
 
         [DataMember]
@@ -142,6 +142,8 @@ namespace BabylonExport.Entities
             {
                 var camera = new BabylonCamera { name = "Default camera", id = Guid.NewGuid().ToString() };
 
+                // Default camera init gives infinit values
+                // Indeed, float.MaxValue - float.MinValue always leads to infinity
                 var distanceVector = MaxVector - MinVector;
                 var midPoint = MinVector + distanceVector / 2;
                 camera.target = midPoint.ToArray();

+ 12 - 29
Exporters/3ds Max/BabylonExport.Entities/BabylonVector3.cs

@@ -38,40 +38,23 @@ namespace BabylonExport.Entities
             return new BabylonVector3 { X = a.X * b, Y = a.Y * b, Z = a.Z * b };
         }
 
-        /**
-         * Returns a new Quaternion object, computed from the Vector3 coordinates.
-         */
-        public BabylonQuaternion toQuaternion()
+        public BabylonQuaternion toQuaternionGltf()
         {
-            var result = new BabylonQuaternion();
-
-            var cosxPlusz = Math.Cos((this.X + this.Z) * 0.5);
-            var sinxPlusz = Math.Sin((this.X + this.Z) * 0.5);
-            var coszMinusx = Math.Cos((this.Z - this.X) * 0.5);
-            var sinzMinusx = Math.Sin((this.Z - this.X) * 0.5);
-            var cosy = Math.Cos(this.Y * 0.5);
-            var siny = Math.Sin(this.Y * 0.5);
-
-            result.X = (float)(coszMinusx * siny);
-            result.Y = (float)(-sinzMinusx * siny);
-            result.Z = (float)(sinxPlusz * cosy);
-            result.W = (float)(cosxPlusz * cosy);
-            return result;
+            BabylonQuaternion babylonQuaternion = RotationYawPitchRollToRefBabylon(X, -Y, -Z);
+            // Doing following computation is ugly but works
+            // The goal is to switch from left to right handed coordinate system
+            // Swap X and Y
+            var tmp = babylonQuaternion.X;
+            babylonQuaternion.X = babylonQuaternion.Y;
+            babylonQuaternion.Y = tmp;
+            return babylonQuaternion;
         }
 
-        ///**
-        // * Returns a new Quaternion object, computed from the Vector3 coordinates.
-        // */
-        //public BabylonQuaternion toQuaternion()
-        //{
-        //    return RotationYawPitchRollToRef(Y,X,Z);
-        //}
-
-
         /**
+         * (Copy pasted from babylon)
          * Sets the passed quaternion "result" from the passed float Euler angles (y, x, z).  
          */
-        public BabylonQuaternion RotationYawPitchRollToRef(float yaw, float pitch, float roll)
+        private BabylonQuaternion RotationYawPitchRollToRefBabylon(float yaw, float pitch, float roll)
         {
             // Produces a quaternion from Euler angles in the z-y-x orientation (Tait-Bryan angles)
             var halfRoll = roll * 0.5;
@@ -92,5 +75,5 @@ namespace BabylonExport.Entities
             result.W = (float)((cosYaw * cosPitch * cosRoll) + (sinYaw * sinPitch * sinRoll));
             return result;
         }
-}
+    }
 }

+ 1 - 1
Exporters/3ds Max/BabylonExport.Entities/packages.config

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="SharpDX" version="2.6.3" targetFramework="net35" />
+  <package id="SharpDX" version="2.6.3" targetFramework="net35" requireReinstallation="true" />
 </packages>

+ 10 - 1
Exporters/3ds Max/GltfExport.Entities/GLTF.cs

@@ -10,7 +10,7 @@ namespace GLTFExport.Entities
         public GLTFAsset asset { get; set; }
 
         [DataMember(EmitDefaultValue = false)]
-        public int scene { get; set; }
+        public int? scene { get; set; }
 
         [DataMember(EmitDefaultValue = false)]
         public GLTFScene[] scenes { get; set; }
@@ -19,6 +19,9 @@ namespace GLTFExport.Entities
         public GLTFNode[] nodes { get; set; }
 
         [DataMember(EmitDefaultValue = false)]
+        public GLTFCamera[] cameras { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
         public GLTFMesh[] meshes { get; set; }
 
         [DataMember(EmitDefaultValue = false)]
@@ -45,6 +48,7 @@ namespace GLTFExport.Entities
         public string OutputPath { get; private set; }
 
         public List<GLTFNode> NodesList { get; private set; }
+        public List<GLTFCamera> CamerasList { get; private set; }
         public List<GLTFBuffer> BuffersList { get; private set; }
         public List<GLTFBufferView> BufferViewsList { get; private set; }
         public List<GLTFAccessor> AccessorsList { get; private set; }
@@ -59,6 +63,7 @@ namespace GLTFExport.Entities
             OutputPath = outputPath;
 
             NodesList = new List<GLTFNode>();
+            CamerasList = new List<GLTFCamera>();
             BuffersList = new List<GLTFBuffer>();
             BufferViewsList = new List<GLTFBufferView>();
             AccessorsList = new List<GLTFAccessor>();
@@ -79,6 +84,10 @@ namespace GLTFExport.Entities
                 nodes = NodesList.ToArray();
                 NodesList.ForEach(node => node.Prepare());
             }
+            if (CamerasList.Count > 0)
+            {
+                cameras = CamerasList.ToArray();
+            }
             if (BuffersList.Count > 0)
             {
                 buffers = BuffersList.ToArray();

+ 25 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFCamera.cs

@@ -0,0 +1,25 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFCamera : GLTFIndexedChildRootProperty
+    {
+        public enum CameraType
+        {
+            perspective,
+            orthographic
+        }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFCameraOrthographic orthographic { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFCameraPerspective perspective { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public string type { get; set; }
+
+        public GLTFNode gltfNode;
+    }
+}

+ 20 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFCameraOrthographic.cs

@@ -0,0 +1,20 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFCameraOrthographic : GLTFProperty
+    {
+        [DataMember(IsRequired = true)]
+        public float xmag { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float ymag { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float zfar { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float znear { get; set; }
+    }
+}

+ 20 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFCameraPerspective.cs

@@ -0,0 +1,20 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFCameraPerspective : GLTFProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public float? aspectRatio { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float yfov { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float? zfar { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float znear { get; set; }
+    }
+}

+ 3 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFExport.Entities.csproj

@@ -44,6 +44,9 @@
     <Compile Include="GLTFAccessor.cs" />
     <Compile Include="GLTFBufferView.cs" />
     <Compile Include="GLTFBuffer.cs" />
+    <Compile Include="GLTFCameraPerspective.cs" />
+    <Compile Include="GLTFCameraOrthographic.cs" />
+    <Compile Include="GLTFCamera.cs" />
     <Compile Include="GLTFSampler.cs" />
     <Compile Include="GLTFIndexedChildRootProperty.cs" />
     <Compile Include="GLTFImage.cs" />

+ 114 - 0
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Camera.cs

@@ -0,0 +1,114 @@
+using BabylonExport.Entities;
+using GLTFExport.Entities;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        // TODO - Test if ok with a gltf viewer working with custom camera (babylon loader/sandbox doesn't load them)
+        private GLTFCamera ExportCamera(BabylonCamera babylonCamera, GLTF gltf, GLTFNode gltfParentNode)
+        {
+            RaiseMessage("GLTFExporter.Camera | Export camera named: " + babylonCamera.name, 1);
+
+            // --------------------------
+            // ---------- Node ----------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Camera | Node", 2);
+            // Node
+            var gltfNode = new GLTFNode();
+            gltfNode.name = babylonCamera.name;
+            gltfNode.index = gltf.NodesList.Count;
+            gltf.NodesList.Add(gltfNode);
+
+            // Hierarchy
+            if (gltfParentNode != null)
+            {
+                RaiseMessage("GLTFExporter.Camera | Add " + babylonCamera.name + " as child to " + gltfParentNode.name, 3);
+                gltfParentNode.ChildrenList.Add(gltfNode.index);
+            }
+            else
+            {
+                // It's a root node
+                // Only root nodes are listed in a gltf scene
+                RaiseMessage("GLTFExporter.Camera | Add " + babylonCamera.name + " as root node to scene", 3);
+                gltf.scenes[0].NodesList.Add(gltfNode.index);
+            }
+
+            // Transform
+            gltfNode.translation = babylonCamera.position;
+            // Switch from left to right handed coordinate system
+            //gltfNode.translation[0] *= -1;
+            if (babylonCamera.rotationQuaternion != null)
+            {
+                gltfNode.rotation = babylonCamera.rotationQuaternion;
+            }
+            else
+            {
+                // Convert rotation vector to quaternion
+                BabylonVector3 rotationVector3 = new BabylonVector3
+                {
+                    X = babylonCamera.rotation[0],
+                    Y = babylonCamera.rotation[1],
+                    Z = babylonCamera.rotation[2]
+                };
+                gltfNode.rotation = rotationVector3.toQuaternionGltf().ToArray();
+            }
+            // No scaling defined for babylon camera. Use identity instead.
+            gltfNode.scale = new float[3] { 1, 1, 1 };
+
+
+            // --- prints ---
+
+            RaiseMessage("GLTFExporter.Camera | babylonCamera data", 2);
+            RaiseMessage("GLTFExporter.Camera | babylonCamera.type=" + babylonCamera.type, 3);
+            RaiseMessage("GLTFExporter.Camera | babylonCamera.fov=" + babylonCamera.fov, 3);
+            RaiseMessage("GLTFExporter.Camera | babylonCamera.maxZ=" + babylonCamera.maxZ, 3);
+            RaiseMessage("GLTFExporter.Camera | babylonCamera.minZ=" + babylonCamera.minZ, 3);
+
+
+            // --------------------------
+            // ------- gltfCamera -------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Camera | create gltfCamera", 2);
+
+            // Camera
+            var gltfCamera = new GLTFCamera { name = babylonCamera.name };
+            gltfCamera.index = gltf.CamerasList.Count;
+            gltf.CamerasList.Add(gltfCamera);
+            gltfNode.camera = gltfCamera.index;
+            gltfCamera.gltfNode = gltfNode;
+
+            // Camera type
+            switch (babylonCamera.mode)
+            {
+                case (BabylonCamera.CameraMode.ORTHOGRAPHIC_CAMERA):
+                    var gltfCameraOrthographic = new GLTFCameraOrthographic();
+                    gltfCameraOrthographic.xmag = 1; // TODO - How to retreive value from babylon? xmag:The floating-point horizontal magnification of the view
+                    gltfCameraOrthographic.ymag = 1; // TODO - How to retreive value from babylon? ymag:The floating-point vertical magnification of the view
+                    gltfCameraOrthographic.zfar = babylonCamera.maxZ;
+                    gltfCameraOrthographic.znear = babylonCamera.minZ;
+
+                    gltfCamera.type = GLTFCamera.CameraType.orthographic.ToString();
+                    gltfCamera.orthographic = gltfCameraOrthographic;
+                    break;
+                case (BabylonCamera.CameraMode.PERSPECTIVE_CAMERA):
+                    var gltfCameraPerspective = new GLTFCameraPerspective();
+                    gltfCameraPerspective.aspectRatio = null; // 0.8f; // TODO - How to retreive value from babylon? The aspect ratio in babylon is computed based on the engine rather than set on a camera (aspectRatio = _gl.drawingBufferWidth / _gl.drawingBufferHeight)
+                    gltfCameraPerspective.yfov = babylonCamera.fov; // WARNING - Babylon camera fov mode is assumed to be vertical (FOVMODE_VERTICAL_FIXED)
+                    gltfCameraPerspective.zfar = babylonCamera.maxZ;
+                    gltfCameraPerspective.znear = babylonCamera.minZ;
+
+                    gltfCamera.type = GLTFCamera.CameraType.perspective.ToString();
+                    gltfCamera.perspective = gltfCameraPerspective;
+                    break;
+                default:
+                    RaiseError("GLTFExporter.Camera | camera mode not found");
+                    break;
+            }
+            
+            return gltfCamera;
+        }
+    }
+}

+ 49 - 0
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Light.cs

@@ -0,0 +1,49 @@
+using BabylonExport.Entities;
+using GLTFExport.Entities;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private GLTFNode ExportLight(BabylonLight babylonLight, GLTF gltf, GLTFNode gltfParentNode)
+        {
+            RaiseMessage("GLTFExporter.Light | ExportLight babylonLight.name=" + babylonLight.name, 1);
+
+            // --------------------------
+            // ---------- Node ----------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Light | Node", 2);
+            // Node
+            var gltfNode = new GLTFNode();
+            gltfNode.name = babylonLight.name;
+            gltfNode.index = gltf.NodesList.Count;
+            gltf.NodesList.Add(gltfNode);
+
+            // Hierarchy
+            if (gltfParentNode != null)
+            {
+                RaiseMessage("GLTFExporter.Light | Add " + babylonLight.name + " as child to " + gltfParentNode.name, 3);
+                gltfParentNode.ChildrenList.Add(gltfNode.index);
+            }
+            else
+            {
+                // It's a root node
+                // Only root nodes are listed in a gltf scene
+                RaiseMessage("GLTFExporter.Light | Add " + babylonLight.name + " as root node to scene", 3);
+                gltf.scenes[0].NodesList.Add(gltfNode.index);
+            }
+
+            // Transform
+            gltfNode.translation = babylonLight.position;
+            // Switch from left to right handed coordinate system
+            //gltfNode.translation[0] *= -1;
+            // No rotation defined for babylon light. Use identity instead.
+            gltfNode.rotation = new float[4] { 0, 0, 0, 1 };
+            // No scaling defined for babylon light. Use identity instead.
+            gltfNode.scale = new float[3] { 1, 1, 1 };
+
+            return gltfNode;
+        }
+    }
+}

+ 23 - 24
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Material.cs

@@ -5,73 +5,69 @@ namespace Max2Babylon
 {
     partial class BabylonExporter
     {
-        //readonly List<IIGameMaterial> referencedMaterials = new List<IIGameMaterial>();
-
         private void ExportMaterial(BabylonMaterial babylonMaterial, GLTF gltf)
         {
             var name = babylonMaterial.name;
             var id = babylonMaterial.id;
 
-            RaiseMessage("GLTFExporter.Material | ExportMaterial name=" + name, 1);
+            RaiseMessage("GLTFExporter.Material | Export material named: " + name, 1);
 
             if (babylonMaterial.GetType() == typeof(BabylonStandardMaterial))
             {
-                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial != null", 1);
-
                 var babylonStandardMaterial = babylonMaterial as BabylonStandardMaterial;
 
 
                 // --- prints ---
 
-                RaiseMessage("GLTFExporter.Material | babylonMaterial data", 1);
-                RaiseMessage("GLTFExporter.Material | babylonMaterial.alpha=" + babylonMaterial.alpha, 2);
-                RaiseMessage("GLTFExporter.Material | babylonMaterial.backFaceCulling=" + babylonMaterial.backFaceCulling, 2);
-                RaiseMessage("GLTFExporter.Material | babylonMaterial.wireframe=" + babylonMaterial.wireframe, 2);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial data", 2);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial.alpha=" + babylonMaterial.alpha, 3);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial.backFaceCulling=" + babylonMaterial.backFaceCulling, 3);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial.wireframe=" + babylonMaterial.wireframe, 3);
 
                 // Ambient
                 for (int i = 0; i < babylonStandardMaterial.ambient.Length; i++)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.ambient[" + i + "]=" + babylonStandardMaterial.ambient[i], 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.ambient[" + i + "]=" + babylonStandardMaterial.ambient[i], 3);
                 }
 
                 // Diffuse
-                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuse.Length=" + babylonStandardMaterial.diffuse.Length, 2);
+                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuse.Length=" + babylonStandardMaterial.diffuse.Length, 3);
                 for (int i = 0; i < babylonStandardMaterial.diffuse.Length; i++)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuse[" + i + "]=" + babylonStandardMaterial.diffuse[i], 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuse[" + i + "]=" + babylonStandardMaterial.diffuse[i], 3);
                 }
                 if (babylonStandardMaterial.diffuseTexture == null)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuseTexture=null", 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuseTexture=null", 3);
                 }
 
                 // Normal / bump
                 if (babylonStandardMaterial.bumpTexture == null)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.bumpTexture=null", 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.bumpTexture=null", 3);
                 }
 
                 // Specular
                 for (int i = 0; i < babylonStandardMaterial.specular.Length; i++)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specular[" + i + "]=" + babylonStandardMaterial.specular[i], 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specular[" + i + "]=" + babylonStandardMaterial.specular[i], 3);
                 }
-                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specularPower=" + babylonStandardMaterial.specularPower, 2);
+                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specularPower=" + babylonStandardMaterial.specularPower, 3);
 
                 // Occlusion
                 if (babylonStandardMaterial.ambientTexture == null)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.ambientTexture=null", 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.ambientTexture=null", 3);
                 }
 
                 // Emissive
                 for (int i = 0; i < babylonStandardMaterial.emissive.Length; i++)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.emissive[" + i + "]=" + babylonStandardMaterial.emissive[i], 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.emissive[" + i + "]=" + babylonStandardMaterial.emissive[i], 3);
                 }
                 if (babylonStandardMaterial.emissiveTexture == null)
                 {
-                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.emissiveTexture=null", 2);
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.emissiveTexture=null", 3);
                 }
 
 
@@ -79,7 +75,7 @@ namespace Max2Babylon
                 // --------- gltfMaterial ---------
                 // --------------------------------
 
-                RaiseMessage("GLTFExporter.Material | create gltfMaterial", 1);
+                RaiseMessage("GLTFExporter.Material | create gltfMaterial", 2);
                 var gltfMaterial = new GLTFMaterial
                 {
                     name = name
@@ -113,10 +109,11 @@ namespace Max2Babylon
                 // --- gltfPbrMetallicRoughness ---
                 // --------------------------------
 
-                RaiseMessage("GLTFExporter.Material | create gltfPbrMetallicRoughness", 1);
+                RaiseMessage("GLTFExporter.Material | create gltfPbrMetallicRoughness", 2);
                 var gltfPbrMetallicRoughness = new GLTFPBRMetallicRoughness();
                 gltfMaterial.pbrMetallicRoughness = gltfPbrMetallicRoughness;
 
+                // TODO - Retreive diffuse or albedo?
                 // Base color
                 var babylonDiffuseColor = babylonStandardMaterial.diffuse;
                 gltfPbrMetallicRoughness.baseColorFactor = new float[4]
@@ -127,7 +124,7 @@ namespace Max2Babylon
                     babylonMaterial.alpha
                 };
                 gltfPbrMetallicRoughness.baseColorTexture = ExportTexture(babylonStandardMaterial.diffuseTexture, gltf);
-
+                 
                 // TODO - Metallic roughness
                 gltfPbrMetallicRoughness.metallicFactor = 0; // Non metal
                 // TODO - roughnessFactor
@@ -137,13 +134,15 @@ namespace Max2Babylon
 
         private void getAlphaMode(BabylonStandardMaterial babylonMaterial, out string alphaMode, out float? alphaCutoff)
         {
-            if (babylonMaterial.diffuseTexture.hasAlpha)
+            if (babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.hasAlpha)
             {
+                // TODO - Babylon standard material is assumed to useAlphaFromDiffuseTexture. If not, the alpha mode is a mask.
                 alphaMode = GLTFMaterial.AlphaMode.BLEND.ToString();
             }
             else
             {
-                alphaMode = GLTFMaterial.AlphaMode.OPAQUE.ToString();
+                // glTF alpha mode default value is "OPAQUE"
+                alphaMode = null; // GLTFMaterial.AlphaMode.OPAQUE.ToString();
             }
             alphaCutoff = null;
         }

+ 273 - 188
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Mesh.cs

@@ -10,15 +10,15 @@ namespace Max2Babylon
 {
     partial class BabylonExporter
     {
-        private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode)
+        private GLTFNode ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene)
         {
-            RaiseMessage("GLTFExporter.Mesh | ExportMesh babylonMesh.name=" + babylonMesh.name, 1);
+            RaiseMessage("GLTFExporter.Mesh | Export mesh named: " + babylonMesh.name, 1);
 
             // --------------------------
             // ---------- Node ----------
             // --------------------------
 
-            RaiseMessage("GLTFExporter.Mesh | Node", 1);
+            RaiseMessage("GLTFExporter.Mesh | Node", 2);
             // Node
             var gltfNode = new GLTFNode();
             gltfNode.name = babylonMesh.name;
@@ -28,19 +28,22 @@ namespace Max2Babylon
             // Hierarchy
             if (gltfParentNode != null)
             {
-                RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 2);
+                RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 3);
                 gltfParentNode.ChildrenList.Add(gltfNode.index);
             }
             else
             {
                 // It's a root node
                 // Only root nodes are listed in a gltf scene
-                RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 2);
+                RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 3);
                 gltf.scenes[0].NodesList.Add(gltfNode.index);
             }
 
             // Transform
             gltfNode.translation = babylonMesh.position;
+            // TODO - Choose between this method and the extra root node
+            // Switch from left to right handed coordinate system
+            //gltfNode.translation[0] *= -1;
             if (babylonMesh.rotationQuaternion != null)
             {
                 gltfNode.rotation = babylonMesh.rotationQuaternion;
@@ -48,17 +51,13 @@ namespace Max2Babylon
             else
             {
                 // Convert rotation vector to quaternion
-                // TODO - Fix it
                 BabylonVector3 rotationVector3 = new BabylonVector3
                 {
                     X = babylonMesh.rotation[0],
                     Y = babylonMesh.rotation[1],
                     Z = babylonMesh.rotation[2]
                 };
-                gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
-
-                RaiseMessage("GLTFExporter.Mesh | rotationVector3=[" + rotationVector3.X + "; " + rotationVector3.Y + "; " + rotationVector3.Z + "]", 2);
-                RaiseMessage("GLTFExporter.Mesh | gltfNode.rotation=[" + gltfNode.rotation[0] + "; " + gltfNode.rotation[1] + "; " + gltfNode.rotation[2] + "; " + gltfNode.rotation[3] + "]", 2);
+                gltfNode.rotation = rotationVector3.toQuaternionGltf().ToArray();
             }
             gltfNode.scale = babylonMesh.scaling;
 
@@ -67,64 +66,73 @@ namespace Max2Babylon
             // --- Mesh from babylon ----
             // --------------------------
 
-            RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 1);
+            if (babylonMesh.positions == null)
+            {
+                RaiseMessage("GLTFExporter.Mesh | Mesh is a dummy", 2);
+                return gltfNode;
+            }
+
+            RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 2);
             // Retreive general data from babylon mesh
             int nbVertices = babylonMesh.positions.Length / 3;
             bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0;
             bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0;
             bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0;
 
-            RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 2);
-            RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 2);
-            RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 2);
-            RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 2);
+            RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 3);
+            RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 3);
+            RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 3);
+            RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 3);
 
             // Retreive vertices data from babylon mesh
             List<GLTFGlobalVertex> globalVertices = new List<GLTFGlobalVertex>();
-            for (int i = 0; i < nbVertices; i++)
+            for (int indexVertex = 0; indexVertex < nbVertices; indexVertex++)
             {
                 GLTFGlobalVertex globalVertex = new GLTFGlobalVertex();
-                globalVertex.Position = createIPoint3(babylonMesh.positions, i);
-                globalVertex.Normal = createIPoint3(babylonMesh.normals, i);
+                globalVertex.Position = createIPoint3(babylonMesh.positions, indexVertex);
+                // Switch from left to right handed coordinate system
+                //globalVertex.Position.X *= -1;
+                globalVertex.Normal = createIPoint3(babylonMesh.normals, indexVertex);
                 if (hasUV)
                 {
-                    globalVertex.UV = createIPoint2(babylonMesh.uvs, i);
+                    globalVertex.UV = createIPoint2(babylonMesh.uvs, indexVertex);
                     // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
                     // While for Babylon, it corresponds to the lower left corner of a texture image
                     globalVertex.UV.Y = 1 - globalVertex.UV.Y;
                 }
                 if (hasUV2)
                 {
-                    globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, i);
+                    globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, indexVertex);
                     // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
                     // While for Babylon, it corresponds to the lower left corner of a texture image
                     globalVertex.UV2.Y = 1 - globalVertex.UV2.Y;
                 }
                 if (hasColor)
                 {
-                    globalVertex.Color = createIPoint4(babylonMesh.colors, i).ToArray();
+                    globalVertex.Color = createIPoint4(babylonMesh.colors, indexVertex).ToArray();
                 }
 
                 globalVertices.Add(globalVertex);
             }
 
             // Retreive indices from babylon mesh
-            List<ushort> indices = new List<ushort>();
-            indices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
+            List<ushort> babylonIndices = new List<ushort>();
+            babylonIndices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
+            // For triangle primitives in gltf, the front face has a counter-clockwise (CCW) winding order
             // Swap face side
-            for (int i = 0; i < indices.Count; i += 3)
-            { 
-                var tmp = indices[i];
-                indices[i] = indices[i + 2];
-                indices[i+2] = tmp;
-            }
+            //for (int i = 0; i < babylonIndices.Count; i += 3)
+            //{
+            //    var tmp = babylonIndices[i];
+            //    babylonIndices[i] = babylonIndices[i + 2];
+            //    babylonIndices[i + 2] = tmp;
+            //}
 
 
             // --------------------------
             // ------- Init glTF --------
             // --------------------------
 
-            RaiseMessage("GLTFExporter.Mesh | Init glTF", 1);
+            RaiseMessage("GLTFExporter.Mesh | Init glTF", 2);
             // Mesh
             var gltfMesh = new GLTFMesh { name = babylonMesh.name };
             gltfMesh.index = gltf.MeshesList.Count;
@@ -132,15 +140,6 @@ namespace Max2Babylon
             gltfNode.mesh = gltfMesh.index;
             gltfMesh.gltfNode = gltfNode;
 
-            // MeshPrimitive
-            var meshPrimitives = new List<GLTFMeshPrimitive>();
-            var meshPrimitive = new GLTFMeshPrimitive
-            {
-                attributes = new Dictionary<string, int>(),
-                mode = GLTFMeshPrimitive.FillMode.TRIANGLES // TODO reteive info from babylon material
-            };
-            meshPrimitives.Add(meshPrimitive);
-
             // Buffer
             var buffer = new GLTFBuffer
             {
@@ -171,51 +170,8 @@ namespace Max2Babylon
             bufferViewFloatVec3.index = gltf.BufferViewsList.Count;
             gltf.BufferViewsList.Add(bufferViewFloatVec3);
 
-            // Accessor - Indices
-            var accessorIndices = new GLTFAccessor
-            {
-                name = "accessorIndices",
-                bufferView = bufferViewScalar.index,
-                BufferView = bufferViewScalar,
-                componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT,
-                type = GLTFAccessor.TypeEnum.SCALAR.ToString()
-            };
-            accessorIndices.index = gltf.AccessorsList.Count;
-            gltf.AccessorsList.Add(accessorIndices);
-            meshPrimitive.indices = accessorIndices.index;
-
-            // Accessor - Positions
-            var accessorPositions = new GLTFAccessor
-            {
-                name = "accessorPositions",
-                bufferView = bufferViewFloatVec3.index,
-                BufferView = bufferViewFloatVec3,
-                componentType = GLTFAccessor.ComponentType.FLOAT,
-                type = GLTFAccessor.TypeEnum.VEC3.ToString(),
-                min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue },
-                max = new float[] { float.MinValue, float.MinValue, float.MinValue }
-            };
-            accessorPositions.index = gltf.AccessorsList.Count;
-            gltf.AccessorsList.Add(accessorPositions);
-            meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
-
-            // Accessor - Normals
-            var accessorNormals = new GLTFAccessor
-            {
-                name = "accessorNormals",
-                bufferView = bufferViewFloatVec3.index,
-                BufferView = bufferViewFloatVec3,
-                componentType = GLTFAccessor.ComponentType.FLOAT,
-                type = GLTFAccessor.TypeEnum.VEC3.ToString()
-            };
-            accessorNormals.index = gltf.AccessorsList.Count;
-            gltf.AccessorsList.Add(accessorNormals);
-            meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
-
             // BufferView - Vector4
             GLTFBufferView bufferViewFloatVec4 = null;
-            // Accessor - Colors
-            GLTFAccessor accessorColors = null;
             if (hasColor)
             {
                 bufferViewFloatVec4 = new GLTFBufferView
@@ -228,23 +184,11 @@ namespace Max2Babylon
                 };
                 bufferViewFloatVec4.index = gltf.BufferViewsList.Count;
                 gltf.BufferViewsList.Add(bufferViewFloatVec4);
-
-                accessorColors = new GLTFAccessor
-                {
-                    name = "accessorColors",
-                    bufferView = bufferViewFloatVec4.index,
-                    BufferView = bufferViewFloatVec4,
-                    componentType = GLTFAccessor.ComponentType.FLOAT,
-                    type = GLTFAccessor.TypeEnum.VEC4.ToString()
-                };
-                accessorColors.index = gltf.AccessorsList.Count;
-                gltf.AccessorsList.Add(accessorColors);
-                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
             }
 
             // BufferView - Vector2
             GLTFBufferView bufferViewFloatVec2 = null;
-            if (hasUV ||hasUV2)
+            if (hasUV || hasUV2)
             {
                 bufferViewFloatVec2 = new GLTFBufferView
                 {
@@ -257,142 +201,283 @@ namespace Max2Babylon
                 gltf.BufferViewsList.Add(bufferViewFloatVec2);
             }
 
-            // Accessor - UV
-            GLTFAccessor accessorUVs = null;
-            if (hasUV)
+            // --------------------------
+            // ---- glTF primitives -----
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Mesh | glTF primitives", 2);
+            var meshPrimitives = new List<GLTFMeshPrimitive>();
+
+            // Global vertices are sorted per submesh
+            var globalVerticesSubMeshes = new List<List<GLTFGlobalVertex>>();
+
+            // In gltf, indices of each mesh primitive are 0-based (ie: min value is 0)
+            // Thus, the gltf indices list is a concatenation of sub lists all 0-based
+            // Example for 2 triangles, each being a submesh:
+            //      babylonIndices = {0,1,2, 3,4,5} gives as result gltfIndicies = {0,1,2, 0,1,2}
+            var gltfIndices = new List<ushort>();
+            
+            foreach (BabylonSubMesh babylonSubMesh in babylonMesh.subMeshes)
             {
-                accessorUVs = new GLTFAccessor
+                // --------------------------
+                // ------ SubMesh data ------
+                // --------------------------
+
+                List<GLTFGlobalVertex> globalVerticesSubMesh = globalVertices.GetRange(babylonSubMesh.verticesStart, babylonSubMesh.verticesCount);
+                globalVerticesSubMeshes.Add(globalVerticesSubMesh);
+
+                List<ushort> _indices = babylonIndices.GetRange(babylonSubMesh.indexStart, babylonSubMesh.indexCount);
+                // Indices of this submesh / primitive are updated to be 0-based
+                var minIndiceValue = _indices.Min(); // Should be equal to babylonSubMesh.indexStart
+                for (int indexIndice = 0; indexIndice < _indices.Count; indexIndice++)
+                {
+                    _indices[indexIndice] -= minIndiceValue;
+                }
+                gltfIndices.AddRange(_indices);
+
+                // --------------------------
+                // -- Init glTF primitive ---
+                // --------------------------
+
+                // MeshPrimitive
+                var meshPrimitive = new GLTFMeshPrimitive
+                {
+                    attributes = new Dictionary<string, int>()
+                };
+                meshPrimitives.Add(meshPrimitive);
+
+                // Accessor - Indices
+                var accessorIndices = new GLTFAccessor
                 {
-                    name = "accessorUVs",
-                    bufferView = bufferViewFloatVec2.index,
-                    BufferView = bufferViewFloatVec2,
+                    name = "accessorIndices",
+                    bufferView = bufferViewScalar.index,
+                    BufferView = bufferViewScalar,
+                    componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT,
+                    type = GLTFAccessor.TypeEnum.SCALAR.ToString()
+                };
+                accessorIndices.index = gltf.AccessorsList.Count;
+                gltf.AccessorsList.Add(accessorIndices);
+                meshPrimitive.indices = accessorIndices.index;
+
+                // Accessor - Positions
+                var accessorPositions = new GLTFAccessor
+                {
+                    name = "accessorPositions",
+                    bufferView = bufferViewFloatVec3.index,
+                    BufferView = bufferViewFloatVec3,
                     componentType = GLTFAccessor.ComponentType.FLOAT,
-                    type = GLTFAccessor.TypeEnum.VEC2.ToString()
+                    type = GLTFAccessor.TypeEnum.VEC3.ToString(),
+                    min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue },
+                    max = new float[] { float.MinValue, float.MinValue, float.MinValue }
                 };
-                accessorUVs.index = gltf.AccessorsList.Count;
-                gltf.AccessorsList.Add(accessorUVs);
-                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
-            }
+                accessorPositions.index = gltf.AccessorsList.Count;
+                gltf.AccessorsList.Add(accessorPositions);
+                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
 
-            // Accessor - UV2
-            GLTFAccessor accessorUV2s = null;
-            if (hasUV2)
-            {
-                accessorUV2s = new GLTFAccessor
+                // Accessor - Normals
+                var accessorNormals = new GLTFAccessor
                 {
-                    name = "accessorUV2s",
-                    bufferView = bufferViewFloatVec2.index,
-                    BufferView = bufferViewFloatVec2,
+                    name = "accessorNormals",
+                    bufferView = bufferViewFloatVec3.index,
+                    BufferView = bufferViewFloatVec3,
                     componentType = GLTFAccessor.ComponentType.FLOAT,
-                    type = GLTFAccessor.TypeEnum.VEC2.ToString()
+                    type = GLTFAccessor.TypeEnum.VEC3.ToString()
                 };
-                accessorUV2s.index = gltf.AccessorsList.Count;
-                gltf.AccessorsList.Add(accessorUV2s);
-                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
-            }
+                accessorNormals.index = gltf.AccessorsList.Count;
+                gltf.AccessorsList.Add(accessorNormals);
+                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
 
+                // Accessor - Colors
+                GLTFAccessor accessorColors = null;
+                if (hasColor)
+                {
+                    accessorColors = new GLTFAccessor
+                    {
+                        name = "accessorColors",
+                        bufferView = bufferViewFloatVec4.index,
+                        BufferView = bufferViewFloatVec4,
+                        componentType = GLTFAccessor.ComponentType.FLOAT,
+                        type = GLTFAccessor.TypeEnum.VEC4.ToString()
+                    };
+                    accessorColors.index = gltf.AccessorsList.Count;
+                    gltf.AccessorsList.Add(accessorColors);
+                    meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
+                }
 
-            // --------------------------
-            // ------ Mesh as glTF ------
-            // --------------------------
+                // Accessor - UV
+                GLTFAccessor accessorUVs = null;
+                if (hasUV)
+                {
+                    accessorUVs = new GLTFAccessor
+                    {
+                        name = "accessorUVs",
+                        bufferView = bufferViewFloatVec2.index,
+                        BufferView = bufferViewFloatVec2,
+                        componentType = GLTFAccessor.ComponentType.FLOAT,
+                        type = GLTFAccessor.TypeEnum.VEC2.ToString()
+                    };
+                    accessorUVs.index = gltf.AccessorsList.Count;
+                    gltf.AccessorsList.Add(accessorUVs);
+                    meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
+                }
 
-            RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 1);
-            // Material
-            //TODO - Handle multimaterials
-            GLTFMaterial gltfMaterial = gltf.MaterialsList.Find(material => material.id == babylonMesh.materialId);
-            if (gltfMaterial != null)
-            {
-                meshPrimitive.material = gltfMaterial.index;
-            }
+                // Accessor - UV2
+                GLTFAccessor accessorUV2s = null;
+                if (hasUV2)
+                {
+                    accessorUV2s = new GLTFAccessor
+                    {
+                        name = "accessorUV2s",
+                        bufferView = bufferViewFloatVec2.index,
+                        BufferView = bufferViewFloatVec2,
+                        componentType = GLTFAccessor.ComponentType.FLOAT,
+                        type = GLTFAccessor.TypeEnum.VEC2.ToString()
+                    };
+                    accessorUV2s.index = gltf.AccessorsList.Count;
+                    gltf.AccessorsList.Add(accessorUV2s);
+                    meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
+                }
 
-            // Update min and max vertex position for each component (X, Y, Z)
-            globalVertices.ForEach((globalVertex) =>
-            {
-                var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
-                for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++)
+                
+                // --------------------------
+                // - Update glTF primitive --
+                // --------------------------
+
+                RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 3);
+
+                // Material
+                if (babylonMesh.materialId != null)
                 {
-                    if (positionArray[indexComponent] < accessorPositions.min[indexComponent])
+                    // Retreive the babylon material
+                    var babylonMaterialId = babylonMesh.materialId;
+                    var babylonMaterials = new List<BabylonMaterial>(babylonScene.materials);
+                    var babylonMaterial = babylonMaterials.Find(_babylonMaterial => _babylonMaterial.id == babylonMaterialId);
+                    if (babylonMaterial == null)
                     {
-                        accessorPositions.min[indexComponent] = positionArray[indexComponent];
+                        // It's a multi material
+                        var babylonMultiMaterials = new List<BabylonMultiMaterial>(babylonScene.multiMaterials);
+                        var babylonMultiMaterial = babylonMultiMaterials.Find(_babylonMultiMaterial => _babylonMultiMaterial.id == babylonMesh.materialId);
+                        babylonMaterialId = babylonMultiMaterial.materials[babylonSubMesh.materialIndex];
+                        babylonMaterial = babylonMaterials.Find(_babylonMaterial => _babylonMaterial.id == babylonMaterialId);
                     }
-                    if (positionArray[indexComponent] > accessorPositions.max[indexComponent])
+
+                    // Update primitive material index
+                    var indexMaterial = babylonMaterialsToExport.FindIndex(_babylonMaterial => _babylonMaterial == babylonMaterial);
+                    if (indexMaterial == -1)
                     {
-                        accessorPositions.max[indexComponent] = positionArray[indexComponent];
+                        // Store material for exportation
+                        indexMaterial = babylonMaterialsToExport.Count;
+                        babylonMaterialsToExport.Add(babylonMaterial);
                     }
-                }
-            });
+                    meshPrimitive.material = indexMaterial;
 
-            // Update byte length and count of accessors, bufferViews and buffers
-            // Scalar
-            AddElementsToAccessor(accessorIndices, indices.Count);
-            // Vector3
-            bufferViewFloatVec3.byteOffset = buffer.byteLength;
-            AddElementsToAccessor(accessorPositions, globalVertices.Count);
-            AddElementsToAccessor(accessorNormals, globalVertices.Count);
-            // Vector4
-            if (hasColor)
-            {
-                bufferViewFloatVec4.byteOffset = buffer.byteLength;
-                AddElementsToAccessor(accessorColors, globalVertices.Count);
-            }
-            // Vector2
-            if (hasUV || hasUV2)
-            {
-                bufferViewFloatVec2.byteOffset = buffer.byteLength;
+                    // TODO - Add and retreive info from babylon material
+                    meshPrimitive.mode = GLTFMeshPrimitive.FillMode.TRIANGLES;
+                }
 
+                // Update min and max vertex position for each component (X, Y, Z)
+                globalVerticesSubMesh.ForEach((globalVertex) =>
+                {
+                    var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
+                    for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++)
+                    {
+                        if (positionArray[indexComponent] < accessorPositions.min[indexComponent])
+                        {
+                            accessorPositions.min[indexComponent] = positionArray[indexComponent];
+                        }
+                        if (positionArray[indexComponent] > accessorPositions.max[indexComponent])
+                        {
+                            accessorPositions.max[indexComponent] = positionArray[indexComponent];
+                        }
+                    }
+                });
+
+                // Update byte length and count of accessors, bufferViews and buffers
+                // Scalar
+                AddElementsToAccessor(accessorIndices, _indices.Count);
+                // Vector3
+                AddElementsToAccessor(accessorPositions, globalVerticesSubMesh.Count);
+                AddElementsToAccessor(accessorNormals, globalVerticesSubMesh.Count);
+                // Vector4
+                if (hasColor)
+                {
+                    AddElementsToAccessor(accessorColors, globalVerticesSubMesh.Count);
+                }
+                // Vector2
                 if (hasUV)
                 {
-                    AddElementsToAccessor(accessorUVs, globalVertices.Count);
+                    AddElementsToAccessor(accessorUVs, globalVerticesSubMesh.Count);
                 }
                 if (hasUV2)
                 {
-                    AddElementsToAccessor(accessorUV2s, globalVertices.Count);
+                    AddElementsToAccessor(accessorUV2s, globalVerticesSubMesh.Count);
                 }
             }
+            gltfMesh.primitives = meshPrimitives.ToArray();
+            
+            // Update byte offset of bufferViews
+            GLTFBufferView lastBufferView = null;
+            gltf.BufferViewsList.FindAll(bufferView => bufferView.buffer == buffer.index).ForEach(bufferView =>
+            {
+                if (lastBufferView != null)
+                {
+                    bufferView.byteOffset = lastBufferView.byteOffset + lastBufferView.byteLength;
+                }
+                lastBufferView = bufferView;
+            });
 
 
             // --------------------------
             // --------- Saving ---------
             // --------------------------
 
-            string outputBinaryFile = Path.Combine(gltf.OutputPath,  gltfMesh.name + ".bin");
-            RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 1);
+            string outputBinaryFile = Path.Combine(gltf.OutputPath, gltfMesh.name + ".bin");
+            RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 2);
 
+            // Write data to binary file
             using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create)))
             {
-                // Binary arrays
-                List<float> vertices = globalVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList();
-                List<float> normals = globalVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
+                // BufferView - Scalar
+                gltfIndices.ForEach(n => writer.Write(n));
 
-                List<float> colors = new List<float>();
-                if (hasColor)
+                // BufferView - Vector3
+                globalVerticesSubMeshes.ForEach(globalVerticesSubMesh =>
                 {
-                    colors = globalVertices.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
-                }
+                    List<float> vertices = globalVerticesSubMesh.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList();
+                    vertices.ForEach(n => writer.Write(n));
 
-                List<float> uvs = new List<float>();
-                if (hasUV)
-                {
-                    uvs = globalVertices.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
-                }
+                    List<float> normals = globalVerticesSubMesh.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
+                    normals.ForEach(n => writer.Write(n));
+                });
 
-                List<float> uvs2 = new List<float>();
-                if (hasUV2)
+                // BufferView - Vector4
+                globalVerticesSubMeshes.ForEach(globalVerticesSubMesh =>
                 {
-                    uvs2 = globalVertices.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
-                }
+                    if (hasColor)
+                    {
+                        List<float> colors = globalVerticesSubMesh.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
+                        colors.ForEach(n => writer.Write(n));
+                    }
+                });
 
-                // Write data to binary file
-                indices.ForEach(n => writer.Write(n));
-                vertices.ForEach(n => writer.Write(n));
-                normals.ForEach(n => writer.Write(n));
-                colors.ForEach(n => writer.Write(n));
-                uvs.ForEach(n => writer.Write(n));
+                // BufferView - Vector2
+                globalVerticesSubMeshes.ForEach(globalVerticesSubMesh =>
+                {
+                    if (hasUV)
+                    {
+                        List<float> uvs = globalVerticesSubMesh.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList();
+                        uvs.ForEach(n => writer.Write(n));
+                    }
+                    
+                    if (hasUV2)
+                    {
+                        List<float> uvs2 = globalVerticesSubMesh.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList();
+                        uvs2.ForEach(n => writer.Write(n));
+                    }
+                });
             }
 
-            gltfMesh.primitives = meshPrimitives.ToArray();
-
-            return gltfMesh;
+            return gltfNode;
         }
 
         private IPoint2 createIPoint2(float[] array, int index)

+ 4 - 4
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Texture.cs

@@ -12,13 +12,13 @@ namespace Max2Babylon
                 return null;
             }
 
-            RaiseMessage("GLTFExporter.Texture | ExportTexture babylonTexture.name=" + babylonTexture.name, 1);
+            RaiseMessage("GLTFExporter.Texture | Export texture named: " + babylonTexture.name, 1);
 
             // --------------------------
             // -------- Sampler ---------
             // --------------------------
 
-            RaiseMessage("GLTFExporter.Texture | create sampler", 1);
+            RaiseMessage("GLTFExporter.Texture | create sampler", 2);
             GLTFSampler gltfSampler = new GLTFSampler();
             gltfSampler.index = gltf.SamplersList.Count;
             gltf.SamplersList.Add(gltfSampler);
@@ -39,7 +39,7 @@ namespace Max2Babylon
             // --------- Image ----------
             // --------------------------
 
-            RaiseMessage("GLTFExporter.Texture | create image", 1);
+            RaiseMessage("GLTFExporter.Texture | create image", 2);
             GLTFImage gltfImage = new GLTFImage
             {
                 uri = babylonTexture.name
@@ -53,7 +53,7 @@ namespace Max2Babylon
             // -------- Texture ---------
             // --------------------------
 
-            RaiseMessage("GLTFExporter.Texture | create texture", 1);
+            RaiseMessage("GLTFExporter.Texture | create texture", 2);
             var gltfTexture = new GLTFTexture
             {
                 name = babylonTexture.name,

+ 93 - 34
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.cs

@@ -1,6 +1,7 @@
 using BabylonExport.Entities;
 using GLTFExport.Entities;
 using Newtonsoft.Json;
+using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
@@ -11,6 +12,9 @@ namespace Max2Babylon
 {
     internal partial class BabylonExporter
     {
+        List<BabylonMaterial> babylonMaterialsToExport;
+        GLTFNode gltfRootNode;
+
         public void ExportGltf(BabylonScene babylonScene, string outputFile, bool generateBinary)
         {
             RaiseMessage("GLTFExporter | Export outputFile=" + outputFile + " generateBinary=" + generateBinary);
@@ -36,30 +40,16 @@ namespace Max2Babylon
             GLTFScene[] scenes = { scene };
             gltf.scenes = scenes;
 
-            // Materials
-            RaiseMessage("GLTFExporter | Exporting materials");
-            ReportProgressChanged(10);
-            var babylonMaterials = babylonScene.MaterialsList;
-            babylonMaterials.ForEach((babylonMaterial) =>
-            {
-                ExportMaterial(babylonMaterial, gltf);
-                CheckCancelled();
-            });
-            // TODO - Handle multimaterials
-            RaiseMessage(string.Format("GLTFExporter | Total: {0}", gltf.MaterialsList.Count /*+ glTF.MultiMaterialsList.Count*/), Color.Gray, 1);
-
             // Nodes
-            List<BabylonNode> babylonNodes = new List<BabylonNode>();
-            babylonNodes.AddRange(babylonScene.meshes);
-            babylonNodes.AddRange(babylonScene.lights);
-            babylonNodes.AddRange(babylonScene.cameras);
+            List<BabylonNode> babylonNodes = getNodes(babylonScene);
 
             // Root nodes
-            RaiseMessage("GLTFExporter | Exporting root nodes");
+            RaiseMessage("GLTFExporter | Exporting nodes");
             List<BabylonNode> babylonRootNodes = babylonNodes.FindAll(node => node.parentId == null);
             var progressionStep = 80.0f / babylonRootNodes.Count;
-            var progression = 20.0f;
+            var progression = 10.0f;
             ReportProgressChanged((int)progression);
+            babylonMaterialsToExport = new List<BabylonMaterial>();
             babylonRootNodes.ForEach(babylonNode =>
             {
                 exportNodeRec(babylonNode, gltf, babylonScene);
@@ -68,15 +58,39 @@ namespace Max2Babylon
                 CheckCancelled();
             });
 
+            // TODO - Choose between this method and the reverse of X axis
+            // Switch from left to right handed coordinate system
+            var tmpNodesList = new List<int>(scene.NodesList);
+            var rootNode = new BabylonMesh
+            {
+                name = "root",
+                rotation = new float[] { 0, (float)Math.PI, 0 },
+                scaling = new float[] { 1, 1, -1 }
+            };
+            scene.NodesList.Clear();
+            gltfRootNode = ExportMesh(rootNode as BabylonMesh, gltf, null, babylonScene);
+            gltfRootNode.ChildrenList.AddRange(tmpNodesList);
+
+            // Materials
+            RaiseMessage("GLTFExporter | Exporting materials");
+            foreach (var babylonMaterial in babylonMaterialsToExport)
+            {
+                ExportMaterial(babylonMaterial, gltf);
+                CheckCancelled();
+            };
+            RaiseMessage(string.Format("GLTFExporter | Nb materials exported: {0}", gltf.MaterialsList.Count), Color.Gray, 1);
+
             // Output
             RaiseMessage("GLTFExporter | Saving to output file");
             // Cast lists to arrays
             gltf.Prepare();
-            var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings()); // Standard serializer, not the optimized one
+            var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings());
             var sb = new StringBuilder();
             var sw = new StringWriter(sb, CultureInfo.InvariantCulture);
 
-            using (var jsonWriter = new JsonTextWriter(sw))
+            // Do not use the optimized writer because it's not necessary to truncate values
+            // Use the bounded writer in case some values are infinity ()
+            using (var jsonWriter = new JsonTextWriterBounded(sw))
             {
                 jsonWriter.Formatting = Formatting.None;
                 jsonSerializer.Serialize(jsonWriter, gltf);
@@ -99,22 +113,25 @@ namespace Max2Babylon
             GLTFNode gltfNode = null; 
             if (babylonNode.GetType() == typeof(BabylonMesh))
             {
-                GLTFMesh gltfMesh = ExportMesh(babylonNode as BabylonMesh, gltf, gltfParentNode);
-                if (gltfMesh != null)
-                {
-                    gltfNode = gltfMesh.gltfNode;
-                }
+                gltfNode = ExportMesh(babylonNode as BabylonMesh, gltf, gltfParentNode, babylonScene);
             }
             else if (babylonNode.GetType() == typeof(BabylonCamera))
             {
-                // TODO - Export camera nodes
-                RaiseError($"TODO - Export camera node named {babylonNode.name}", 1);
+                GLTFCamera gltfCamera = ExportCamera(babylonNode as BabylonCamera, gltf, gltfParentNode);
+                gltfNode = gltfCamera.gltfNode;
             }
             else if (babylonNode.GetType() == typeof(BabylonLight))
             {
-                // TODO - Export light nodes as empty nodes (no lights in glTF 2.0 core)
-                RaiseError($"TODO - Export light node named {babylonNode.name}", 1);
-                RaiseWarning($"GLTFExporter.Node | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1);
+                if (isNodeRelevantToExport(babylonNode, babylonScene))
+                {
+                    // Export light nodes as empty nodes (no lights in glTF 2.0 core)
+                    RaiseWarning($"GLTFExporter | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1);
+                    gltfNode = ExportLight(babylonNode as BabylonLight, gltf, gltfParentNode);
+                }
+                else
+                {
+                    RaiseMessage($"GLTFExporter | Light named {babylonNode.name} is not relevant to export", 1);
+                }
             }
             else
             {
@@ -132,14 +149,56 @@ namespace Max2Babylon
             }
         }
 
-        private List<BabylonNode> getDescendants(BabylonNode babylonNode, BabylonScene babylonScene)
+        private List<BabylonNode> getNodes(BabylonScene babylonScene)
         {
             List<BabylonNode> babylonNodes = new List<BabylonNode>();
-            babylonNodes.AddRange(babylonScene.meshes);
-            babylonNodes.AddRange(babylonScene.lights);
-            babylonNodes.AddRange(babylonScene.cameras);
+            if (babylonScene.meshes != null)
+            {
+                babylonNodes.AddRange(babylonScene.meshes);
+            }
+            if (babylonScene.lights != null)
+            {
+                babylonNodes.AddRange(babylonScene.lights);
+            }
+            if (babylonScene.cameras != null)
+            {
+                babylonNodes.AddRange(babylonScene.cameras);
+            }
+            return babylonNodes;
+        }
 
+        private List<BabylonNode> getDescendants(BabylonNode babylonNode, BabylonScene babylonScene)
+        {
+            List<BabylonNode> babylonNodes = getNodes(babylonScene);
             return babylonNodes.FindAll(node => node.parentId == babylonNode.id);
         }
+
+        /// <summary>
+        /// Return true if node descendant hierarchy has any Mesh or Camera to export
+        /// </summary>
+        private bool isNodeRelevantToExport(BabylonNode babylonNode, BabylonScene babylonScene)
+        {
+            var type = babylonNode.GetType();
+            if (type == typeof(BabylonMesh) ||
+                type == typeof(BabylonCamera))
+            {
+                return true;
+            }
+
+            // Descandant recursivity
+            List<BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
+            int indexDescendant = 0;
+            while (indexDescendant < babylonDescendants.Count) // while instead of for to stop as soon as a relevant node has been found
+            {
+                if (isNodeRelevantToExport(babylonDescendants[indexDescendant], babylonScene))
+                {
+                    return true;
+                }
+                indexDescendant++;
+            }
+
+            // No relevant node found in hierarchy
+            return false;
+        }
     }
 }

+ 42 - 0
Exporters/3ds Max/Max2Babylon/2017/JsonTextWriterBounded.cs

@@ -0,0 +1,42 @@
+using Newtonsoft.Json;
+using System.IO;
+
+namespace Max2Babylon
+{
+    class JsonTextWriterBounded : JsonTextWriter
+    {
+        public JsonTextWriterBounded(TextWriter textWriter)
+            : base(textWriter)
+        {
+        }
+
+        public override void WriteValue(float value)
+        {
+            if (float.IsNegativeInfinity(value))
+            {
+                value = float.MinValue;
+            }
+            else if (float.IsPositiveInfinity(value))
+            {
+                value = float.MaxValue;
+            }
+            base.WriteValue(value);
+        }
+
+        public override void WriteValue(float? value)
+        {
+            if (value.HasValue)
+            {
+                if (float.IsNegativeInfinity(value.Value))
+                {
+                    value = float.MinValue;
+                }
+                else if (float.IsPositiveInfinity(value.Value))
+                {
+                    value = float.MaxValue;
+                }
+            }
+            base.WriteValue(value);
+        }
+    }
+}

+ 3 - 0
Exporters/3ds Max/Max2Babylon/2017/Max2Babylon2017.csproj

@@ -191,11 +191,14 @@
     <Compile Include="..\Tools\WebServer.cs">
       <Link>Tools\WebServer.cs</Link>
     </Compile>
+    <Compile Include="Exporter\BabylonExporter.GLTFExporter.Light.cs" />
     <Compile Include="Exporter\BabylonExporter.GLTFExporter.Material.cs" />
+    <Compile Include="Exporter\BabylonExporter.GLTFExporter.Camera.cs" />
     <Compile Include="Exporter\BabylonExporter.GLTFExporter.Texture.cs" />
     <Compile Include="Exporter\BabylonExporter.GLTFExporter.Mesh.cs" />
     <Compile Include="Exporter\BabylonExporter.GLTFExporter.cs" />
     <Compile Include="Exporter\GLTFGlobalVertex.cs" />
+    <Compile Include="JsonTextWriterBounded.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\Resources.Designer.cs">
       <AutoGen>True</AutoGen>

+ 16 - 6
Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.Designer.cs

@@ -13,13 +13,13 @@ namespace Max2Babylon.Properties {
     
     
     /// <summary>
-    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    ///   Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées.
     /// </summary>
-    // This class was auto-generated by the StronglyTypedResourceBuilder
-    // class via a tool like ResGen or Visual Studio.
-    // To add or remove a member, edit your .ResX file then rerun ResGen
-    // with the /str option, or rebuild your VS project.
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder
+    // à l'aide d'un outil, tel que ResGen ou Visual Studio.
+    // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen
+    // avec l'option /str ou régénérez votre projet VS.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
     internal class Resources {
@@ -59,5 +59,15 @@ namespace Max2Babylon.Properties {
                 resourceCulture = value;
             }
         }
+        
+        /// <summary>
+        ///   Recherche une ressource localisée de type System.Drawing.Bitmap.
+        /// </summary>
+        internal static System.Drawing.Bitmap Logo_Exporter_v3 {
+            get {
+                object obj = ResourceManager.GetObject("Logo_Exporter_v3", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
     }
 }

+ 4 - 0
Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.resx

@@ -117,4 +117,8 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="Logo_Exporter_v3" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Logo_Exporter_v3.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
 </root>

BIN
Exporters/3ds Max/Max2Babylon/2017/Resources/Logo_Exporter_v3.jpg


+ 19 - 6
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Camera.cs

@@ -56,15 +56,28 @@ namespace Max2Babylon
             babylonCamera.applyGravity = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity");
             babylonCamera.ellipsoid = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid");
 
-            // Position
-            var wm = cameraNode.GetLocalTM(0);
+            // Position / rotation
+            var localTM = cameraNode.GetObjectTM(0);
             if (cameraNode.NodeParent != null)
             {
                 var parentWorld = cameraNode.NodeParent.GetObjectTM(0);
-                wm.MultiplyBy(parentWorld.Inverse);
+                localTM.MultiplyBy(parentWorld.Inverse);
+            }
+
+            var position = localTM.Translation;
+            var rotation = localTM.Rotation;
+            var exportQuaternions = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportquaternions");
+
+            babylonCamera.position = new[] { position.X, position.Y, position.Z };
+
+            if (exportQuaternions)
+            {
+                babylonCamera.rotationQuaternion = new[] { rotation.X, rotation.Y, rotation.Z, -rotation.W };
+            }
+            else
+            {
+                babylonCamera.rotation = QuaternionToEulerAngles(rotation);
             }
-            var position = wm.Translation;
-            babylonCamera.position = new [] { position.X, position.Y, position.Z };
 
             // Target
             var target = gameCamera.CameraTarget;
@@ -74,7 +87,7 @@ namespace Max2Babylon
             }
             else
             {
-                var dir = wm.GetRow(3);
+                var dir = localTM.GetRow(3);
                 babylonCamera.target = new [] { position.X - dir.X, position.Y - dir.Y, position.Z - dir.Z };
             }
 

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

@@ -84,14 +84,14 @@ namespace Max2Babylon
             return parentId;
         }
 
-        private void RotationToEulerAngles(BabylonAbstractMesh babylonMesh, IQuat rotation)
+        private float[] QuaternionToEulerAngles(IQuat rotation)
         {
             float rotx = 0, roty = 0, rotz = 0;
             unsafe
             {
                 rotation.GetEuler(new IntPtr(&rotx), new IntPtr(&roty), new IntPtr(&rotz));
             }
-            babylonMesh.rotation = new[] { rotx, roty, rotz };
+            return new[] { rotx, roty, rotz };
         }
 
         private int bonesCount;
@@ -226,7 +226,7 @@ namespace Max2Babylon
             }
             else
             {
-                RotationToEulerAngles(babylonMesh, meshRotation);
+                babylonMesh.rotation = QuaternionToEulerAngles(meshRotation);
             }
 
             babylonMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z };
@@ -487,7 +487,7 @@ namespace Max2Babylon
                     }
                     else
                     {
-                        RotationToEulerAngles(instance, instanceRotation);
+                        instance.rotation = QuaternionToEulerAngles(instanceRotation);
                     }
 
                     instance.scaling = new[] { instanceScale.X, instanceScale.Y, instanceScale.Z };

+ 1 - 1
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs

@@ -309,7 +309,7 @@ namespace Max2Babylon
 
             // Output
             RaiseMessage("Saving to output file");
-            babylonScene.Prepare(false);
+            babylonScene.Prepare(false, false);
             var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings());
             var sb = new StringBuilder();
             var sw = new StringWriter(sb, CultureInfo.InvariantCulture);

+ 2 - 3
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.Designer.cs

@@ -28,7 +28,6 @@
         /// </summary>
         private void InitializeComponent()
         {
-            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExporterForm));
             this.butExport = new System.Windows.Forms.Button();
             this.label1 = new System.Windows.Forms.Label();
             this.txtFilename = new System.Windows.Forms.TextBox();
@@ -138,7 +137,7 @@
             // pictureBox2
             // 
             this.pictureBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-            this.pictureBox2.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox2.Image")));
+            this.pictureBox2.Image = global::Max2Babylon.Properties.Resources.Logo_Exporter_v3;
             this.pictureBox2.Location = new System.Drawing.Point(511, 12);
             this.pictureBox2.Name = "pictureBox2";
             this.pictureBox2.Size = new System.Drawing.Size(300, 130);
@@ -272,7 +271,7 @@
             this.chkGltf.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
             this.chkGltf.Location = new System.Drawing.Point(30, 133);
             this.chkGltf.Name = "chkGltf";
-            this.chkGltf.Size = new System.Drawing.Size(135, 17);
+            this.chkGltf.Size = new System.Drawing.Size(107, 17);
             this.chkGltf.TabIndex = 17;
             this.chkGltf.Text = "Generate glTF file";
             this.chkGltf.UseVisualStyleBackColor = true;

+ 0 - 197
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx

@@ -120,201 +120,4 @@
   <metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
-  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
-  <data name="pictureBox2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-    <value>
-        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
-</value>
-  </data>
 </root>

+ 1 - 1
Playground/frame.html

@@ -77,7 +77,7 @@
     <a class="link" id="link" href="#" target="_blank">Edit</a>
 
     <script src="https://code.jquery.com/jquery.js"></script>
-    <script src="frame.js"></script>
+    <script src="/js/frame.js"></script>
 </body>
 
 </html>

Playground/frame.js → Playground/js/frame.js


+ 158 - 0
Playground/js/perf.js

@@ -0,0 +1,158 @@
+(function () {
+    var snippetUrl = "https://babylonjs-api2.azurewebsites.net/snippets";
+    var currentSnippetToken;
+    var engine;
+    var scripts;
+    var zipCode;
+    BABYLON.Engine.ShadersRepository = "/src/Shaders/";
+    var loadScript = function (scriptURL, title) {
+        var xhr = new XMLHttpRequest();
+
+        xhr.open('GET', scriptURL, true);
+
+        xhr.onreadystatechange = function () {
+            if (xhr.readyState === 4) {
+                if (xhr.status === 200) {
+                    blockEditorChange = true;
+                    console.log(xhr.responseText);
+                    jsEditor.setValue(xhr.responseText);
+                    jsEditor.setPosition({ lineNumber: 0, column: 0 });
+                    blockEditorChange = false;
+                    compileAndRun();
+
+                    document.getElementById("currentScript").innerHTML = title;
+
+                    currentSnippetToken = null;
+                }
+            }
+        };
+
+        xhr.send(null);
+    };
+
+    var showError = function(error) {
+        console.warn(error);
+    };
+
+    compileAndRun = function (code) {
+        try {
+
+            if (!BABYLON.Engine.isSupported()) {
+                showError("Your browser does not support WebGL");
+                return;
+            }
+
+            if (engine) {
+                engine.dispose();
+                engine = null;
+            }
+
+            var canvas = document.getElementById("renderCanvas");
+            engine = new BABYLON.Engine(canvas, true, {stencil: true});
+            engine.renderEvenInBackground = false;
+            BABYLON.Camera.ForceAttachControlToAlwaysPreventDefault = true;
+
+            engine.runRenderLoop(function () {
+                if (engine.scenes.length === 0) {
+                    return;
+                }
+
+                if (canvas.width !== canvas.clientWidth) {
+                    engine.resize();
+                }
+
+                var scene = engine.scenes[0];
+
+                if (scene.activeCamera || scene.activeCameras.length > 0) {
+                    scene.render();
+                }
+            });
+
+            var scene;
+            if (code.indexOf("createScene") !== -1) { // createScene
+                eval(code);
+                scene = createScene();
+                if (!scene) {
+                    showError("createScene function must return a scene.");
+                    return;
+                }
+
+                zipCode = code + "\r\n\r\nvar scene = createScene();";
+            } else if (code.indexOf("CreateScene") !== -1) { // CreateScene
+                eval(code);
+                scene = CreateScene();
+                if (!scene) {
+                    showError("CreateScene function must return a scene.");
+                    return;
+                }
+
+                zipCode = code + "\r\n\r\nvar scene = CreateScene();";
+            } else if (code.indexOf("createscene") !== -1) { // createscene
+                eval(code);
+                scene = createscene();
+                if (!scene) {
+                    showError("createscene function must return a scene.");
+                    return;
+                }
+
+                zipCode = code + "\r\n\r\nvar scene = createscene();";
+            } else { // Direct code
+                scene = new BABYLON.Scene(engine);
+                eval("runScript = function(scene, canvas) {" + code + "}");
+                runScript(scene, canvas);
+
+                zipCode = "var scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
+            }
+
+        } catch (e) {
+            // showError(e.message);
+        }
+    };
+    window.addEventListener("resize", function () {
+        if (engine) {
+            engine.resize();
+        }
+    });
+
+    // UI
+
+    var cleanHash = function () {
+        var splits = decodeURIComponent(location.hash.substr(1)).split("#");
+
+        if (splits.length > 2) {
+            splits.splice(2, splits.length - 2);
+        }
+
+        location.hash = splits.join("#");
+    };
+
+    var checkHash = function () {
+        if (location.hash) {
+            cleanHash();
+
+            try {
+                var xmlHttp = new XMLHttpRequest();
+                xmlHttp.onreadystatechange = function () {
+                    if (xmlHttp.readyState === 4) {
+                        if (xmlHttp.status === 200) {
+                            var snippetCode = JSON.parse(JSON.parse(xmlHttp.responseText)[0].jsonPayload).code;
+                            compileAndRun(snippetCode);
+                        }
+                    }
+                };
+
+                var hash = location.hash.substr(1);
+                currentSnippetToken = hash.split("#")[0];
+                if(!hash.split("#")[1]) hash += "#0";
+
+                xmlHttp.open("GET", snippetUrl + "/" + hash.replace("#", "/"));
+                xmlHttp.send();
+            } catch (e) {
+
+            }
+        }
+    };
+
+    checkHash();
+
+})();

+ 77 - 0
Playground/perf.html

@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>Babylon.js Playground</title>
+    <link rel="shortcut icon" href="https://www.babylonjs.com/img/favicon/favicon.ico">
+	<link rel="apple-touch-icon" sizes="57x57" href="https://www.babylonjs.com/img/favicon/apple-icon-57x57.png">
+	<link rel="apple-touch-icon" sizes="60x60" href="https://www.babylonjs.com/img/favicon/apple-icon-60x60.png">
+	<link rel="apple-touch-icon" sizes="72x72" href="https://www.babylonjs.com/img/favicon/apple-icon-72x72.png">
+	<link rel="apple-touch-icon" sizes="76x76" href="https://www.babylonjs.com/img/favicon/apple-icon-76x76.png">
+	<link rel="apple-touch-icon" sizes="114x114" href="https://www.babylonjs.com/img/favicon/apple-icon-114x114.png">
+	<link rel="apple-touch-icon" sizes="120x120" href="https://www.babylonjs.com/img/favicon/apple-icon-120x120.png">
+	<link rel="apple-touch-icon" sizes="144x144" href="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png">
+	<link rel="apple-touch-icon" sizes="152x152" href="https://www.babylonjs.com/img/favicon/apple-icon-152x152.png">
+	<link rel="apple-touch-icon" sizes="180x180" href="https://www.babylonjs.com/img/favicon/apple-icon-180x180.png">
+	<link rel="icon" type="image/png" sizes="192x192"  href="https://www.babylonjs.com/img/favicon/android-icon-192x192.png">
+	<link rel="icon" type="image/png" sizes="32x32" href="https://www.babylonjs.com/img/favicon/favicon-32x32.png">
+	<link rel="icon" type="image/png" sizes="96x96" href="https://www.babylonjs.com/img/favicon/favicon-96x96.png">
+	<link rel="icon" type="image/png" sizes="16x16" href="https://www.babylonjs.com/img/favicon/favicon-16x16.png">
+	<link rel="manifest" href="https://www.babylonjs.com/img/favicon/manifest.json">
+	<meta name="msapplication-TileColor" content="#ffffff">
+	<meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
+	<meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
+	<meta name="theme-color" content="#ffffff">
+
+    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
+    <!-- Babylon.js -->
+    <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/Oimo.js"></script>
+    <script src="https://preview.babylonjs.com/babylon.js"></script>    
+    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
+
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.fireMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.waterMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.lavaMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.normalMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.skyMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.triPlanarMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.terrainMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.gradientMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.furMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.gridMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.shadowOnlyMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.customMaterial.min.js"></script>
+    <script src="https://preview.babylonjs.com/materialsLibrary/babylon.cellMaterial.min.js"></script>
+
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.brickProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.cloudProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.fireProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.grassProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.marbleProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.roadProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.starfieldProceduralTexture.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylon.woodProceduralTexture.min.js"></script>
+
+    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylon.asciiArtPostProcess.min.js"></script>
+    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylon.digitalRainPostProcess.min.js"></script>
+
+    <script src="https://preview.babylonjs.com/loaders/babylon.glTFFileLoader.js"></script>
+    <script src="https://preview.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
+    <script src="https://preview.babylonjs.com/loaders/babylon.stlFileLoader.js"></script>
+
+    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
+    
+    <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>    
+    <script src="https://rawgit.com/BabylonJS/Extensions/master/canvas2D/dist/preview%20release/babylon.canvas2d.min.js"></script>
+    <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js"></script>
+    <link href="frame.css" rel="stylesheet" />
+</head>
+
+<body>
+    <canvas touch-action="none" id="renderCanvas"></canvas>
+    <script src="https://code.jquery.com/jquery.js"></script>
+    <script src="/js/perf.js"></script>
+</body>
+
+</html>

+ 9 - 3
Tools/Gulp/config.json

@@ -68,6 +68,7 @@
                 "../../src/babylon.scene.js",
                 "../../src/Mesh/babylon.buffer.js",
                 "../../src/Mesh/babylon.vertexBuffer.js",
+                "../../src/Materials/Textures/babylon.internalTexture.js",
                 "../../src/Materials/Textures/babylon.baseTexture.js",
                 "../../src/Materials/Textures/babylon.texture.js",
                 "../../src/Mesh/babylon.mesh.js",
@@ -528,9 +529,14 @@
             "files": [
                 "../../src/Cameras/Inputs/babylon.freeCameraGamepadInput.js",
                 "../../src/Cameras/Inputs/babylon.arcRotateCameraGamepadInput.js",
-                "../../src/Tools/Gamepad/babylon.gamepadManager.js",
-                "../../src/Tools/Gamepad/babylon.gamepads.js",
-                "../../src/Tools/Gamepad/babylon.extendedGamepad.js"
+                "../../src/Gamepad/babylon.gamepadManager.js",
+                "../../src/Gamepad/babylon.gamepad.js",
+                "../../src/Gamepad/babylon.xboxGamepad.js",
+                "../../src/Gamepad/Controllers/babylon.poseEnabledController.js",
+                "../../src/Gamepad/Controllers/babylon.webVRController.js",
+                "../../src/Gamepad/Controllers/babylon.oculusTouchController.js",
+                "../../src/Gamepad/Controllers/babylon.viveController.js",
+                "../../src/Gamepad/Controllers/babylon.genericController.js"
             ],
             "dependUpon" : [
                 "core"

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1124 - 988
dist/preview release/babylon.d.ts


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 43 - 43
dist/preview release/babylon.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1228 - 522
dist/preview release/babylon.max.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1124 - 988
dist/preview release/babylon.module.d.ts


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 44 - 44
dist/preview release/babylon.worker.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 8229 - 8093
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 32 - 31
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1145 - 459
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 8229 - 8093
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 2 - 1
dist/preview release/gui/babylon.gui.js

@@ -238,6 +238,7 @@ var BABYLON;
                 }
                 // Render
                 context.font = "18px Arial";
+                context.strokeStyle = "white";
                 var measure = new GUI.Measure(0, 0, renderWidth, renderHeight);
                 this._rootContainer._draw(measure, context);
             };
@@ -663,7 +664,7 @@ var BABYLON;
                 this._fontSize = new GUI.ValueAndUnit(18, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
                 this._width = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
                 this._height = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
-                this._color = "white";
+                this._color = "";
                 this._horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
                 this._verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
                 this._isDirty = true;

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 263 - 263
dist/preview release/inspector/babylon.inspector.bundle.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -155,6 +155,7 @@ declare module BABYLON.GLTF1 {
         buffer: string;
         byteOffset: number;
         byteLength: number;
+        byteStride: number;
         target?: number;
     }
     interface IGLTFBuffer extends IGLTFChildRootProperty {

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


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

@@ -344,7 +344,14 @@ declare module BABYLON.GLTF2 {
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
-        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, componentType, onSuccess);
+        private _buildInt8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildInt16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, stride, length);
+        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
         blockPendingTracking: boolean;

+ 79 - 12
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -1012,7 +1012,73 @@ var BABYLON;
                     });
                 }
             };
-            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, componentType, onSuccess) {
+            GLTFLoader.prototype._buildInt8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int8Array(buffer, byteOffset);
+                var targetBuffer = new Int8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint8Array(buffer, byteOffset);
+                var targetBuffer = new Uint8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildInt16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int16Array(buffer, byteOffset);
+                var targetBuffer = new Int16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint16Array(buffer, byteOffset);
+                var targetBuffer = new Uint16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint32Array(buffer, byteOffset);
+                var targetBuffer = new Uint32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildFloat32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Float32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Float32Array(buffer, byteOffset);
+                var targetBuffer = new Float32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._extractInterleavedData = function (sourceBuffer, targetBuffer, bytePerComponent, stride, length) {
+                var tempIndex = 0;
+                var sourceIndex = 0;
+                var storageSize = bytePerComponent;
+                while (tempIndex < length) {
+                    for (var cursor = 0; cursor < storageSize; cursor++) {
+                        targetBuffer[tempIndex] = sourceBuffer[sourceIndex + cursor];
+                        tempIndex++;
+                    }
+                    sourceIndex += stride;
+                }
+            };
+            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess) {
                 var _this = this;
                 byteOffset += (bufferView.byteOffset || 0);
                 this._loadBufferAsync(bufferView.buffer, function (bufferData) {
@@ -1025,22 +1091,22 @@ var BABYLON;
                     var bufferViewData;
                     switch (componentType) {
                         case GLTF2.EComponentType.BYTE:
-                            bufferViewData = new Int8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.UNSIGNED_BYTE:
-                            bufferViewData = new Uint8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.SHORT:
-                            bufferViewData = new Int16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.UNSIGNED_SHORT:
-                            bufferViewData = new Uint16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.UNSIGNED_INT:
-                            bufferViewData = new Uint32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.FLOAT:
-                            bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         default:
                             _this._onError("Invalid component type (" + componentType + ")");
@@ -1052,8 +1118,9 @@ var BABYLON;
             GLTFLoader.prototype._loadAccessorAsync = function (accessor, onSuccess) {
                 var bufferView = this._gltf.bufferViews[accessor.bufferView];
                 var byteOffset = accessor.byteOffset || 0;
-                var byteLength = accessor.count * this._getByteStrideFromType(accessor);
-                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
+                var bytePerComponent = this._getByteStrideFromType(accessor);
+                var byteLength = accessor.count * bytePerComponent;
+                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, accessor.componentType, onSuccess);
             };
             GLTFLoader.prototype._getByteStrideFromType = function (accessor) {
                 switch (accessor.type) {
@@ -1182,7 +1249,7 @@ var BABYLON;
                 if (material.normalTexture) {
                     babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
                     babylonMaterial.invertNormalMapX = true;
-                    babylonMaterial.invertNormalMapY = true;
+                    babylonMaterial.invertNormalMapY = false;
                     if (material.normalTexture.scale !== undefined) {
                         babylonMaterial.bumpTexture.level = material.normalTexture.scale;
                     }
@@ -1265,7 +1332,7 @@ var BABYLON;
                 };
                 if (!source.uri) {
                     var bufferView = this._gltf.bufferViews[source.bufferView];
-                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
+                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, 1, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(source.uri)) {
                     setTextureData(new Uint8Array(GLTF2.GLTFUtils.DecodeBase64(source.uri)));
@@ -1341,7 +1408,7 @@ var BABYLON;
                 // Set defaults if undefined
                 mode = mode === undefined ? GLTF2.ETextureWrapMode.REPEAT : mode;
                 switch (mode) {
-                    case GLTF2.ETextureWrapMode.CLAMP_TO_EDGE: BABYLON.Texture.CLAMP_ADDRESSMODE;
+                    case GLTF2.ETextureWrapMode.CLAMP_TO_EDGE: return BABYLON.Texture.CLAMP_ADDRESSMODE;
                     case GLTF2.ETextureWrapMode.MIRRORED_REPEAT: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case GLTF2.ETextureWrapMode.REPEAT: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     default:

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


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

@@ -155,6 +155,7 @@ declare module BABYLON.GLTF1 {
         buffer: string;
         byteOffset: number;
         byteLength: number;
+        byteStride: number;
         target?: number;
     }
     interface IGLTFBuffer extends IGLTFChildRootProperty {
@@ -839,7 +840,14 @@ declare module BABYLON.GLTF2 {
         private _loadAnimations();
         private _loadAnimationChannel(animation, animationIndex, channelIndex);
         private _loadBufferAsync(index, onSuccess);
-        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, componentType, onSuccess);
+        private _buildInt8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint8ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildInt16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint16ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildUint32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, byteStride, bytePerComponent);
+        private _extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, stride, length);
+        private _loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess);
         private _loadAccessorAsync(accessor, onSuccess);
         private _getByteStrideFromType(accessor);
         blockPendingTracking: boolean;

+ 79 - 12
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3170,7 +3170,73 @@ var BABYLON;
                     });
                 }
             };
-            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, componentType, onSuccess) {
+            GLTFLoader.prototype._buildInt8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int8Array(buffer, byteOffset);
+                var targetBuffer = new Int8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint8ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint8Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint8Array(buffer, byteOffset);
+                var targetBuffer = new Uint8Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildInt16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Int16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Int16Array(buffer, byteOffset);
+                var targetBuffer = new Int16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint16ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint16Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint16Array(buffer, byteOffset);
+                var targetBuffer = new Uint16Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildUint32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Uint32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Uint32Array(buffer, byteOffset);
+                var targetBuffer = new Uint32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._buildFloat32ArrayBuffer = function (buffer, byteOffset, byteLength, byteStride, bytePerComponent) {
+                if (!byteStride) {
+                    return new Float32Array(buffer, byteOffset, byteLength);
+                }
+                var sourceBuffer = new Float32Array(buffer, byteOffset);
+                var targetBuffer = new Float32Array(byteLength);
+                this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+                return targetBuffer;
+            };
+            GLTFLoader.prototype._extractInterleavedData = function (sourceBuffer, targetBuffer, bytePerComponent, stride, length) {
+                var tempIndex = 0;
+                var sourceIndex = 0;
+                var storageSize = bytePerComponent;
+                while (tempIndex < length) {
+                    for (var cursor = 0; cursor < storageSize; cursor++) {
+                        targetBuffer[tempIndex] = sourceBuffer[sourceIndex + cursor];
+                        tempIndex++;
+                    }
+                    sourceIndex += stride;
+                }
+            };
+            GLTFLoader.prototype._loadBufferViewAsync = function (bufferView, byteOffset, byteLength, bytePerComponent, componentType, onSuccess) {
                 var _this = this;
                 byteOffset += (bufferView.byteOffset || 0);
                 this._loadBufferAsync(bufferView.buffer, function (bufferData) {
@@ -3183,22 +3249,22 @@ var BABYLON;
                     var bufferViewData;
                     switch (componentType) {
                         case GLTF2.EComponentType.BYTE:
-                            bufferViewData = new Int8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.UNSIGNED_BYTE:
-                            bufferViewData = new Uint8Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.SHORT:
-                            bufferViewData = new Int16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildInt16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.UNSIGNED_SHORT:
-                            bufferViewData = new Uint16Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.UNSIGNED_INT:
-                            bufferViewData = new Uint32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildUint32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         case GLTF2.EComponentType.FLOAT:
-                            bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
+                            bufferViewData = _this._buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                             break;
                         default:
                             _this._onError("Invalid component type (" + componentType + ")");
@@ -3210,8 +3276,9 @@ var BABYLON;
             GLTFLoader.prototype._loadAccessorAsync = function (accessor, onSuccess) {
                 var bufferView = this._gltf.bufferViews[accessor.bufferView];
                 var byteOffset = accessor.byteOffset || 0;
-                var byteLength = accessor.count * this._getByteStrideFromType(accessor);
-                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
+                var bytePerComponent = this._getByteStrideFromType(accessor);
+                var byteLength = accessor.count * bytePerComponent;
+                this._loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, accessor.componentType, onSuccess);
             };
             GLTFLoader.prototype._getByteStrideFromType = function (accessor) {
                 switch (accessor.type) {
@@ -3340,7 +3407,7 @@ var BABYLON;
                 if (material.normalTexture) {
                     babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
                     babylonMaterial.invertNormalMapX = true;
-                    babylonMaterial.invertNormalMapY = true;
+                    babylonMaterial.invertNormalMapY = false;
                     if (material.normalTexture.scale !== undefined) {
                         babylonMaterial.bumpTexture.level = material.normalTexture.scale;
                     }
@@ -3423,7 +3490,7 @@ var BABYLON;
                 };
                 if (!source.uri) {
                     var bufferView = this._gltf.bufferViews[source.bufferView];
-                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
+                    this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, 1, GLTF2.EComponentType.UNSIGNED_BYTE, setTextureData);
                 }
                 else if (GLTF2.GLTFUtils.IsBase64(source.uri)) {
                     setTextureData(new Uint8Array(GLTF2.GLTFUtils.DecodeBase64(source.uri)));
@@ -3499,7 +3566,7 @@ var BABYLON;
                 // Set defaults if undefined
                 mode = mode === undefined ? GLTF2.ETextureWrapMode.REPEAT : mode;
                 switch (mode) {
-                    case GLTF2.ETextureWrapMode.CLAMP_TO_EDGE: BABYLON.Texture.CLAMP_ADDRESSMODE;
+                    case GLTF2.ETextureWrapMode.CLAMP_TO_EDGE: return BABYLON.Texture.CLAMP_ADDRESSMODE;
                     case GLTF2.ETextureWrapMode.MIRRORED_REPEAT: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case GLTF2.ETextureWrapMode.REPEAT: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     default:

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


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

@@ -1,16 +1,19 @@
 # 3.1.0:
 
 ## Major updates
+- Added support for webgl context lost and restored events. (Doc here)[http://doc.babylonjs.com/tutorials/optimizing_your_scene#handling-webgl-context-lost] ([deltakosh](https://github.com/deltakosh))
 - Added support for non-pow2 textures when in WebGL2 mode ([deltakosh](https://github.com/deltakosh))
 - Engine can now be initialized with an existing webgl context ([deltakosh](https://github.com/deltakosh))
 - Introduced behaviors. (Doc here)[http://doc.babylonjs.com/overviews/behaviors] ([deltakosh](https://github.com/deltakosh))
+- Added support for WebGL Occlusion queries. (Doc here)[http://doc.babylonjs.com/overviews/occlusionquery] ([Ibraheem Osama](https://github.com/IbraheemOsama))
 - New behaviors for ArcRotateCamera:
  - AutoRotation ([deltakosh](https://github.com/deltakosh))
  - Framing ([deltakosh](https://github.com/deltakosh))
  - Bouncing ([deltakosh](https://github.com/deltakosh))
 
 ## Updates
-- POW2 textures rescale is now done by shaders (It was done using canvas before) ([deltakosh](https://github.com/deltakosh))
+- New `camera.storeState()` and `camera.restoreState()` functions to store / restore cameras position / rotation / fov. (Doc here)[http://doc.babylonjs.com/tutorials/cameras#state] ([deltakosh](https://github.com/deltakosh))
+- POW2 textures rescale is now done by shaders (It was previously done using canvas) ([deltakosh](https://github.com/deltakosh))
 - Added `SceneLoader.CleanBoneMatrixWeights` to force the loader to normalize matrix weights when loading bones (off by default) ([deltakosh](https://github.com/deltakosh)) 
 - Added `camera.onViewMatrixChangedObservable` and `camera.onProjectionMatrixChangedObservable` ([deltakosh](https://github.com/deltakosh))
 - Added support for folders when drag'n'dropping into the sandbox ([deltakosh](https://github.com/deltakosh))
@@ -19,6 +22,10 @@
 - GUI: Added support for pointer move events on projected UI ([deltakosh](https://github.com/deltakosh))
 - Normals are generated automatically by StandardMaterial if meshes do not have normals ([deltakosh](https://github.com/deltakosh))
 - Added `mesh.onMaterialChangedObservable` to notify when a new material is set ([deltakosh](https://github.com/deltakosh))
+- Improved the SPS perfs for dead or invisible solid particles ([jerome](https://github.com/jbousquie))  
 
 ## Bug fixes
 - Fixed a bug with PBR on iOS ([sebavan](https://github.com/sebavan))
+
+## Breaking changes
+- `Gamepads` was removed in favor of `scene.gamepadManager`

+ 1 - 0
gui/src/advancedDynamicTexture.ts

@@ -256,6 +256,7 @@ module BABYLON.GUI {
 
             // Render
             context.font = "18px Arial";
+            context.strokeStyle = "white";
             var measure = new Measure(0, 0, renderWidth, renderHeight);
             this._rootContainer._draw(measure, context);
         }

+ 1 - 1
gui/src/controls/control.ts

@@ -15,7 +15,7 @@ module BABYLON.GUI {
         public _height = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
         private _lastMeasuredFont: string;
         protected _fontOffset: {ascent: number, height: number, descent: number};
-        private _color = "white";
+        private _color = "";
         protected _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         protected _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
         private _isDirty = true;

+ 1 - 0
loaders/src/glTF/1.0/babylon.glTFLoaderInterfaces.ts

@@ -117,6 +117,7 @@ module BABYLON.GLTF1 {
         buffer: string;
         byteOffset: number;
         byteLength: number;
+        byteStride: number;
 
         target?: number;
     }

+ 104 - 11
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -800,7 +800,99 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _loadBufferViewAsync(bufferView: IGLTFBufferView, byteOffset: number, byteLength: number, componentType: EComponentType, onSuccess: (data: ArrayBufferView) => void): void {
+        private _buildInt8ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Int8Array {
+            if (!byteStride) {
+                return new Int8Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Int8Array(buffer, byteOffset);
+            let targetBuffer = new Int8Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+
+            return targetBuffer;              
+        }
+
+        private _buildUint8ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint8Array {
+            if (!byteStride) {
+                return new Uint8Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Uint8Array(buffer, byteOffset);
+            let targetBuffer = new Uint8Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride, targetBuffer.length);
+
+            return targetBuffer;              
+        }        
+
+        private _buildInt16ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Int16Array {
+            if (!byteStride) {
+                return new Int16Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Int16Array(buffer, byteOffset);
+            let targetBuffer = new Int16Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+
+            return targetBuffer;             
+        }   
+
+        private _buildUint16ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint16Array {
+            if (!byteStride) {
+                return new Uint16Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Uint16Array(buffer, byteOffset);
+            let targetBuffer = new Uint16Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 2, targetBuffer.length);
+
+            return targetBuffer;             
+        }          
+        
+        private _buildUint32ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Uint32Array {
+            if (!byteStride) {
+                return new Uint32Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Uint32Array(buffer, byteOffset);
+            let targetBuffer = new Uint32Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+
+            return targetBuffer;            
+        }     
+        
+        private _buildFloat32ArrayBuffer(buffer: ArrayBuffer, byteOffset: number, byteLength: number, byteStride: number, bytePerComponent: number): Float32Array {
+            if (!byteStride) {
+                return new Float32Array(buffer, byteOffset, byteLength);
+            }
+
+            let sourceBuffer = new Float32Array(buffer, byteOffset);
+            let targetBuffer = new Float32Array(byteLength);
+
+            this._extractInterleavedData(sourceBuffer, targetBuffer, bytePerComponent, byteStride / 4, targetBuffer.length);
+
+            return targetBuffer;
+        }    
+        
+        private _extractInterleavedData(sourceBuffer: ArrayBufferView, targetBuffer: ArrayBufferView, bytePerComponent: number, stride: number, length: number): void {
+            let tempIndex = 0;
+            let sourceIndex = 0;            
+            let storageSize = bytePerComponent;
+
+            while (tempIndex < length) {
+                for (var cursor = 0; cursor < storageSize; cursor++) {
+                    targetBuffer[tempIndex] = sourceBuffer[sourceIndex + cursor]
+                    tempIndex++;
+                }
+                sourceIndex += stride;
+            }
+        }
+
+        private _loadBufferViewAsync(bufferView: IGLTFBufferView, byteOffset: number, byteLength: number, bytePerComponent: number, componentType: EComponentType, onSuccess: (data: ArrayBufferView) => void): void {
             byteOffset += (bufferView.byteOffset || 0);
 
             this._loadBufferAsync(bufferView.buffer, bufferData => {
@@ -815,22 +907,22 @@ module BABYLON.GLTF2 {
                 var bufferViewData;
                 switch (componentType) {
                     case EComponentType.BYTE:
-                        bufferViewData = new Int8Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildInt8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                     case EComponentType.UNSIGNED_BYTE:
-                        bufferViewData = new Uint8Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildUint8ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                     case EComponentType.SHORT:
-                        bufferViewData = new Int16Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildInt16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                     case EComponentType.UNSIGNED_SHORT:
-                        bufferViewData = new Uint16Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildUint16ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                     case EComponentType.UNSIGNED_INT:
-                        bufferViewData = new Uint32Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildUint32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                     case EComponentType.FLOAT:
-                        bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
+                        bufferViewData = this._buildFloat32ArrayBuffer(buffer, byteOffset, byteLength, bufferView.byteStride, bytePerComponent);
                         break;
                     default:
                         this._onError("Invalid component type (" + componentType + ")");
@@ -844,8 +936,9 @@ module BABYLON.GLTF2 {
         private _loadAccessorAsync(accessor: IGLTFAccessor, onSuccess: (data: ArrayBufferView) => void): void {
             var bufferView = this._gltf.bufferViews[accessor.bufferView];
             var byteOffset = accessor.byteOffset || 0;
-            var byteLength = accessor.count * this._getByteStrideFromType(accessor);
-            this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
+            let bytePerComponent = this._getByteStrideFromType(accessor);
+            var byteLength = accessor.count * bytePerComponent;
+            this._loadBufferViewAsync(bufferView, byteOffset, byteLength, bytePerComponent, accessor.componentType, onSuccess);
         }
 
         private _getByteStrideFromType(accessor: IGLTFAccessor): number {
@@ -996,7 +1089,7 @@ module BABYLON.GLTF2 {
             if (material.normalTexture) {
                 babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
                 babylonMaterial.invertNormalMapX = true;
-                babylonMaterial.invertNormalMapY = true;
+                babylonMaterial.invertNormalMapY = false;
                 if (material.normalTexture.scale !== undefined) {
                     babylonMaterial.bumpTexture.level = material.normalTexture.scale;
                 }
@@ -1091,7 +1184,7 @@ module BABYLON.GLTF2 {
 
             if (!source.uri) {
                 var bufferView = this._gltf.bufferViews[source.bufferView];
-                this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE, setTextureData);
+                this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, 1, EComponentType.UNSIGNED_BYTE, setTextureData);
             }
             else if (GLTFUtils.IsBase64(source.uri)) {
                 setTextureData(new Uint8Array(GLTFUtils.DecodeBase64(source.uri)));

+ 1 - 1
loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts

@@ -40,7 +40,7 @@ module BABYLON.GLTF2 {
             mode = mode === undefined ? ETextureWrapMode.REPEAT : mode;
 
             switch (mode) {
-                case ETextureWrapMode.CLAMP_TO_EDGE: Texture.CLAMP_ADDRESSMODE;
+                case ETextureWrapMode.CLAMP_TO_EDGE: return Texture.CLAMP_ADDRESSMODE;
                 case ETextureWrapMode.MIRRORED_REPEAT: return Texture.MIRROR_ADDRESSMODE;
                 case ETextureWrapMode.REPEAT: return Texture.WRAP_ADDRESSMODE;
                 default:

+ 9 - 5
localDev/index.html

@@ -7,6 +7,7 @@
     <script src="https://babylonjs.azurewebsites.net/Oimo.js"></script>
 	<script src="../assets/refs/dat.gui.min.js"></script>
 	<script src="../tools/DevLoader/BabylonLoader.js"></script>
+	<script src="src/webgl-debug.js"></script>
 
 	<style>
 		html, body {
@@ -42,6 +43,7 @@
 	
 	<script>
 		var canvas = document.getElementById("renderCanvas");
+		// canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
 		var divFps = document.getElementById("fps");
 
 		// Global to simulate PG.
@@ -68,11 +70,13 @@
 					// call the scene creation from the js.
 					var scene = createScene();
 
-					// Register a render loop to repeatedly render the scene
-					engine.runRenderLoop(function () {
-						scene.render();
-						divFps.innerHTML = engine.getFps().toFixed() + " fps";
-					});
+					if (scene) {
+						// Register a render loop to repeatedly render the scene
+						engine.runRenderLoop(function () {
+							scene.render();
+							divFps.innerHTML = engine.getFps().toFixed() + " fps";
+						});
+					}
 
 					// Resize
 					window.addEventListener("resize", function () {

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1191 - 0
localDev/src/webgl-debug.js


+ 4 - 3
src/Behaviors/Cameras/babylon.autoRotationBehavior.ts

@@ -98,7 +98,7 @@ module BABYLON {
 
             this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {      
                 let now = Tools.Now;
-                let dt = 16;
+                let dt = 0;
                 if (this._lastFrameTime != null) {
                     dt =  now - this._lastFrameTime;
                 }
@@ -116,11 +116,12 @@ module BABYLON {
             });
         }
              
-        public detach(camera: ArcRotateCamera): void {
+        public detach(): void {
             let scene = this._attachedCamera.getScene();
             
             scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
-            camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+			this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+			this._attachedCamera = null;
 		}
 
 		/**

+ 4 - 3
src/Behaviors/Cameras/babylon.bouncingBehavior.ts

@@ -90,11 +90,12 @@ module BABYLON {
             });
         }
         
-        public detach(camera: ArcRotateCamera): void {
-			camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+        public detach(): void {
+			this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
 			if (this._onMeshTargetChangedObserver) {
-				camera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
+				this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
 			}
+			this._attachedCamera = null;
         }
 
         // Animations

+ 5 - 3
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -181,12 +181,14 @@ module BABYLON {
             });
         }
              
-        public detach(camera: ArcRotateCamera): void {
+        public detach(): void {
             let scene = this._attachedCamera.getScene();
             
             scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
-			camera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
-			camera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
+			this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
+			this._attachedCamera.onMeshTargetChangedObservable.remove(this._onMeshTargetChangedObserver);
+
+			this._attachedCamera = null;
         }
 
         // Framing control

+ 1 - 1
src/Behaviors/babylon.behavior.ts

@@ -3,6 +3,6 @@ module BABYLON {
         name: string;
 
         attach(node: T): void;
-        detach(node: T): void;
+        detach(): void;
     }
 }

+ 37 - 7
src/Cameras/Inputs/babylon.arcRotateCameraKeyboardMoveInput.ts

@@ -20,15 +20,26 @@ module BABYLON {
         @serialize()
         public keysRight = [39];
 
+        @serialize()
+        public keysReset = [111];        
+
+        @serialize()
+        public panningSensibility: number = 50.0;        
+
+        private _ctrlPressed: boolean;
+
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             element.tabIndex = 1;
 
             this._onKeyDown = evt => {
 
+                this._ctrlPressed = evt.ctrlKey;
+
                 if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     this.keysDown.indexOf(evt.keyCode) !== -1 ||
                     this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    this.keysRight.indexOf(evt.keyCode) !== -1) {
+                    this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                    this.keysReset.indexOf(evt.keyCode) !== -1) {
                     var index = this._keys.indexOf(evt.keyCode);
 
                     if (index === -1) {
@@ -44,11 +55,12 @@ module BABYLON {
             };
 
             this._onKeyUp = evt => {
-
+                
                 if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
                     this.keysDown.indexOf(evt.keyCode) !== -1 ||
                     this.keysLeft.indexOf(evt.keyCode) !== -1 ||
-                    this.keysRight.indexOf(evt.keyCode) !== -1) {
+                    this.keysRight.indexOf(evt.keyCode) !== -1 ||
+                    this.keysReset.indexOf(evt.keyCode) !== -1) {
                     var index = this._keys.indexOf(evt.keyCode);
 
                     if (index >= 0) {
@@ -111,13 +123,31 @@ module BABYLON {
                 for (var index = 0; index < this._keys.length; index++) {
                     var keyCode = this._keys[index];
                     if (this.keysLeft.indexOf(keyCode) !== -1) {
-                        camera.inertialAlphaOffset -= 0.01;
+                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                            camera.inertialPanningX -= 1 / this.panningSensibility;
+                        } else {
+                            camera.inertialAlphaOffset -= 0.01;
+                        }
                     } else if (this.keysUp.indexOf(keyCode) !== -1) {
-                        camera.inertialBetaOffset -= 0.01;
+                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                            camera.inertialPanningY += 1 / this.panningSensibility;
+                        } else {
+                            camera.inertialBetaOffset -= 0.01;
+                        }
                     } else if (this.keysRight.indexOf(keyCode) !== -1) {
-                        camera.inertialAlphaOffset += 0.01;
+                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                            camera.inertialPanningX += 1 / this.panningSensibility;
+                        } else {                        
+                            camera.inertialAlphaOffset += 0.01;
+                        }
                     } else if (this.keysDown.indexOf(keyCode) !== -1) {
-                        camera.inertialBetaOffset += 0.01;
+                        if (this._ctrlPressed && this.camera._useCtrlForPanning) {
+                            camera.inertialPanningY -= 1 / this.panningSensibility;
+                        } else {
+                            camera.inertialBetaOffset += 0.01;
+                        }
+                    } else if (this.keysReset.indexOf(keyCode) !== -1) {
+                        camera.restoreState();
                     }
                 }
             }

+ 8 - 7
src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts

@@ -51,7 +51,6 @@ module BABYLON {
                         //Nothing to do with the error. Execution will continue.
                     }
 
-
                     // Manage panning with pan button click
                     this._isPanClick = evt.button === this.camera._panningMouseButton;
 
@@ -67,7 +66,11 @@ module BABYLON {
                         evt.preventDefault();
                         element.focus();
                     }
-                } else if (p.type === PointerEventTypes.POINTERUP) {
+                } 
+                else if (p.type === PointerEventTypes.POINTERDOUBLETAP) {
+                    this.camera.restoreState();
+                }
+                else if (p.type === PointerEventTypes.POINTERUP) {
                     try {
                         evt.srcElement.releasePointerCapture(evt.pointerId);
                     } catch (e) {
@@ -96,10 +99,8 @@ module BABYLON {
                         if (this.panningSensibility !== 0 &&
                             ((evt.ctrlKey && this.camera._useCtrlForPanning) ||
                                 (!this.camera._useCtrlForPanning && this._isPanClick))) {
-                            this.camera
-                                .inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
-                            this.camera
-                                .inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
+                            this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
+                            this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
                         } else {
                             var offsetX = evt.clientX - cacheSoloPointer.x;
                             var offsetY = evt.clientY - cacheSoloPointer.y;
@@ -138,7 +139,7 @@ module BABYLON {
                 }
             }
 
-            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
+            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE | PointerEventTypes._POINTERDOUBLETAP);
 
             this._onContextMenu = evt => {
                 evt.preventDefault();

+ 4 - 2
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -93,8 +93,10 @@ module BABYLON {
             if (this._webVRsupportedAndReady) {
                 this._scene.getEngine().disableVR();
             }
-            this._position = this._scene.activeCamera.position;
-            this._scene.activeCamera.dispose();
+            if (this._scene.activeCamera) {
+                this._position = this._scene.activeCamera.position;
+                this._scene.activeCamera.dispose();
+            }
             this._scene.activeCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position, this._scene); 
             this._scene.activeCamera.attachControl(this._canvas);
             this._isInVRMode = false;

+ 8 - 15
src/Cameras/VR/babylon.webVRCamera.ts

@@ -34,7 +34,7 @@ module BABYLON {
         positionScale?: number;
         displayName?: string; //if there are more than one VRDisplays.
         controllerMeshes?: boolean; // should the native controller meshes be initialized
-        defaultLightningOnControllers?: boolean; // creating a default HemiLight only on controllers
+        defaultLightingOnControllers?: boolean; // creating a default HemiLight only on controllers
     }
 
     export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
@@ -60,9 +60,7 @@ module BABYLON {
         public deviceScaleFactor: number = 1;
 
         public controllers: Array<WebVRController> = [];
-        public nonVRControllers: Array<Gamepad> = [];
         public onControllersAttachedObservable = new Observable<Array<WebVRController>>();
-        public onNonVRControllersAttachedObservable = new Observable<Gamepad>();
 
         public rigParenting: boolean = true; // should the rig cameras be used as parent instead of this camera.
 
@@ -71,6 +69,8 @@ module BABYLON {
         constructor(name: string, position: Vector3, scene: Scene, private webVROptions: WebVROptions = {}) {
             super(name, position, scene);
 
+            this.minZ = 0.1;
+
             //legacy support - the compensation boolean was removed.
             if (arguments.length === 5) {
                 this.webVROptions = arguments[4];
@@ -83,8 +83,8 @@ module BABYLON {
             if (this.webVROptions.controllerMeshes == undefined) {
                 this.webVROptions.controllerMeshes = true;
             }
-            if (this.webVROptions.defaultLightningOnControllers == undefined) {
-                this.webVROptions.defaultLightningOnControllers = true;
+            if (this.webVROptions.defaultLightingOnControllers == undefined) {
+                this.webVROptions.defaultLightingOnControllers = true;
             }
 
             this.rotationQuaternion = new Quaternion();
@@ -201,7 +201,6 @@ module BABYLON {
                     //backwards comp
                     let pose = this._vrDevice.getPose();
                     this._frameData.pose = pose;
-                    // calculate view and projection matrix
                 }
 
                 this.updateFromDevice(this._frameData.pose);
@@ -360,8 +359,8 @@ module BABYLON {
 
                 let parentCamera = <WebVRFreeCamera> this.parent;
 
-                parentCamera._vrDevice.depthNear = this.minZ;
-                parentCamera._vrDevice.depthFar = this.maxZ;
+                parentCamera._vrDevice.depthNear = parentCamera.minZ;
+                parentCamera._vrDevice.depthFar = parentCamera.maxZ;
                 
                 var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
                 Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
@@ -395,8 +394,6 @@ module BABYLON {
                     }
 
                     this.controllers.splice(index, 1);
-                    
-                    webVrController.dispose();
                 }
             });
 
@@ -405,7 +402,7 @@ module BABYLON {
                     let webVrController: WebVRController = <WebVRController>gamepad;
                     if (this.webVROptions.controllerMeshes) {
                         webVrController.initControllerMesh(this.getScene(), (loadedMesh) => {
-                            if (this.webVROptions.defaultLightningOnControllers) {
+                            if (this.webVROptions.defaultLightingOnControllers) {
                                 if (!this._lightOnControllers) {
                                     this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), this.getScene());
                                 }
@@ -445,10 +442,6 @@ module BABYLON {
                         }
                     }
                 }
-                else {
-                    this.nonVRControllers.push(gamepad);
-                    this.onNonVRControllersAttachedObservable.notifyObservers(gamepad);
-                }
             });
         }
     }

+ 43 - 0
src/Cameras/babylon.arcRotateCamera.ts

@@ -343,6 +343,47 @@ module BABYLON {
             return this._target;
         }
 
+       // State
+
+        /**
+         * Store current camera state (fov, position, etc..)
+         */
+        private _storedAlpha: number;
+        private _storedBeta: number;
+        private _storedRadius: number;
+        private _storedTarget: Vector3;     
+
+        public storeState(): Camera {
+            this._storedAlpha = this.alpha;
+            this._storedBeta = this.beta;
+            this._storedRadius = this.radius;
+            this._storedTarget = this._getTargetPosition().clone();
+
+            return super.storeState();
+        }
+
+        /**
+         * Restored camera state. You must call storeState() first
+         */
+        public restoreState(): boolean {
+            if (!super.restoreState()) {
+                return false;
+            }
+
+            this.alpha = this._storedAlpha;
+            this.beta = this._storedBeta;
+            this.radius = this._storedRadius;
+            this.setTarget(this._storedTarget);
+
+            this.inertialAlphaOffset = 0;
+            this.inertialBetaOffset = 0;
+            this.inertialRadiusOffset = 0;
+            this.inertialPanningX = 0;
+            this.inertialPanningY = 0;
+        
+            return true;
+        }             
+
         // Synchronized
         public _isSynchronizedViewMatrix(): boolean {
             if (!super._isSynchronizedViewMatrix())
@@ -366,6 +407,8 @@ module BABYLON {
                 this.inertialAlphaOffset = 0;
                 this.inertialBetaOffset = 0;
                 this.inertialRadiusOffset = 0;
+                this.inertialPanningX = 0;
+                this.inertialPanningY = 0;
             };
         }
 

+ 30 - 2
src/Cameras/babylon.camera.ts

@@ -86,7 +86,7 @@
         public fov = 0.8;
 
         @serialize()
-        public minZ = 0.1;
+        public minZ = 1;
 
         @serialize()
         public maxZ = 10000.0;
@@ -154,6 +154,32 @@
             this.position = position;
         }
 
+        private _storedFov: number;
+        private _stateStored: boolean;
+
+        /**
+         * Store current camera state (fov, position, etc..)
+         */
+        public storeState(): Camera {
+            this._stateStored = true;
+            this._storedFov = this.fov;
+
+            return this;
+        }
+
+        /**
+         * Restored camera state. You must call storeState() first
+         */
+        public restoreState(): boolean {
+            if (!this._stateStored) {
+                return false;
+            }
+
+            this.fov = this._storedFov;
+
+            return true;
+        }
+
         public getClassName(): string {
             return "Camera";
         }
@@ -553,7 +579,9 @@
             this.onAfterCheckInputsObservable.clear();
 
             // Inputs
-            this.inputs.clear();
+            if (this.inputs) {
+                this.inputs.clear();
+            }
 
             // Animations
             this.getScene().stopAnimation(this);

+ 6 - 6
src/Cameras/babylon.freeCamera.ts

@@ -89,7 +89,7 @@
             super(name, position, scene);
             this.inputs = new FreeCameraInputsManager(this);
             this.inputs.addKeyboard().addMouse();
-        }
+        }     
 
         // Controls
         public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
@@ -114,7 +114,7 @@
             this._collisionMask = !isNaN(mask) ? mask : -1;
         }
 	 
-        public _collideWithWorld(velocity: Vector3): void {
+        public _collideWithWorld(direction: Vector3): void {
             var globalPosition: Vector3;
 
             if (this.parent) {
@@ -133,15 +133,15 @@
             this._collider.collisionMask = this._collisionMask;
 		
             //no need for clone, as long as gravity is not on.
-            var actualVelocity = velocity;
+            var actualDirection = direction;
 			
-            //add gravity to the velocity to prevent the dual-collision checking
+            //add gravity to the direction to prevent the dual-collision checking
             if (this.applyGravity) {
                 //this prevents mending with cameraDirection, a global variable of the free camera class.
-                actualVelocity = velocity.add(this.getScene().gravity);
+                actualDirection = direction.add(this.getScene().gravity);
             }
 
-            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDirection, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
 
         }
 

+ 40 - 0
src/Cameras/babylon.targetCamera.ts

@@ -57,6 +57,46 @@ module BABYLON {
             return this.lockedTarget.absolutePosition || this.lockedTarget;
         }
 
+        // State
+
+        /**
+         * Store current camera state (fov, position, etc..)
+         */
+        private _storedPosition: Vector3;
+        private _storedRotation: Vector3;
+        private _storedRotationQuaternion: Quaternion;
+
+        public storeState(): Camera {
+            this._storedPosition = this.position.clone();
+            this._storedRotation = this.rotation.clone();
+            if (this.rotationQuaternion) {
+                this._storedRotationQuaternion = this.rotationQuaternion.clone();
+            }
+
+            return super.storeState();
+        }
+
+        /**
+         * Restored camera state. You must call storeState() first
+         */
+        public restoreState(): boolean {
+            if (!super.restoreState()) {
+                return false;
+            }
+
+            this.position = this._storedPosition.clone();
+            this.rotation = this._storedRotation.clone();
+
+            if (this.rotationQuaternion) {
+                this.rotationQuaternion = this._storedRotationQuaternion.clone();
+            }
+
+            this.cameraDirection.copyFromFloats(0, 0, 0);
+            this.cameraRotation.copyFromFloats(0, 0);
+
+            return true;
+        }           
+
         // Cache
         public _initCache() {
             super._initCache();

+ 26 - 0
src/Gamepad/Controllers/babylon.genericController.ts

@@ -0,0 +1,26 @@
+module BABYLON {
+    
+    export class GenericController extends WebVRController {
+        private _defaultModel: BABYLON.AbstractMesh;
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+        }
+
+        public initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void) {
+            SceneLoader.ImportMesh("", "http://yoda.blob.core.windows.net/models/", "genericvrcontroller.babylon", scene, (newMeshes) => {
+                this._defaultModel = newMeshes[1];
+                if (meshLoaded) {
+                    meshLoaded(this._defaultModel);
+                }
+                this.attachToMesh(this._defaultModel);
+            });
+        }
+
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            console.log("Button id: " + buttonIdx + "state: ");
+            console.dir(state);
+        }
+    }
+
+}

+ 130 - 0
src/Gamepad/Controllers/babylon.oculusTouchController.ts

@@ -0,0 +1,130 @@
+module BABYLON {
+
+    export class OculusTouchController extends WebVRController {
+        private _defaultModel: BABYLON.AbstractMesh;
+
+        public onSecondaryTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        public onThumbRestChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.OCULUS;
+        }
+
+        public initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void) {
+            let meshName = this.hand === 'right' ? 'RightTouch.babylon' : 'LeftTouch.babylon';
+            SceneLoader.ImportMesh("", "http://yoda.blob.core.windows.net/models/", meshName, scene, (newMeshes) => {
+                /*
+                Parent Mesh name: oculus_touch_left
+                - body
+                - trigger
+                - thumbstick
+                - grip
+                - button_y 
+                - button_x
+                - button_enter
+                */
+
+                this._defaultModel = newMeshes[1];
+                if (meshLoaded) {
+                    meshLoaded(this._defaultModel);
+                }
+                this.attachToMesh(this._defaultModel);
+            });
+        }
+
+
+        // helper getters for left and right hand.
+        public get onAButtonStateChangedObservable() {
+            if (this.hand === 'right') {
+                return this.onMainButtonStateChangedObservable;
+            } else {
+                throw new Error('No A button on left hand');
+            }
+        }
+
+        public get onBButtonStateChangedObservable() {
+            if (this.hand === 'right') {
+                return this.onSecondaryButtonStateChangedObservable;
+            } else {
+                throw new Error('No B button on left hand');
+            }
+        }
+
+        public get onXButtonStateChangedObservable() {
+            if (this.hand === 'left') {
+                return this.onMainButtonStateChangedObservable;
+            } else {
+                throw new Error('No X button on right hand');
+            }
+        }
+
+        public get onYButtonStateChangedObservable() {
+            if (this.hand === 'left') {
+                return this.onSecondaryButtonStateChangedObservable;
+            } else {
+                throw new Error('No Y button on right hand');
+            }
+        }
+
+        /*
+         0) thumb stick (touch, press, value = pressed (0,1)). value is in this.leftStick
+         1) index trigger (touch (?), press (only when value > 0.1), value 0 to 1)
+         2) secondary trigger (same)
+         3) A (right) X (left), touch, pressed = value
+         4) B / Y 
+         5) thumb rest
+        */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let notifyObject = state; //{ state: state, changes: changes };
+            let triggerDirection = this.hand === 'right' ? -1 : 1;
+            switch (buttonIdx) {
+                case 0:
+                    this.onPadStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 1: // index trigger
+                    if (this._defaultModel) {
+                        (<AbstractMesh>(this._defaultModel.getChildren()[3])).rotation.x = -notifyObject.value * 0.20;
+                        (<AbstractMesh>(this._defaultModel.getChildren()[3])).position.y = -notifyObject.value * 0.005;
+                        (<AbstractMesh>(this._defaultModel.getChildren()[3])).position.z = -notifyObject.value * 0.005;
+                    }
+                    this.onTriggerStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 2:  // secondary trigger
+                    if (this._defaultModel) {
+                        (<AbstractMesh>(this._defaultModel.getChildren()[4])).position.x = triggerDirection * notifyObject.value * 0.0035;
+                    }
+                    this.onSecondaryTriggerStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 3:
+                    if (this._defaultModel) {
+                        if (notifyObject.pressed) {
+                            (<AbstractMesh>(this._defaultModel.getChildren()[1])).position.y = -0.001;
+                        }
+                        else {
+                            (<AbstractMesh>(this._defaultModel.getChildren()[1])).position.y = 0;
+                        }
+                    }
+                    this.onMainButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 4:
+                    if (this._defaultModel) {
+                        if (notifyObject.pressed) {
+                            (<AbstractMesh>(this._defaultModel.getChildren()[2])).position.y = -0.001;
+                        }
+                        else {
+                            (<AbstractMesh>(this._defaultModel.getChildren()[2])).position.y = 0;
+                        }
+                    }
+                    this.onSecondaryButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 5:
+                    this.onThumbRestChangedObservable.notifyObservers(notifyObject);
+                    return;
+            }
+        }
+
+    }
+
+}

+ 166 - 0
src/Gamepad/Controllers/babylon.poseEnabledController.ts

@@ -0,0 +1,166 @@
+module BABYLON {
+
+    export enum PoseEnabledControllerType {
+        VIVE,
+        OCULUS,
+        WINDOWS,
+        GENERIC
+    }
+
+    export interface MutableGamepadButton {
+        value: number;
+        touched: boolean;
+        pressed: boolean;
+    }
+    
+    export interface ExtendedGamepadButton extends GamepadButton {
+        readonly pressed: boolean;
+        readonly touched: boolean;
+        readonly value: number;
+    }
+
+    export class PoseEnabledControllerHelper {
+        public static InitiateController(vrGamepad: any) {
+            // Oculus Touch
+            if (vrGamepad.id.indexOf('Oculus Touch') !== -1) {
+                return new OculusTouchController(vrGamepad);
+            }
+            // Windows Mixed Reality controllers 
+            // else if (vrGamepad.id.indexOf('Spatial Control') === 0) {
+            //     //return new WindowsMixedRealityController(vrGamepad);
+            // }
+            // HTC Vive
+            else if (vrGamepad.id.toLowerCase().indexOf('openvr') !== -1) {
+                return new ViveController(vrGamepad);
+            }
+            // Generic 
+            else {
+                return new GenericController(vrGamepad);
+            }
+        }
+    }
+
+    export class PoseEnabledController extends Gamepad implements PoseControlled {
+        devicePosition: Vector3;
+        deviceRotationQuaternion: Quaternion;
+        deviceScaleFactor: number = 1;
+
+        public position: Vector3;
+        public rotationQuaternion: Quaternion;
+        public controllerType: PoseEnabledControllerType;
+
+        private _calculatedPosition: Vector3;
+        private _calculatedRotation: Quaternion;
+
+        public rawPose: DevicePose; //GamepadPose;
+
+        public _mesh: AbstractMesh; // a node that will be attached to this Gamepad
+        private _poseControlledCamera: TargetCamera;
+
+        private _leftHandSystemQuaternion: Quaternion = new Quaternion();
+
+        constructor(public vrGamepad) {
+            super(vrGamepad.id, vrGamepad.index, vrGamepad);
+            this.type = Gamepad.POSE_ENABLED;
+            this.controllerType = PoseEnabledControllerType.GENERIC;
+            this.position = Vector3.Zero();
+            this.rotationQuaternion = new Quaternion();
+            this.devicePosition = Vector3.Zero();
+            this.deviceRotationQuaternion = new Quaternion();
+
+            this._calculatedPosition = Vector3.Zero();
+            this._calculatedRotation = new Quaternion();
+            Quaternion.RotationYawPitchRollToRef(Math.PI, 0, 0, this._leftHandSystemQuaternion);
+        }
+
+        public update() {
+            super.update();
+            var pose: GamepadPose = this.vrGamepad.pose;
+            this.updateFromDevice(pose);
+
+            if (this._mesh) {
+                this._mesh.position.copyFrom(this._calculatedPosition);
+                this._mesh.rotationQuaternion.copyFrom(this._calculatedRotation);
+            }
+        }
+
+        updateFromDevice(poseData: DevicePose) {
+            if (poseData) {
+                this.rawPose = poseData;
+                if (poseData.position) {
+                    this.devicePosition.copyFromFloats(poseData.position[0], poseData.position[1], -poseData.position[2]);
+                    if (this._mesh && this._mesh.getScene().useRightHandedSystem) {
+                        this.devicePosition.z *= -1;
+                    }
+
+                    this.devicePosition.scaleToRef(this.deviceScaleFactor, this._calculatedPosition);
+                    this._calculatedPosition.addInPlace(this.position);
+                }
+                if (poseData.orientation) {
+                    this.deviceRotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);
+                    if (this._mesh) {
+                        if (this._mesh.getScene().useRightHandedSystem) {
+                            this.deviceRotationQuaternion.z *= -1;
+                            this.deviceRotationQuaternion.w *= -1;
+                        } else {
+                            this.deviceRotationQuaternion.multiplyToRef(this._leftHandSystemQuaternion, this.deviceRotationQuaternion);
+                        }
+                    }
+
+                    // if the camera is set, rotate to the camera's rotation
+                    this.deviceRotationQuaternion.multiplyToRef(this.rotationQuaternion, this._calculatedRotation);
+                }
+            }
+        }
+
+
+        public attachToMesh(mesh: AbstractMesh) {
+            if (this._mesh) {
+                this._mesh.parent = undefined;
+            }
+            this._mesh = mesh;
+            if (this._poseControlledCamera) {
+                this._mesh.parent = this._poseControlledCamera;
+            }
+            if (!this._mesh.rotationQuaternion) {
+                this._mesh.rotationQuaternion = new Quaternion();
+            }
+        }
+
+        public attachToPoseControlledCamera(camera: TargetCamera) {
+            this._poseControlledCamera = camera;
+            if (this._mesh) {
+                this._mesh.parent = this._poseControlledCamera;
+            }
+        }
+
+        public dispose() {
+            if (this._mesh) {
+                this._mesh.dispose();
+            }
+            this._mesh = undefined;
+
+            super.dispose();
+        }
+
+        public get mesh(): AbstractMesh {
+            return this._mesh;
+        }
+
+        public getForwardRay(length = 100): Ray {
+            if (!this.mesh) {
+                return new Ray(Vector3.Zero(), new BABYLON.Vector3(0, 0, 1), length);
+            }
+
+            var m = this.mesh.getWorldMatrix();
+            var origin = m.getTranslation();
+
+            var forward = new BABYLON.Vector3(0, 0, -1);
+            var forwardWorld = BABYLON.Vector3.TransformNormal(forward, m);
+
+            var direction = BABYLON.Vector3.Normalize(forwardWorld);            
+
+            return new Ray(origin, direction, length);
+        } 
+    }
+}

+ 82 - 0
src/Gamepad/Controllers/babylon.viveController.ts

@@ -0,0 +1,82 @@
+module BABYLON {
+
+    export class ViveController extends WebVRController {
+        private _defaultModel: BABYLON.AbstractMesh;
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.VIVE;
+        }
+
+        public initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void) {
+            SceneLoader.ImportMesh("", "http://yoda.blob.core.windows.net/models/", "ViveWand.babylon", scene, (newMeshes) => {
+                /*
+                Parent Mesh name: ViveWand
+                - body
+                - r_gripper
+                - l_gripper
+                - menu_button
+                - system_button
+                - trackpad
+                - trigger
+                - LED
+                */
+                this._defaultModel = newMeshes[1];
+                if (meshLoaded) {
+                    meshLoaded(this._defaultModel);
+                }
+                this.attachToMesh(this._defaultModel);
+            });
+        }
+
+
+        public get onLeftButtonStateChangedObservable() {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onRightButtonStateChangedObservable() {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onMenuButtonStateChangedObservable() {
+            return this.onSecondaryButtonStateChangedObservable;
+        }
+
+        /**
+         * Vive mapping:
+         * 0: touchpad
+         * 1: trigger
+         * 2: left AND right buttons
+         * 3: menu button
+         */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let notifyObject = state; //{ state: state, changes: changes };
+            switch (buttonIdx) {
+                case 0:
+                    this.onPadStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 1: // index trigger
+                    if (this._defaultModel) {
+                        (<AbstractMesh>(this._defaultModel.getChildren()[6])).rotation.x = -notifyObject.value * 0.15;
+                    }
+                    this.onTriggerStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 2:  // left AND right button
+                    this.onMainButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 3:
+                    if (this._defaultModel) {
+                        if (notifyObject.pressed) {
+                            (<AbstractMesh>(this._defaultModel.getChildren()[2])).position.y = -0.001;
+                        }
+                        else {
+                            (<AbstractMesh>(this._defaultModel.getChildren()[2])).position.y = 0;
+                        }
+                    }
+                    this.onSecondaryButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+            }
+        }
+    }
+
+}

+ 93 - 0
src/Gamepad/Controllers/babylon.webVRController.ts

@@ -0,0 +1,93 @@
+module BABYLON {
+
+    export abstract class WebVRController extends PoseEnabledController {
+
+        public onTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        public onMainButtonStateChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        public onSecondaryButtonStateChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        public onPadStateChangedObservable = new Observable<ExtendedGamepadButton>();
+        public onPadValuesChangedObservable = new Observable<StickValues>();
+
+        protected _buttons: Array<MutableGamepadButton>;
+
+        private _onButtonStateChange: (controlledIndex: number, buttonIndex: number, state: ExtendedGamepadButton) => void;
+
+        public onButtonStateChange(callback: (controlledIndex: number, buttonIndex: number, state: ExtendedGamepadButton) => void) {
+            this._onButtonStateChange = callback;
+        }
+
+        public pad: StickValues = { x: 0, y: 0 };
+
+        public hand: string; // 'left' or 'right', see https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this._buttons = new Array<ExtendedGamepadButton>(vrGamepad.buttons.length);
+            this.hand = vrGamepad.hand;
+        }
+
+        public update() {
+            super.update();
+            for (var index = 0; index < this._buttons.length; index++) {
+                this._setButtonValue(this.vrGamepad.buttons[index], this._buttons[index], index);
+            };
+            if (this.leftStick.x !== this.pad.x || this.leftStick.y !== this.pad.y) {
+                this.pad.x = this.leftStick.x;
+                this.pad.y = this.leftStick.y;
+                this.onPadValuesChangedObservable.notifyObservers(this.pad);
+            }
+        }
+
+        protected abstract handleButtonChange(buttonIdx: number, value: ExtendedGamepadButton, changes: GamepadButtonChanges);
+
+        public abstract initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void);
+
+        private _setButtonValue(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton, buttonIndex: number) {
+            if (!newState) {
+                newState = {
+                    pressed: false,
+                    touched: false,
+                    value: 0
+                };
+            }  
+            if (!currentState) {
+                this._buttons[buttonIndex] = {
+                    pressed: newState.pressed,
+                    touched: newState.touched,
+                    value: newState.value
+                }
+                return;
+            }
+            this._checkChanges(newState, currentState);
+            if (this._changes.changed) {
+                this._onButtonStateChange && this._onButtonStateChange(this.index, buttonIndex, newState);
+
+                this.handleButtonChange(buttonIndex, newState, this._changes);
+            }
+            this._buttons[buttonIndex].pressed = newState.pressed;
+            this._buttons[buttonIndex].touched = newState.touched;
+            // oculus triggers are never 0, thou not touched.
+            this._buttons[buttonIndex].value = newState.value < 0.00000001 ? 0 : newState.value;
+        }
+
+        // avoid GC, store state in a tmp object
+        private _changes: GamepadButtonChanges = {
+            pressChanged: false,
+            touchChanged: false,
+            valueChanged: false,
+            changed: false
+        };
+
+        private _checkChanges(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton) {
+            this._changes.pressChanged = newState.pressed !== currentState.pressed;
+            this._changes.touchChanged = newState.touched !== currentState.touched;
+            this._changes.valueChanged = newState.value !== currentState.value;
+            this._changes.changed = this._changes.pressChanged || this._changes.touchChanged || this._changes.valueChanged;
+            return this._changes;
+        }
+    }
+        
+}

+ 125 - 0
src/Gamepad/babylon.gamepad.ts

@@ -0,0 +1,125 @@
+module BABYLON {
+    export class StickValues {
+        constructor(public x, public y) {
+        }
+    }
+
+    export interface GamepadButtonChanges {
+        changed: boolean;
+        pressChanged: boolean;
+        touchChanged: boolean;
+        valueChanged: boolean;
+    }
+    
+    export class Gamepad {
+
+        public type: number;
+
+        private _leftStick: StickValues;
+        private _rightStick: StickValues;
+
+        private _leftStickAxisX: number;
+        private _leftStickAxisY: number;
+        private _rightStickAxisX: number;
+        private _rightStickAxisY: number;
+
+        private _onleftstickchanged: (values: StickValues) => void;
+        private _onrightstickchanged: (values: StickValues) => void;
+
+        public static GAMEPAD = 0;
+        public static GENERIC = 1;
+        public static XBOX = 2;
+        public static POSE_ENABLED = 3;
+
+        constructor(public id: string, public index: number, public browserGamepad, leftStickX: number = 0, leftStickY: number = 1, rightStickX: number = 2, rightStickY: number = 3) {
+            this.type = Gamepad.GAMEPAD;
+            this._leftStickAxisX = leftStickX;
+            this._leftStickAxisY = leftStickY;
+            this._rightStickAxisX = rightStickX;
+            this._rightStickAxisY = rightStickY;
+            if (this.browserGamepad.axes.length >= 2) {
+                this._leftStick = { x: this.browserGamepad.axes[this._leftStickAxisX], y: this.browserGamepad.axes[this._leftStickAxisY] };
+            }
+            if (this.browserGamepad.axes.length >= 4) {
+                this._rightStick = { x: this.browserGamepad.axes[this._rightStickAxisX], y: this.browserGamepad.axes[this._rightStickAxisY] };
+            }
+        }
+
+        public onleftstickchanged(callback: (values: StickValues) => void) {
+            this._onleftstickchanged = callback;
+        }
+
+        public onrightstickchanged(callback: (values: StickValues) => void) {
+            this._onrightstickchanged = callback;
+        }
+
+        public get leftStick(): StickValues {
+            return this._leftStick;
+        }
+        public set leftStick(newValues: StickValues) {
+            if (this._onleftstickchanged && (this._leftStick.x !== newValues.x || this._leftStick.y !== newValues.y)) {
+                this._onleftstickchanged(newValues);
+            }
+            this._leftStick = newValues;
+        }
+        public get rightStick(): StickValues {
+            return this._rightStick;
+        }
+        public set rightStick(newValues: StickValues) {
+            if (this._onrightstickchanged && (this._rightStick.x !== newValues.x || this._rightStick.y !== newValues.y)) {
+                this._onrightstickchanged(newValues);
+            }
+            this._rightStick = newValues;
+        }
+
+        public update() {
+            if (this._leftStick) {
+                this.leftStick = { x: this.browserGamepad.axes[this._leftStickAxisX], y: this.browserGamepad.axes[this._leftStickAxisY] };
+            }
+            if (this._rightStick) {
+                this.rightStick = { x: this.browserGamepad.axes[this._rightStickAxisX], y: this.browserGamepad.axes[this._rightStickAxisY] };
+            }
+        }
+
+        public dispose() {            
+        }
+    }
+
+    export class GenericPad extends Gamepad {
+        private _buttons: Array<number>;
+        private _onbuttondown: (buttonPressed: number) => void;
+        private _onbuttonup: (buttonReleased: number) => void;
+
+        public onbuttondown(callback: (buttonPressed: number) => void) {
+            this._onbuttondown = callback;
+        }
+        public onbuttonup(callback: (buttonReleased: number) => void) {
+            this._onbuttonup = callback;
+        }
+
+        constructor(id: string, index: number, browserGamepad) {
+            super(id, index, browserGamepad);
+            this.type = Gamepad.GENERIC;
+            this._buttons = new Array(browserGamepad.buttons.length);
+        }
+
+        private _setButtonValue(newValue: number, currentValue: number, buttonIndex: number): number {
+            if (newValue !== currentValue) {
+                if (this._onbuttondown && newValue === 1) {
+                    this._onbuttondown(buttonIndex);
+                }
+                if (this._onbuttonup && newValue === 0) {
+                    this._onbuttonup(buttonIndex);
+                }
+            }
+            return newValue;
+        }
+
+        public update() {
+            super.update();
+            for (var index = 0; index < this._buttons.length; index++) {
+                this._buttons[index] = this._setButtonValue(this.browserGamepad.buttons[index].value, this._buttons[index], index);
+            }
+        }
+    }
+}

+ 3 - 8
src/Tools/Gamepad/babylon.gamepadManager.ts

@@ -39,6 +39,8 @@
                         this._babylonGamepads[i] = null;
                         
                         this.onGamepadDisconnectedObservable.notifyObservers(gamepadToRemove);
+
+                        gamepadToRemove.dispose();
                         break;
                     }
                 }            
@@ -93,7 +95,6 @@
             if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
                 newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
             }
-            // (<string>gamepad.id).search("Open VR") !== -1 || (<string>gamepad.id).search("Oculus Touch") !== -1
             // if pose is supported, use the (WebVR) pose enabled controller
             else if (gamepad.pose) {
                 newGamepad = PoseEnabledControllerHelper.InitiateController(gamepad);
@@ -129,13 +130,7 @@
             }
 
             if (this._isMonitoring) {
-                if (window.requestAnimationFrame) {
-                    window.requestAnimationFrame(() => { this._checkGamepadsStatus(); });
-                } else if (window.mozRequestAnimationFrame) {
-                    window.mozRequestAnimationFrame(() => { this._checkGamepadsStatus(); });
-                } else if (window.webkitRequestAnimationFrame) {
-                    window.webkitRequestAnimationFrame(() => { this._checkGamepadsStatus(); });
-                }
+                Tools.QueueNewFrame(() => { this._checkGamepadsStatus(); });
             }
         }
 

+ 0 - 112
src/Tools/Gamepad/babylon.gamepads.ts

@@ -1,116 +1,4 @@
 module BABYLON {
-    export class StickValues {
-        constructor(public x, public y) {
-        }
-    }
-    export class Gamepad {
-
-        public type: number;
-
-        private _leftStick: StickValues;
-        private _rightStick: StickValues;
-
-        private _leftStickAxisX: number;
-        private _leftStickAxisY: number;
-        private _rightStickAxisX: number;
-        private _rightStickAxisY: number;
-
-        private _onleftstickchanged: (values: StickValues) => void;
-        private _onrightstickchanged: (values: StickValues) => void;
-
-        public static GAMEPAD = 0;
-        public static GENERIC = 1;
-        public static XBOX = 2;
-        public static POSE_ENABLED = 3;
-
-        constructor(public id: string, public index: number, public browserGamepad, leftStickX: number = 0, leftStickY: number = 1, rightStickX: number = 2, rightStickY: number = 3) {
-            this.type = Gamepad.GAMEPAD;
-            this._leftStickAxisX = leftStickX;
-            this._leftStickAxisY = leftStickY;
-            this._rightStickAxisX = rightStickX;
-            this._rightStickAxisY = rightStickY;
-            if (this.browserGamepad.axes.length >= 2) {
-                this._leftStick = { x: this.browserGamepad.axes[this._leftStickAxisX], y: this.browserGamepad.axes[this._leftStickAxisY] };
-            }
-            if (this.browserGamepad.axes.length >= 4) {
-                this._rightStick = { x: this.browserGamepad.axes[this._rightStickAxisX], y: this.browserGamepad.axes[this._rightStickAxisY] };
-            }
-        }
-
-        public onleftstickchanged(callback: (values: StickValues) => void) {
-            this._onleftstickchanged = callback;
-        }
-
-        public onrightstickchanged(callback: (values: StickValues) => void) {
-            this._onrightstickchanged = callback;
-        }
-
-        public get leftStick(): StickValues {
-            return this._leftStick;
-        }
-        public set leftStick(newValues: StickValues) {
-            if (this._onleftstickchanged && (this._leftStick.x !== newValues.x || this._leftStick.y !== newValues.y)) {
-                this._onleftstickchanged(newValues);
-            }
-            this._leftStick = newValues;
-        }
-        public get rightStick(): StickValues {
-            return this._rightStick;
-        }
-        public set rightStick(newValues: StickValues) {
-            if (this._onrightstickchanged && (this._rightStick.x !== newValues.x || this._rightStick.y !== newValues.y)) {
-                this._onrightstickchanged(newValues);
-            }
-            this._rightStick = newValues;
-        }
-
-        public update() {
-            if (this._leftStick) {
-                this.leftStick = { x: this.browserGamepad.axes[this._leftStickAxisX], y: this.browserGamepad.axes[this._leftStickAxisY] };
-            }
-            if (this._rightStick) {
-                this.rightStick = { x: this.browserGamepad.axes[this._rightStickAxisX], y: this.browserGamepad.axes[this._rightStickAxisY] };
-            }
-        }
-    }
-
-    export class GenericPad extends Gamepad {
-        private _buttons: Array<number>;
-        private _onbuttondown: (buttonPressed: number) => void;
-        private _onbuttonup: (buttonReleased: number) => void;
-
-        public onbuttondown(callback: (buttonPressed: number) => void) {
-            this._onbuttondown = callback;
-        }
-        public onbuttonup(callback: (buttonReleased: number) => void) {
-            this._onbuttonup = callback;
-        }
-
-        constructor(id: string, index: number, browserGamepad) {
-            super(id, index, browserGamepad);
-            this.type = Gamepad.GENERIC;
-            this._buttons = new Array(browserGamepad.buttons.length);
-        }
-
-        private _setButtonValue(newValue: number, currentValue: number, buttonIndex: number): number {
-            if (newValue !== currentValue) {
-                if (this._onbuttondown && newValue === 1) {
-                    this._onbuttondown(buttonIndex);
-                }
-                if (this._onbuttonup && newValue === 0) {
-                    this._onbuttonup(buttonIndex);
-                }
-            }
-            return newValue;
-        }
-
-        public update() {
-            super.update();
-            for (var index = 0; index < this._buttons.length; index++) {
-                this._buttons[index] = this._setButtonValue(this.browserGamepad.buttons[index].value, this._buttons[index], index);
-            }
-        }
-    }
 
     export enum Xbox360Button {
         A,

+ 28 - 13
src/Layer/babylon.highlightlayer.ts

@@ -285,16 +285,7 @@ module BABYLON {
             var vertexBuffer = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
             this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
 
-            // Indices
-            var indices = [];
-            indices.push(0);
-            indices.push(1);
-            indices.push(2);
-            indices.push(0);
-            indices.push(2);
-            indices.push(3);
-
-            this._indexBuffer = engine.createIndexBuffer(indices);
+            this._createIndexBuffer();
 
             // Effect
             this._glowMapMergeEffect = engine.createEffect("glowMapMerge",
@@ -308,6 +299,28 @@ module BABYLON {
             // Create Textures and post processes
             this.createTextureAndPostProcesses();
         }
+       
+        private _createIndexBuffer(): void {
+            var engine = this._scene.getEngine();
+
+            // Indices
+            var indices = [];
+            indices.push(0);
+            indices.push(1);
+            indices.push(2);
+
+            indices.push(0);
+            indices.push(2);
+            indices.push(3);
+
+            this._indexBuffer = engine.createIndexBuffer(indices);
+        }
+
+        public _rebuild(): void {
+            this._vertexBuffers[VertexBuffer.PositionKind]._rebuild();
+
+            this._createIndexBuffer();
+        }        
 
         /**
          * Creates the render target textures and post processes used in the highlight layer.
@@ -334,6 +347,7 @@ module BABYLON {
             this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
             this._mainTexture.renderParticles = false;
             this._mainTexture.renderList = null;
+            this._mainTexture.ignoreCameraViewport = true;
 
             this._blurTexture = new RenderTargetTexture("HighlightLayerBlurRTT",
                 {
@@ -349,6 +363,7 @@ module BABYLON {
             this._blurTexture.anisotropicFilteringLevel = 16;
             this._blurTexture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
             this._blurTexture.renderParticles = false;
+            this._blurTexture.ignoreCameraViewport = true;
 
             this._downSamplePostprocess = new PassPostProcess("HighlightLayerPPP", this._options.blurTextureSizeRatio,
                 null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
@@ -388,7 +403,7 @@ module BABYLON {
 
                 this._scene.postProcessManager.directRender(
                     [this._downSamplePostprocess, this._horizontalBlurPostprocess, this._verticalBlurPostprocess],
-                    this._blurTexture.getInternalTexture());
+                    this._blurTexture.getInternalTexture(), true);
 
                 this.onAfterBlurObservable.notifyObservers(this);
             });
@@ -774,8 +789,8 @@ module BABYLON {
                 this._mainTextureDesiredSize.height = this._options.mainTextureFixedSize;
             }
             else {
-                this._mainTextureDesiredSize.width = this._engine.getRenderingCanvas().width * this._options.mainTextureRatio;
-                this._mainTextureDesiredSize.height = this._engine.getRenderingCanvas().height * this._options.mainTextureRatio;
+                this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._options.mainTextureRatio;
+                this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._options.mainTextureRatio;
 
                 this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
                 this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;

+ 21 - 10
src/Layer/babylon.layer.ts

@@ -80,6 +80,23 @@
             var vertexBuffer = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
             this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
 
+            this._createIndexBuffer();
+
+            // Effects
+            this._effect = engine.createEffect("layer",
+                [VertexBuffer.PositionKind],
+                ["textureMatrix", "color", "scale", "offset"],
+                ["textureSampler"], "");
+
+            this._alphaTestEffect = engine.createEffect("layer",
+                [VertexBuffer.PositionKind],
+                ["textureMatrix", "color", "scale", "offset"],
+                ["textureSampler"], "#define ALPHATEST");
+        }
+
+        private _createIndexBuffer(): void {
+            var engine = this._scene.getEngine();
+
             // Indices
             var indices = [];
             indices.push(0);
@@ -91,17 +108,12 @@
             indices.push(3);
 
             this._indexBuffer = engine.createIndexBuffer(indices);
+        }
 
-            // Effects
-            this._effect = engine.createEffect("layer",
-                [VertexBuffer.PositionKind],
-                ["textureMatrix", "color", "scale", "offset"],
-                ["textureSampler"], "");
+        public _rebuild(): void {
+            this._vertexBuffers[VertexBuffer.PositionKind]._rebuild();
 
-            this._alphaTestEffect = engine.createEffect("layer",
-                [VertexBuffer.PositionKind],
-                ["textureMatrix", "color", "scale", "offset"],
-                ["textureSampler"], "#define ALPHATEST");
+            this._createIndexBuffer();
         }
 
         public render(): void {
@@ -119,7 +131,6 @@
             engine.enableEffect(currentEffect);
             engine.setState(false);
 
-
             // Texture
             currentEffect.setTexture("textureSampler", this.texture);
             currentEffect.setMatrix("textureMatrix", this.texture.getTextureMatrix());

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

@@ -154,6 +154,10 @@
         }
 
         public set usePoissonSampling(value: boolean) {
+            if (!value && this.filter!== ShadowGenerator.FILTER_POISSONSAMPLING) {
+                return;
+            }    
+
             this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
         }
 
@@ -179,6 +183,9 @@
             return this.filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP;
         }
         public set useExponentialShadowMap(value: boolean) {
+            if (!value && this.filter!== ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) {
+                return;
+            }
             this.filter = (value ? ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
@@ -186,6 +193,9 @@
             return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
         }
         public set useBlurExponentialShadowMap(value: boolean) {
+            if (!value && this.filter!== ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
+                return;
+            }            
             this.filter = (value ? ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
@@ -193,6 +203,9 @@
             return this.filter === ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP;
         }
         public set useCloseExponentialShadowMap(value: boolean) {
+            if (!value && this.filter!== ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP) {
+                return;
+            }                   
             this.filter = (value ? ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
@@ -200,6 +213,9 @@
             return this.filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
         }
         public set useBlurCloseExponentialShadowMap(value: boolean) {
+            if (!value && this.filter!== ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
+                return;
+            }                  
             this.filter = (value ? ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 

+ 3 - 3
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -1061,7 +1061,9 @@
                 this._uniformBuffer.bindToEffect(effect, "Material");
 
                 this.bindViewProjection(effect);
-
+                var reflectionTexture = this._getReflectionTexture();
+                var refractionTexture = this._getRefractionTexture();
+                               
                 if (!this._uniformBuffer.useUbo || !this.isFrozen || !this._uniformBuffer.isSync) {
 
                     // Texture uniforms
@@ -1081,7 +1083,6 @@
                             MaterialHelper.BindTextureMatrix(this._opacityTexture, this._uniformBuffer, "opacity");
                         }
 
-                        var reflectionTexture = this._getReflectionTexture();
                         if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
                             this._uniformBuffer.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix());
                             this._uniformBuffer.updateFloat2("vReflectionInfos", reflectionTexture.level, 0);
@@ -1146,7 +1147,6 @@
                             }                                                         
                         }
 
-                        var refractionTexture = this._getRefractionTexture();
                         if (refractionTexture && StandardMaterial.RefractionTextureEnabled) {
                             this._uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
 

+ 16 - 1
src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts

@@ -63,6 +63,12 @@
 
             this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
 
+            this._createIndexBuffer();
+        }
+
+        private _createIndexBuffer(): void {
+            var engine = this.getScene().getEngine();
+
             // Indices
             var indices = [];
             indices.push(0);
@@ -76,6 +82,15 @@
             this._indexBuffer = engine.createIndexBuffer(indices);
         }
 
+        public _rebuild(): void {
+            this._vertexBuffers[VertexBuffer.PositionKind]._rebuild();
+            this._createIndexBuffer();
+
+            if (this.refreshRate === RenderTargetTexture.REFRESHRATE_RENDER_ONCE) {
+                this.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
+            }            
+        }
+
         public reset(): void {
             if (this._effect === undefined) {
                 return;
@@ -113,7 +128,7 @@
 
                     if (this._fallbackTexture) {
                         this._texture = this._fallbackTexture._texture;
-                        this._texture.references++;
+                        this._texture.incrementReferences();
                     }
 
                     this._fallbackTextureUsed = true;

+ 13 - 23
src/Materials/Textures/babylon.baseTexture.ts

@@ -103,10 +103,8 @@
 
         public delayLoadState = Engine.DELAYLOADSTATE_NONE;
 
-        public _cachedAnisotropicFilteringLevel: number;
-
         private _scene: Scene;
-        public _texture: WebGLTexture;
+        public _texture: InternalTexture;
         private _uid: string;
 
         public get isBlocking(): boolean {
@@ -131,7 +129,7 @@
             return null;
         }
 
-        public getInternalTexture(): WebGLTexture {
+        public getInternalTexture(): InternalTexture {
             return this._texture;
         }
 
@@ -153,8 +151,8 @@
         }
 
         public getSize(): ISize {
-            if (this._texture._width) {
-                return new Size(this._texture._width, this._texture._height);
+            if (this._texture.width) {
+                return new Size(this._texture.width, this._texture.height);
             }
 
             if (this._texture._size) {
@@ -172,7 +170,7 @@
                 return new Size(this._texture._size, this._texture._size);
             }
 
-            return new Size(this._texture._baseWidth, this._texture._baseHeight);
+            return new Size(this._texture.baseWidth, this._texture.baseHeight);
         }
 
         public scale(ratio: number): void {
@@ -182,26 +180,14 @@
             return false;
         }
 
-        public _removeFromCache(url: string, noMipmap: boolean): void {
-            var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
-            for (var index = 0; index < texturesCache.length; index++) {
-                var texturesCacheEntry = texturesCache[index];
-
-                if (texturesCacheEntry.url === url && texturesCacheEntry.generateMipMaps === !noMipmap) {
-                    texturesCache.splice(index, 1);
-                    return;
-                }
-            }
-        }
-
-        public _getFromCache(url: string, noMipmap: boolean, sampling?: number): WebGLTexture {
+        public _getFromCache(url: string, noMipmap: boolean, sampling?: number): InternalTexture {
             var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
             for (var index = 0; index < texturesCache.length; index++) {
                 var texturesCacheEntry = texturesCache[index];
 
                 if (texturesCacheEntry.url === url && texturesCacheEntry.generateMipMaps === !noMipmap) {
                     if (!sampling || sampling === texturesCacheEntry.samplingMode) {
-                        texturesCacheEntry.references++;
+                        texturesCacheEntry.incrementReferences();
                         return texturesCacheEntry;
                     }
                 }
@@ -210,6 +196,10 @@
             return null;
         }
 
+        public _rebuild(): void {
+            
+        }
+
         public delayLoad(): void {
         }
 
@@ -250,8 +240,8 @@
 
         public releaseInternalTexture(): void {
             if (this._texture) {
-                this._scene.getEngine().releaseInternalTexture(this._texture);
-                delete this._texture;
+                this._texture.dispose();
+                this._texture = null;
             }
         }
 

+ 1 - 1
src/Materials/Textures/babylon.colorGradingTexture.ts

@@ -145,7 +145,7 @@ module BABYLON {
                     }
                 }
 
-                this.getScene().getEngine().updateTextureSize(texture, size * size, size);
+                texture.updateSize(size * size, size);
                 this.getScene().getEngine().updateRawTexture(texture, data, BABYLON.Engine.TEXTUREFORMAT_RGBA, false);
             }
 

+ 1 - 1
src/Materials/Textures/babylon.cubeTexture.ts

@@ -71,7 +71,7 @@
                 if (this._texture.isReady) {
                     Tools.SetImmediate(() => onLoad());
                 } else {
-                    this._texture.onLoadedCallbacks.push(onLoad);
+                    this._texture.onLoadedObservable.add(onLoad);
                 }
             }
         }

+ 4 - 0
src/Materials/Textures/babylon.dynamicTexture.ts

@@ -119,5 +119,9 @@ module BABYLON {
 
             return newTexture;
         }
+
+        public _rebuild(): void {
+            this.update();
+        }
     }
 } 

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

@@ -153,7 +153,7 @@ module BABYLON {
                 this._size = intArrayView[1]; // CubeMap max mip face size.
 
                 // Update Texture Information.
-                this.getScene().getEngine().updateTextureSize(this._texture, this._size, this._size);
+                this._texture.updateSize(this._size, this._size);
 
                 // Fill polynomial information.
                 var sphericalPolynomial = new SphericalPolynomial();

+ 221 - 0
src/Materials/Textures/babylon.internalTexture.ts

@@ -0,0 +1,221 @@
+module BABYLON {
+    export class InternalTexture {
+
+        public static DATASOURCE_UNKNOWN = 0;
+        public static DATASOURCE_URL = 1;
+        public static DATASOURCE_TEMP = 2;
+        public static DATASOURCE_RAW = 3;
+        public static DATASOURCE_DYNAMIC = 4;
+        public static DATASOURCE_RENDERTARGET = 5;
+        public static DATASOURCE_MULTIRENDERTARGET = 6;
+        public static DATASOURCE_CUBE = 7;
+        public static DATASOURCE_CUBERAW = 8;
+        public static DATASOURCE_CUBEPREFILTERED = 9;
+
+        public isReady: boolean;
+        public isCube: boolean;
+        public url: string;
+        public samplingMode: number;
+        public generateMipMaps: boolean;
+        public samples: number;
+        public type: number;
+        public format: number;
+        public onLoadedObservable = new Observable<InternalTexture>();
+        public width: number;
+        public height: number;
+        public baseWidth: number;
+        public baseHeight: number;
+        public invertY: boolean;
+
+        // Private
+        public _dataSource = InternalTexture.DATASOURCE_UNKNOWN;
+        public _buffer: ArrayBuffer | HTMLImageElement;
+        public _bufferView: ArrayBufferView;
+        public _bufferViewArray: ArrayBufferView[];
+        public _size: number;
+        public _extension: string;
+        public _files: string[];
+        public _workingCanvas: HTMLCanvasElement;
+        public _workingContext: CanvasRenderingContext2D;
+        public _framebuffer: WebGLFramebuffer;
+        public _depthStencilBuffer: WebGLRenderbuffer;
+        public _MSAAFramebuffer: WebGLFramebuffer;
+        public _MSAARenderBuffer: WebGLRenderbuffer;
+        public _cachedCoordinatesMode: number;
+        public _cachedWrapU: number;
+        public _cachedWrapV: number;
+        public _cachedAnisotropicFilteringLevel: number;
+        public _isDisabled: boolean;
+        public _compression: string;
+        public _generateStencilBuffer: boolean;
+        public _generateDepthBuffer: boolean;
+        public _sphericalPolynomial: BABYLON.SphericalPolynomial;
+        public _lodGenerationScale: number;
+        public _lodGenerationOffset: number;
+        // The following three fields helps sharing generated fixed LODs for texture filtering
+        // In environment not supporting the textureLOD extension like EDGE. They are for internal use only.
+        // They are at the level of the gl texture to benefit from the cache.
+        public _lodTextureHigh: BABYLON.BaseTexture;
+        public _lodTextureMid: BABYLON.BaseTexture;
+        public _lodTextureLow: BABYLON.BaseTexture;
+
+        public _webGLTexture: WebGLTexture;
+        public _references: number = 1;
+        private _engine: Engine;
+
+        public get dataSource(): number {
+            return this._dataSource;
+        }
+
+        constructor(engine: Engine, dataSource: number) {
+            this._engine = engine;
+            this._dataSource = dataSource;
+            
+            this._webGLTexture = engine._createTexture();
+        }
+
+        public incrementReferences(): void {
+            this._references++;
+        }
+
+        public updateSize(width: number, height: number): void {
+            this.width = width;
+            this.height = height;
+            this._size = width * height;
+            this.baseWidth = width;
+            this.baseHeight = height;
+        }
+
+        public _rebuild(): void {
+            var proxy: InternalTexture;
+            this.isReady = false;
+            this._cachedCoordinatesMode = null;
+            this._cachedWrapU = null;
+            this._cachedWrapV = null;
+            this._cachedAnisotropicFilteringLevel = null;
+
+            switch (this._dataSource) {
+                case InternalTexture.DATASOURCE_TEMP:
+                    return;
+
+                case InternalTexture.DATASOURCE_URL:
+                    proxy = this._engine.createTexture(this.url, !this.generateMipMaps, this.invertY, null, this.samplingMode, () => {
+                        this.isReady = true;
+                    }, null, this._buffer, null, this.format); 
+                    proxy._swapAndDie(this);
+                    return;
+
+                case InternalTexture.DATASOURCE_RAW:
+                    proxy = this._engine.createRawTexture(this._bufferView, this.baseWidth, this.baseHeight, this.format, this.generateMipMaps, 
+                                                            this.invertY, this.samplingMode, this._compression); 
+                    proxy._swapAndDie(this);
+
+                    this.isReady = true;
+                return;                    
+                
+                case InternalTexture.DATASOURCE_DYNAMIC:
+                    proxy = this._engine.createDynamicTexture(this.baseWidth, this.baseHeight, this.generateMipMaps, this.samplingMode); 
+                    proxy._swapAndDie(this);
+
+                    // The engine will make sure to update content so no need to flag it as isReady = true
+                return;
+
+                case InternalTexture.DATASOURCE_RENDERTARGET:
+                    let options = new RenderTargetCreationOptions();
+                    options.generateDepthBuffer = this._generateDepthBuffer;
+                    options.generateMipMaps = this.generateMipMaps;
+                    options.generateStencilBuffer = this._generateStencilBuffer;
+                    options.samplingMode = this.samplingMode;
+                    options.type = this.type;
+
+                    if (this.isCube) {
+                        proxy = this._engine.createRenderTargetCubeTexture(this.width, options); 
+                    } else {
+                        let size = {
+                            width: this.width,
+                            height: this.height
+                        }
+
+                        proxy = this._engine.createRenderTargetTexture(size, options); 
+                    }
+                    proxy._swapAndDie(this);
+
+                    this.isReady = true;
+                return;                                      
+
+                case InternalTexture.DATASOURCE_CUBE:
+                    proxy = this._engine.createCubeTexture(this.url, null, this._files, !this.generateMipMaps, () => {
+                        this.isReady = true;
+                    }, null, this.format, this._extension);
+                    proxy._swapAndDie(this);
+                    return;
+
+                case InternalTexture.DATASOURCE_CUBERAW:
+                    proxy = this._engine.createRawCubeTexture(this._bufferViewArray, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
+                    proxy._swapAndDie(this);
+
+                    this.isReady = true;
+                    return;                    
+
+                case InternalTexture.DATASOURCE_CUBEPREFILTERED:
+                    proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, (proxy) => {
+                        proxy._swapAndDie(this);
+                        
+                        this.isReady = true;
+                    }, null, this.format, this._extension);
+                    return;
+            }
+        }
+
+        private _swapAndDie(target: InternalTexture): void {
+            target._webGLTexture = this._webGLTexture;
+
+            if (this._framebuffer) {
+                target._framebuffer = this._framebuffer;
+            }
+
+            if (this._depthStencilBuffer) {
+                target._depthStencilBuffer = this._depthStencilBuffer;
+            }
+
+            if (this._lodTextureHigh) {
+                if (target._lodTextureHigh) {
+                    target._lodTextureHigh.dispose();
+                }
+                target._lodTextureHigh = this._lodTextureHigh;
+            }
+
+            if (this._lodTextureMid) {
+                if (target._lodTextureMid) {
+                    target._lodTextureMid.dispose();
+                }                
+                target._lodTextureMid = this._lodTextureMid;
+            }
+
+            if (this._lodTextureLow) {
+                if (target._lodTextureLow) {
+                    target._lodTextureLow.dispose();
+                }                     
+                target._lodTextureLow = this._lodTextureLow;
+            }
+
+            let cache = this._engine.getLoadedTexturesCache();
+            var index = cache.indexOf(this);
+            if (index !== -1) {
+                cache.splice(index, 1);
+            }
+        }
+        
+        public dispose(): void {
+            if (!this._webGLTexture) {
+                return;
+            }
+
+            this._references--;
+            if (this._references === 0) {
+                this._engine._releaseTexture(this);
+                this._webGLTexture = null;
+            }
+        }
+    }
+}

+ 31 - 15
src/Materials/Textures/babylon.multiRenderTarget.ts

@@ -10,7 +10,7 @@ module BABYLON {
     };
     export class MultiRenderTarget extends RenderTargetTexture {
 
-        private _webGLTextures: WebGLTexture[];
+        private _internalTextures: InternalTexture[];
         private _textures: Texture[];
         private _count: number;
 
@@ -73,23 +73,39 @@ module BABYLON {
                 generateDepthTexture: generateDepthTexture,
                 types: types,
                 textureCount: count
-            };
-
-            this._webGLTextures = scene.getEngine().createMultipleRenderTarget(size, this._multiRenderTargetOptions);
+            };            
+            
+            this._createInternalTextures();
+            this._createTextures();
+        }
 
+        public _rebuild(): void {
+            this.releaseInternalTextures();
             this._createInternalTextures();
+
+            for (var i = 0; i < this._internalTextures.length; i++) {
+                var texture = this._textures[i];
+                texture._texture = this._internalTextures[i];
+            }
+
+            // Keeps references to frame buffer and stencil/depth buffer
+            this._texture = this._internalTextures[0];
         }
 
         private _createInternalTextures(): void {
+            this._internalTextures = this.getScene().getEngine().createMultipleRenderTarget(this._size , this._multiRenderTargetOptions);
+        }
+
+        private _createTextures(): void {
             this._textures = [];
-            for (var i = 0; i < this._webGLTextures.length; i++) {
+            for (var i = 0; i < this._internalTextures.length; i++) {
                 var texture = new BABYLON.Texture(null, this.getScene());
-                texture._texture = this._webGLTextures[i];
+                texture._texture = this._internalTextures[i];
                 this._textures.push(texture);
             }
 
             // Keeps references to frame buffer and stencil/depth buffer
-            this._texture = this._webGLTextures[0];
+            this._texture = this._internalTextures[0];
         }
 
         public get samples(): number {
@@ -101,14 +117,14 @@ module BABYLON {
                 return;
             }
             
-            for (var i = 0 ; i < this._webGLTextures.length; i++) {
-                this._samples = this.getScene().getEngine().updateRenderTargetTextureSampleCount(this._webGLTextures[i], value);
+            for (var i = 0 ; i < this._internalTextures.length; i++) {
+                this._samples = this.getScene().getEngine().updateRenderTargetTextureSampleCount(this._internalTextures[i], value);
             }
         }
 
         public resize(size: any) {
             this.releaseInternalTextures();
-            this._webGLTextures = this.getScene().getEngine().createMultipleRenderTarget(size, this._multiRenderTargetOptions);
+            this._internalTextures = this.getScene().getEngine().createMultipleRenderTarget(size, this._multiRenderTargetOptions);
             this._createInternalTextures();
         }
 
@@ -119,14 +135,14 @@ module BABYLON {
         }
 
         public releaseInternalTextures(): void {
-            if (!this._webGLTextures) {
+            if (!this._internalTextures) {
                 return;
             }
 
-            for (var i = this._webGLTextures.length - 1; i >= 0; i--) {
-                if (this._webGLTextures[i] !== undefined) {
-                    this.getScene().getEngine().releaseInternalTexture(this._webGLTextures[i]);
-                    this._webGLTextures.splice(i, 1);
+            for (var i = this._internalTextures.length - 1; i >= 0; i--) {
+                if (this._internalTextures[i] !== undefined) {
+                    this._internalTextures[i].dispose();
+                    this._internalTextures.splice(i, 1);
                 }
             }
         }

+ 12 - 17
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -1,12 +1,4 @@
 module BABYLON {
-    export interface IRenderTargetOptions {
-        generateMipMaps: boolean,
-        type: number,
-        samplingMode: number,
-        generateDepthBuffer: boolean,
-        generateStencilBuffer: boolean
-    }
-
     export class RenderTargetTexture extends Texture {
         public static _REFRESHRATE_RENDER_ONCE: number = 0;
         public static _REFRESHRATE_RENDER_ONEVERYFRAME: number = 1;
@@ -119,8 +111,8 @@
         protected _refreshRate = 1;
         protected _textureMatrix: Matrix;
         protected _samples = 1;
-        protected _renderTargetOptions: IRenderTargetOptions;
-        public get renderTargetOptions(): IRenderTargetOptions {
+        protected _renderTargetOptions: RenderTargetCreationOptions;
+        public get renderTargetOptions(): RenderTargetCreationOptions {
             return this._renderTargetOptions;
         }
 
@@ -248,13 +240,6 @@
             return false;
         }
 
-        public isReady(): boolean {
-            if (!this.getScene().renderTargetsEnabled) {
-                return false;
-            }
-            return super.isReady();
-        }
-
         public getRenderSize(): number {
             return this._size;
         }
@@ -577,5 +562,15 @@
 
             super.dispose();
         }
+
+        public _rebuild(): void {
+            if (this.refreshRate === RenderTargetTexture.REFRESHRATE_RENDER_ONCE) {
+                this.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
+            }
+
+            if (this._postProcessManager) {
+                this._postProcessManager._rebuild();
+            }
+        }
     }
 }

+ 0 - 0
src/Materials/Textures/babylon.texture.ts


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov