瀏覽代碼

Merge branch 'master' into ColorGrading

Sébastien Vandenberghe 9 年之前
父節點
當前提交
f7a5e2e461
共有 61 個文件被更改,包括 10681 次插入5956 次删除
  1. 9 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs
  2. 二進制
      Exporters/3ds Max/Max2Babylon-0.4.6.zip
  3. 277 0
      Exporters/3ds Max/Max2Babylon/2017/Max2Babylon2017.csproj
  4. 36 0
      Exporters/3ds Max/Max2Babylon/2017/Properties/AssemblyInfo.cs
  5. 63 0
      Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.Designer.cs
  6. 120 0
      Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.resx
  7. 二進制
      Exporters/3ds Max/Max2Babylon/2017/Refs/Autodesk.Max.dll
  8. 4 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Light.cs
  9. 1 1
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Material.cs
  10. 91 20
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs
  11. 4 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.ShadowGenerator.cs
  12. 5 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Texture.cs
  13. 35 6
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs
  14. 7 0
      Exporters/3ds Max/Max2Babylon/Exporter/GlobalVertex.cs
  15. 8 0
      Exporters/3ds Max/Max2Babylon/Forms/ActionsBuilderForm.cs
  16. 6 1
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.cs
  17. 834 834
      Exporters/3ds Max/Max2Babylon/Tools/Tools.cs
  18. 84 84
      Exporters/3ds Max/Max2Babylon/Tools/VNormal.cs
  19. 182 182
      Exporters/3ds Max/Max2Babylon/Tools/WebServer.cs
  20. 2 2
      Tools/Gulp/package.json
  21. 26 25
      dist/preview release/babylon.core.js
  22. 3332 3293
      dist/preview release/babylon.d.ts
  23. 32 32
      dist/preview release/babylon.js
  24. 248 142
      dist/preview release/babylon.max.js
  25. 31 31
      dist/preview release/babylon.noworker.js
  26. 15 12
      dist/preview release/what's new.md
  27. 15 47
      materialsLibrary/dist/babylon.simpleMaterial.js
  28. 1 1
      materialsLibrary/dist/babylon.simpleMaterial.min.js
  29. 1 0
      materialsLibrary/dist/dts/babylon.simpleMaterial.d.ts
  30. 17 50
      materialsLibrary/materials/simple/babylon.simpleMaterial.ts
  31. 2 8
      materialsLibrary/materials/simple/simple.fragment.fx
  32. 4551 849
      materialsLibrary/test/refs/babylon.max.js
  33. 3 3
      src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.js
  34. 12 12
      src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.ts
  35. 3 0
      src/Loading/Plugins/babylon.babylonFileLoader.js
  36. 4 0
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  37. 19 2
      src/Materials/babylon.effect.js
  38. 23 2
      src/Materials/babylon.effect.ts
  39. 12 0
      src/Materials/babylon.material.js
  40. 15 0
      src/Materials/babylon.material.ts
  41. 35 5
      src/Materials/babylon.materialHelper.js
  42. 48 6
      src/Materials/babylon.materialHelper.ts
  43. 14 45
      src/Materials/babylon.standardMaterial.js
  44. 21 52
      src/Materials/babylon.standardMaterial.ts
  45. 2 2
      src/Math/babylon.math.js
  46. 2 2
      src/Math/babylon.math.ts
  47. 1 1
      src/Mesh/babylon.instancedMesh.js
  48. 1 1
      src/Mesh/babylon.instancedMesh.ts
  49. 11 11
      src/Particles/babylon.solidParticleSystem.js
  50. 12 12
      src/Particles/babylon.solidParticleSystem.ts
  51. 4 0
      src/Physics/babylon.physicsImpostor.js
  52. 7 3
      src/Physics/babylon.physicsImpostor.ts
  53. 2 8
      src/Shaders/default.fragment.fx
  54. 2 8
      src/Shaders/legacydefault.fragment.fx
  55. 2 8
      src/Shaders/legacypbr.fragment.fx
  56. 2 8
      src/Shaders/pbr.fragment.fx
  57. 139 68
      src/Tools/babylon.dynamicFloatArray.js
  58. 142 73
      src/Tools/babylon.dynamicFloatArray.ts
  59. 100 0
      src/Tools/babylon.smartCollection.js
  60. 2 2
      src/babylon.engine.js
  61. 2 2
      src/babylon.engine.ts

+ 9 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs

@@ -51,6 +51,12 @@ namespace BabylonExport.Entities
         public float[] matricesWeights { get; set; }
 
         [DataMember]
+        public int[] matricesIndicesExtra { get; set; }
+
+        [DataMember]
+        public float[] matricesWeightsExtra { get; set; }
+
+        [DataMember]
         public int[] indices { get; set; }
 
         [DataMember]
@@ -78,6 +84,9 @@ namespace BabylonExport.Entities
         public int skeletonId { get; set; }
 
         [DataMember]
+        public int numBoneInfluencers { get; set; }
+
+        [DataMember]
         public bool showBoundingBox { get; set; }
 
         [DataMember]

二進制
Exporters/3ds Max/Max2Babylon-0.4.6.zip


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

@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{2F49C726-A1F8-40D4-859F-1355949608DC}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Max2Babylon</RootNamespace>
+    <AssemblyName>Max2Babylon</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+    <SccProjectName>SAK</SccProjectName>
+    <SccLocalPath>SAK</SccLocalPath>
+    <SccAuxPath>SAK</SccAuxPath>
+    <SccProvider>SAK</SccProvider>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+    <RestorePackages>true</RestorePackages>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>C:\Program Files\Autodesk\3ds Max 2017\bin\assemblies\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;MAX2017</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>C:\Program Files\Autodesk\3ds Max 2013\bin\assemblies\</OutputPath>
+    <DefineConstants>TRACE;MAX2015</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Autodesk.Max, Version=17.0.630.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\Repos\Babylon.js\Exporters\3ds Max\Max2Babylon\2015\Refs\Autodesk.Max.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="SharpDX, Version=2.4.2.0, Culture=neutral, PublicKeyToken=627a3d6d1956f55a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\BabylonExport.Core\Refs\SharpDX.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Web" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\BabylonActionCallback.cs">
+      <Link>BabylonActionCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\BabylonExportActionItem.cs">
+      <Link>BabylonExportActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\BabylonPropertiesActionItem.cs">
+      <Link>BabylonPropertiesActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\Descriptor.cs">
+      <Link>Descriptor.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs">
+      <Link>Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\ActionBuilder\BabylonExporter.Action.cs">
+      <Link>Exporter\ActionBuilder\BabylonExporter.Action.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Animation.cs">
+      <Link>Exporter\BabylonExporter.Animation.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Camera.cs">
+      <Link>Exporter\BabylonExporter.Camera.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.cs">
+      <Link>Exporter\BabylonExporter.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Light.cs">
+      <Link>Exporter\BabylonExporter.Light.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Material.cs">
+      <Link>Exporter\BabylonExporter.Material.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Mesh.cs">
+      <Link>Exporter\BabylonExporter.Mesh.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.ShadowGenerator.cs">
+      <Link>Exporter\BabylonExporter.ShadowGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Skeleton.cs">
+      <Link>Exporter\BabylonExporter.Skeleton.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Texture.cs">
+      <Link>Exporter\BabylonExporter.Texture.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\GlobalVertex.cs">
+      <Link>Exporter\GlobalVertex.cs</Link>
+    </Compile>
+    <Compile Include="..\Forms\ActionsBuilderForm.cs">
+      <Link>Forms\ActionsBuilderForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ActionsBuilderForm.designer.cs">
+      <Link>Forms\ActionsBuilderForm.designer.cs</Link>
+      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\CameraPropertiesForm.cs">
+      <Link>Forms\CameraPropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\CameraPropertiesForm.Designer.cs">
+      <Link>Forms\CameraPropertiesForm.Designer.cs</Link>
+      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\ExporterForm.cs">
+      <Link>Forms\ExporterForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ExporterForm.Designer.cs">
+      <Link>Forms\ExporterForm.Designer.cs</Link>
+      <DependentUpon>ExporterForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\LightPropertiesForm.cs">
+      <Link>Forms\LightPropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\LightPropertiesForm.Designer.cs">
+      <Link>Forms\LightPropertiesForm.Designer.cs</Link>
+      <DependentUpon>LightPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\ObjectPropertiesForm.cs">
+      <Link>Forms\ObjectPropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ObjectPropertiesForm.Designer.cs">
+      <Link>Forms\ObjectPropertiesForm.Designer.cs</Link>
+      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\ScenePropertiesForm.cs">
+      <Link>Forms\ScenePropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ScenePropertiesForm.Designer.cs">
+      <Link>Forms\ScenePropertiesForm.Designer.cs</Link>
+      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\Vector3Control.cs">
+      <Link>Forms\Vector3Control.cs</Link>
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="..\Forms\Vector3Control.Designer.cs">
+      <Link>Forms\Vector3Control.Designer.cs</Link>
+      <DependentUpon>Vector3Control.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\GlobalUtility.cs">
+      <Link>GlobalUtility.cs</Link>
+    </Compile>
+    <Compile Include="..\JsonTextWriterOptimized.cs">
+      <Link>JsonTextWriterOptimized.cs</Link>
+    </Compile>
+    <Compile Include="..\Loader.cs">
+      <Link>Loader.cs</Link>
+    </Compile>
+    <Compile Include="..\Tools\Tools.cs">
+      <Link>Tools\Tools.cs</Link>
+    </Compile>
+    <Compile Include="..\Tools\VNormal.cs">
+      <Link>Tools\VNormal.cs</Link>
+    </Compile>
+    <Compile Include="..\Tools\WebServer.cs">
+      <Link>Tools\WebServer.cs</Link>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Refs\Autodesk.Max.dll" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="..\Forms\ActionsBuilderForm.resx">
+      <Link>Forms\ActionsBuilderForm.resx</Link>
+      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\CameraPropertiesForm.resx">
+      <Link>Forms\CameraPropertiesForm.resx</Link>
+      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\ExporterForm.resx">
+      <Link>Forms\ExporterForm.resx</Link>
+      <DependentUpon>ExporterForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\LightPropertiesForm.resx">
+      <Link>Forms\LightPropertiesForm.resx</Link>
+      <DependentUpon>LightPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\ObjectPropertiesForm.resx">
+      <Link>Forms\ObjectPropertiesForm.resx</Link>
+      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\ScenePropertiesForm.resx">
+      <Link>Forms\ScenePropertiesForm.resx</Link>
+      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\Vector3Control.resx">
+      <Link>Forms\Vector3Control.resx</Link>
+      <DependentUpon>Vector3Control.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\BabylonExport.Entities\BabylonExport.Entities.csproj">
+      <Project>{6150965a-658c-4263-89ad-4f980eb0675d}</Project>
+      <Name>BabylonExport.Entities</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\BabylonFileConverter\BabylonFileConverter.csproj">
+      <Project>{a6b76356-1d1c-4c82-8199-a6406da85a95}</Project>
+      <Name>BabylonFileConverter</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable 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('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+  </Target>
+  <PropertyGroup>
+    <PostBuildEvent>mkdir "$(OutDir)BabylonActionsBuilder"
+mkdir "$(OutDir)BabylonActionsBuilder\fonts"
+mkdir "$(OutDir)BabylonActionsBuilder\fonts\SinkinSans"
+copy "$(SolutionDir)ActionsBuilder\Sources\fonts.css" "$(OutDir)BabylonActionsBuilder\fonts.css"
+copy "$(SolutionDir)ActionsBuilder\Sources\index.css" "$(OutDir)BabylonActionsBuilder\index.css"
+copy "$(SolutionDir)ActionsBuilder\Sources\index.html" "$(OutDir)BabylonActionsBuilder\index.html"
+copy "$(SolutionDir)ActionsBuilder\Sources\actionsbuilder.max.js" "$(OutDir)BabylonActionsBuilder\actionsbuilder.max.js"
+copy "$(SolutionDir)ActionsBuilder\Sources\raphael.js" "$(OutDir)BabylonActionsBuilder\raphael.js"
+copy "$(SolutionDir)babylon.max.js" "$(OutDir)BabylonActionsBuilder\babylon.max.js"
+copy "$(SolutionDir)ActionsBuilder\Sources\fonts\SinkinSans\" "$(OutDir)BabylonActionsBuilder\fonts\SinkinSans\"</PostBuildEvent>
+  </PropertyGroup>
+  <!-- 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">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 36 - 0
Exporters/3ds Max/Max2Babylon/2017/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Max2Babylon")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Max2Babylon")]
+[assembly: AssemblyCopyright("Copyright ©  2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2d621b1c-3661-49bd-8dd3-4c5de51fce94")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 63 - 0
Exporters/3ds Max/Max2Babylon/2017/Properties/Resources.Designer.cs

@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.34014
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Max2Babylon.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </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")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Max2Babylon.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}

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

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

二進制
Exporters/3ds Max/Max2Babylon/2017/Refs/Autodesk.Max.dll


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

@@ -123,7 +123,11 @@ namespace Max2Babylon
 
                 foreach (var meshNode in maxScene.NodesListBySuperClass(SClass_ID.Geomobject))
                 {
+#if MAX2017
+                    if (meshNode.CastShadows)
+#else
                     if (meshNode.CastShadows == 1)
+#endif
                     {
                         var inList = maxLight.ExclList.FindNode(meshNode) != -1;
 

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

@@ -48,7 +48,7 @@ namespace Max2Babylon
                 return;
             }
 
-            var babylonMaterial = new BabylonMaterial
+            var babylonMaterial = new BabylonStandardMaterial
             {
                 name = name,
                 id = id,

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

@@ -145,7 +145,7 @@ namespace Max2Babylon
                     refDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f),
                 };
 
-                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional", 0);
+                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional");
                 
                 if (isDirectional)
                 {
@@ -167,12 +167,18 @@ namespace Max2Babylon
             }
 
             // Misc.
+#if MAX2017
+            babylonMesh.isVisible = meshNode.MaxNode.Renderable;
+            babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows;
+            babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics;
+#else
             babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1;
-            babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");
             babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
+            babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1;
+#endif
+            babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");
             babylonMesh.showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox");
             babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox");
-            babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1;
             babylonMesh.alphaIndex = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000);
 
             // Actions
@@ -186,6 +192,7 @@ namespace Max2Babylon
             var unskinnedMesh = gameMesh;
             IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
             List<int> boneIds = null;
+            int nbBones = 0;
             if (isSkinned)
             {
                 bonesCount = skin.TotalSkinBoneCount;
@@ -231,17 +238,25 @@ namespace Max2Babylon
             {
                 if (unskinnedMesh.NumberOfFaces < 1)
                 {
-                    RaiseError(string.Format("Mesh {0} has no face", babylonMesh.name), 2);
+                    RaiseError($"Mesh {babylonMesh.name} has no face", 2);
                 }
 
                 if (unskinnedMesh.NumberOfVerts < 3)
                 {
-                    RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name), 2);
+                    RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2);
                 }
 
                 if (unskinnedMesh.NumberOfVerts >= 65536)
                 {
-                    RaiseWarning(string.Format("Mesh {0} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", babylonMesh.name), 2);
+                    RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2);
+                }
+
+                if (skin != null)
+                {
+                    for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++)
+                    {
+                        nbBones = Math.Max(nbBones, skin.GetNumberOfBones(vertexIndex));
+                    }
                 }
 
                 // Physics
@@ -295,7 +310,11 @@ namespace Max2Babylon
                 bool hasUV2 = false;
                 for (int i = 0; i < mappingChannels.Count; ++i)
                 {
+#if MAX2017
+                    var indexer = i;
+#else
                     var indexer = new IntPtr(i);
+#endif
                     var channelNum = mappingChannels[indexer];
                     if (channelNum == 1)
                     {
@@ -325,7 +344,7 @@ namespace Max2Babylon
 
                 for (int i = 0; i < multiMatsCount; ++i)
                 {
-                    int materialId = meshNode.NodeMaterial == null ? 0 : meshNode.NodeMaterial.GetMaterialID(i);
+                    int materialId = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0;
                     var indexCount = 0;
                     var minVertexIndex = int.MaxValue;
                     var maxVertexIndex = int.MinValue;
@@ -336,7 +355,7 @@ namespace Max2Babylon
                         for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j)
                         {
                             var face = unskinnedMesh.GetFace(j);
-                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
+                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds, nbBones);
                         }
                     }
                     else
@@ -344,11 +363,17 @@ namespace Max2Babylon
                         ITab<IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId);
                         for (int j = 0; j < materialFaces.Count; ++j)
                         {
+#if MAX2017
+                            var faceIndexer = j;
+#else
                             var faceIndexer = new IntPtr(j);
+#endif
                             var face = materialFaces[faceIndexer];
 
+#if !MAX2017
                             Marshal.FreeHGlobal(faceIndexer);
-                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds);
+#endif
+                            ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds, nbBones);
                         }
                     }
 
@@ -367,7 +392,7 @@ namespace Max2Babylon
 
                 if (vertices.Count >= 65536)
                 {
-                    RaiseWarning(string.Format("Mesh {0} has {1} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", babylonMesh.name, vertices.Count), 2);
+                    RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2);
 
                     if (!optimizeVertices)
                     {
@@ -375,7 +400,7 @@ namespace Max2Babylon
                     }
                 }
 
-                RaiseMessage(string.Format("{0} vertices, {1} faces", vertices.Count, indices.Count / 3), 2);
+                RaiseMessage($"{vertices.Count} vertices, {indices.Count/3} faces", 2);
 
                 // Buffers
                 babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();
@@ -393,6 +418,13 @@ namespace Max2Babylon
                 {
                     babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                     babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();
+
+                    babylonMesh.numBoneInfluencers = nbBones;
+                    if (nbBones > 4)
+                    {
+                        babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra.ToArray()).ToArray();
+                        babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray();
+                    }
                 }
 
                 if (hasColor)
@@ -416,10 +448,16 @@ namespace Max2Babylon
 
             for (var index = 0; index < tabs.Count; index++)
             {
+#if MAX2017
+                var indexer = index;
+#else
                 var indexer = new IntPtr(index);
+#endif
                 var tab = tabs[indexer];
 
+#if !MAX2017
                 Marshal.FreeHGlobal(indexer);
+#endif
 
                 if (meshNode.MaxNode.GetGuid() == tab.GetGuid())
                 {
@@ -485,11 +523,11 @@ namespace Max2Babylon
             babylonScene.MeshesList.Add(babylonMesh);
         }
 
-        private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds)
+        private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds, int nbBones)
         {
-            var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
-            var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
-            var c = CreateGlobalVertex(unskinnedMesh, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds);
+            var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds, nbBones);
+            var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds, nbBones);
+            var c = CreateGlobalVertex(unskinnedMesh, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds, nbBones);
             indices.Add(a);
             indices.Add(b);
             indices.Add(c);
@@ -587,7 +625,7 @@ namespace Max2Babylon
         }
 
 
-        int CreateGlobalVertex(IIGameMesh mesh, IFaceEx face, int facePart, List<GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List<int> boneIds)
+        int CreateGlobalVertex(IIGameMesh mesh, IFaceEx face, int facePart, List<GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List<int> boneIds, int nbBones)
         {
             var vertexIndex = (int)face.Vert[facePart];
 
@@ -654,11 +692,11 @@ namespace Max2Babylon
                 float weight0 = 0;
                 float weight1 = 0;
                 float weight2 = 0;
+                float weight3 = 0;
                 int bone0 = bonesCount;
                 int bone1 = bonesCount;
                 int bone2 = bonesCount;
                 int bone3 = bonesCount;
-                int nbBones = skin.GetNumberOfBones(vertexIndex);
 
                 if (nbBones > 0)
                 {
@@ -681,6 +719,7 @@ namespace Max2Babylon
                 if (nbBones > 3)
                 {
                     bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 3).NodeID);
+                    weight3 = skin.GetWeight(vertexIndex, 3);
                 }
 
                 if (nbBones == 0)
@@ -689,13 +728,45 @@ namespace Max2Babylon
                     bone0 = bonesCount;
                 }
 
+                vertex.Weights = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3);
+                vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;
+
                 if (nbBones > 4)
                 {
-                    RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 4 bones influences per vertex.", 2);
+                    bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 4).NodeID);
+                    weight0 = skin.GetWeight(vertexIndex, 4);
+
+                    weight1 = 0;
+                    weight2 = 0;
+                    weight3 = 0;
+
+                    if (nbBones > 5)
+                    {
+                        bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 5).NodeID);
+                        weight1 = skin.GetWeight(vertexIndex, 5);
+                    }
+
+                    if (nbBones > 6)
+                    {
+                        bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 6).NodeID);
+                        weight2 = skin.GetWeight(vertexIndex, 6);
+                    }
+
+                    if (nbBones > 7)
+                    {
+                        bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 7).NodeID);
+                        weight3 = skin.GetWeight(vertexIndex, 7);
+                    }
+
+                    vertex.WeightsExtra = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3);
+                    vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;
+
+                    if (nbBones > 8)
+                    {
+                        RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 8 bones influences per vertex.", 2);
+                    }
                 }
 
-                vertex.Weights = Loader.Global.Point4.Create(weight0, weight1, weight2, 1.0 - weight0 - weight1 - weight2);
-                vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;
             }
 
             if (verticesAlreadyExported != null)

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

@@ -47,7 +47,11 @@ namespace Max2Babylon
 
             foreach (var meshNode in Loader.Core.RootNode.NodesListBySuperClass(SClass_ID.Geomobject))
             {
+#if MAX2017
+                if (meshNode.CastShadows)
+#else
                 if (meshNode.CastShadows == 1)
+#endif
                 {
                     var inList = maxLight.ExclList.FindNode(meshNode) != -1;
 

+ 5 - 0
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Texture.cs

@@ -120,6 +120,11 @@ namespace Max2Babylon
             }
 
             // Bitmap
+            if (texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null)
+            {
+                return null;
+            }
+
             var texture = texMap.GetParamBlock(0).Owner as IBitmapTex;
 
             if (texture == null)

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

@@ -111,12 +111,22 @@ namespace Max2Babylon
             {
                 var forceSave = Loader.Core.FileSave;
 
-                if (callerForm != null)
-                {
-                    callerForm.BringToFront();
-                }
+                callerForm?.BringToFront();
             }
 
+            // Producer
+            babylonScene.producer = new BabylonProducer
+            {
+                name = "3dsmax",
+#if MAX2017
+                version = "2017",
+#else
+                version = Loader.Core.ProductVersion.ToString(),
+#endif
+                exporter_version = "0.4.5",
+                file = Path.GetFileName(outputFile)
+            };
+
             // Global
             babylonScene.autoClear = true;
             babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray();
@@ -158,9 +168,17 @@ namespace Max2Babylon
             var camerasTab = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Camera);
             for (int ix = 0; ix < camerasTab.Count; ++ix)
             {
+#if MAX2017
+                var indexer = ix;
+#else
                 var indexer = new IntPtr(ix);
+#endif
                 var cameraNode = camerasTab[indexer];
+
+#if !MAX2017
                 Marshal.FreeHGlobal(indexer);
+#endif
+                
                 ExportCamera(gameScene, cameraNode, babylonScene);
 
                 if (mainCamera == null && babylonScene.CamerasList.Count > 0)
@@ -197,7 +215,7 @@ namespace Max2Babylon
                         babylonScene.fogColor = fog.GetColor(0).ToArray();
                         babylonScene.fogMode = 3;
                     }
-#if !MAX2015 && !MAX2016
+#if !MAX2015 && !MAX2016 && !MAX2017
                     else
                     {
                         var paramBlock = atmospheric.GetReference(0) as IIParamBlock;
@@ -222,9 +240,16 @@ namespace Max2Babylon
             var progression = 10.0f;
             for (int ix = 0; ix < meshes.Count; ++ix)
             {
+#if MAX2017
+                var indexer = ix;
+#else
                 var indexer = new IntPtr(ix);
+#endif
                 var meshNode = meshes[indexer];
+
+#if !MAX2017
                 Marshal.FreeHGlobal(indexer);
+#endif
                 ExportMesh(gameScene, meshNode, babylonScene);
 
 
@@ -251,7 +276,11 @@ namespace Max2Babylon
             var lightNodes = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Light);
             for (var i = 0; i < lightNodes.Count; ++i)
             {
-                ExportLight(gameScene, lightNodes[new IntPtr(i)], babylonScene);
+#if MAX2017
+                ExportLight(gameScene, lightNodes[i], babylonScene);
+#else
+                    ExportLight(gameScene, lightNodes[new IntPtr(i)], babylonScene);
+#endif
                 CheckCancelled();
             }
 

+ 7 - 0
Exporters/3ds Max/Max2Babylon/Exporter/GlobalVertex.cs

@@ -12,6 +12,8 @@ namespace Max2Babylon
         public IPoint2 UV2 { get; set; }
         public int BonesIndices { get; set; }
         public IPoint4 Weights { get; set; }
+        public int BonesIndicesExtra { get; set; }
+        public IPoint4 WeightsExtra { get; set; }
         public float[] Color { get; set; }
 
         public override int GetHashCode()
@@ -58,6 +60,11 @@ namespace Max2Babylon
                 return false;
             }
 
+            if (WeightsExtra != null && !other.WeightsExtra.IsAlmostEqualTo(WeightsExtra, Tools.Epsilon))
+            {
+                return false;
+            }
+
             if (Color != null && !other.Color.IsAlmostEqualTo(Color, Tools.Epsilon))
             {
                 return false;

+ 8 - 0
Exporters/3ds Max/Max2Babylon/Forms/ActionsBuilderForm.cs

@@ -56,7 +56,11 @@ namespace Max2Babylon
             object[] names = new object[list.Count];
             for (int i = 0; i < list.Count; i++)
             {
+#if MAX2017
+                var indexer = i;
+#else
                 var indexer = new IntPtr(i);
+#endif
                 var node = list[indexer];
                 names[i] = node.MaxNode.Name;
             }
@@ -68,7 +72,11 @@ namespace Max2Babylon
             object[] names = new object[list.Count];
             for (int i = 0; i < list.Count; i++)
             {
+#if MAX2017
+                var indexer = i;
+#else
                 var indexer = new IntPtr(i);
+#endif
                 var node = list[indexer].MaxNode;
                 string soundFile = "";
 

+ 6 - 1
Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.cs

@@ -58,7 +58,12 @@ namespace Max2Babylon
             {
                 var node = Loader.Core.GetSelNode(index);
 
-                if (node.ObjectRef != null && node.ObjectRef.Eval(0).Obj.SuperClassID == SClass_ID.Geomobject)
+                if (node.ObjectRef != null && 
+                    (
+                    node.ObjectRef.Eval(0).Obj.SuperClassID == SClass_ID.Geomobject
+                    ||
+                    node.ObjectRef.Eval(0).Obj.SuperClassID == SClass_ID.Helper
+                    ))
                 {
                     objects.Add(node);
                 }

文件差異過大導致無法顯示
+ 834 - 834
Exporters/3ds Max/Max2Babylon/Tools/Tools.cs


+ 84 - 84
Exporters/3ds Max/Max2Babylon/Tools/VNormal.cs

@@ -1,84 +1,84 @@
-using Autodesk.Max;
-using SharpDX;
-
-namespace Max2Babylon
-{
-    public class VNormal
-    {
-        Vector3 norm;
-        uint smooth;
-        VNormal next;
-        bool init;
-
-        public VNormal()
-        {
-            smooth = 0;
-            next = null;
-            init = false;
-            norm = new Vector3(0, 0, 0);
-        }
-
-        public VNormal(Vector3 n, uint s)
-        {
-            next = null;
-            init = true;
-            norm = n;
-            smooth = s;
-        }
-
-        public void AddNormal(Vector3 n, uint s)
-        {
-            if (((s & smooth) == 0) && init)
-            {
-                if (next != null)
-                    next.AddNormal(n, s);
-                else
-                {
-                    next = new VNormal(n, s);
-                }
-            }
-            else
-            {
-                norm += n;
-                smooth |= s;
-                init = true;
-            }
-        }
-
-        public IPoint3 GetNormal(uint s)
-        {
-            if (((smooth & s) != 0) || next == null)
-                return norm.ToPoint3();
-
-            return next.GetNormal(s);
-        }
-
-        // Normalize each normal in the list
-        public void Normalize()
-        {
-            VNormal ptr = next;
-            VNormal prev = this;
-
-            while (ptr != null)
-            {
-                if ((ptr.smooth & smooth) != 0)
-                {
-                    norm += ptr.norm;
-                    prev.next = ptr.next;
-                    ptr = prev.next;
-                }
-                else
-                {
-                    prev = ptr;
-                    ptr = ptr.next;
-                }
-            }
-            norm.Normalize();
-
-            if (next != null)
-            {
-                next.Normalize();
-            }
-        }
-    }
-}
+using Autodesk.Max;
+using SharpDX;
+
+namespace Max2Babylon
+{
+    public class VNormal
+    {
+        Vector3 norm;
+        uint smooth;
+        VNormal next;
+        bool init;
+
+        public VNormal()
+        {
+            smooth = 0;
+            next = null;
+            init = false;
+            norm = new Vector3(0, 0, 0);
+        }
+
+        public VNormal(Vector3 n, uint s)
+        {
+            next = null;
+            init = true;
+            norm = n;
+            smooth = s;
+        }
+
+        public void AddNormal(Vector3 n, uint s)
+        {
+            if (((s & smooth) == 0) && init)
+            {
+                if (next != null)
+                    next.AddNormal(n, s);
+                else
+                {
+                    next = new VNormal(n, s);
+                }
+            }
+            else
+            {
+                norm += n;
+                smooth |= s;
+                init = true;
+            }
+        }
+
+        public IPoint3 GetNormal(uint s)
+        {
+            if (((smooth & s) != 0) || next == null)
+                return norm.ToPoint3();
+
+            return next.GetNormal(s);
+        }
+
+        // Normalize each normal in the list
+        public void Normalize()
+        {
+            VNormal ptr = next;
+            VNormal prev = this;
+
+            while (ptr != null)
+            {
+                if ((ptr.smooth & smooth) != 0)
+                {
+                    norm += ptr.norm;
+                    prev.next = ptr.next;
+                    ptr = prev.next;
+                }
+                else
+                {
+                    prev = ptr;
+                    ptr = ptr.next;
+                }
+            }
+            norm.Normalize();
+
+            if (next != null)
+            {
+                next.Normalize();
+            }
+        }
+    }
+}

+ 182 - 182
Exporters/3ds Max/Max2Babylon/Tools/WebServer.cs

@@ -1,182 +1,182 @@
-using System;
-using System.IO;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-using System.Web;
-
-namespace Max2Babylon
-{
-    public static class WebServer
-    {
-        private static readonly HttpListener listener;
-        private static Task runningTask;
-
-        const string HtmlResponseText = @"
-<!doctype html>
-<html>
-
-<head>
-    <title>Babylon.js</title>
-    <script type='text/javascript' src='http://www.babylonjs.com/oimo.js'></script>
-    <script type='text/javascript' src='http://www.babylonjs.com/cannon.js'></script>
-    <script type='text/javascript' src='http://www.babylonjs.com/babylon.js'></script>
-    <style type='text/css'>
-        html, body, div, canvas {
-            width: 100%;
-            height: 100%;
-            padding: 0;
-            margin: 0;
-            overflow: hidden;
-        }
-
-        #debugLayerButton {
-            position: absolute;
-            border: white solid 1px;
-            background: rgba(128, 128, 128, 0.3);
-            color: white;
-            left: 50%;
-            width: 100px;
-            margin-left:-50px;
-            bottom: 10px;
-        }
-    </style>
-</head>
-
-<body>
-    <canvas id='canvas'></canvas>
-    <button id='debugLayerButton'>Debug layer</button>
-    <script type='text/javascript'>
-        var canvas = document.getElementById('canvas');
-        var engine = new BABYLON.Engine(canvas, true);
-       
-        BABYLON.SceneLoader.Load('', '###SCENE###', engine, function (newScene) {
-            newScene.activeCamera.attachControl(canvas);
-
-            engine.runRenderLoop(function() {
-                newScene.render();
-            });
-
-            window.addEventListener('resize', function () {
-                engine.resize();
-            });
-
-            document.getElementById('debugLayerButton').addEventListener('click', function () {
-                if (newScene.debugLayer.isVisible()) {
-                    newScene.debugLayer.hide();
-                } else {
-                    newScene.debugLayer.show();
-                }
-            });
-        });
-    </script>
-</body>
-</html>";
-
-        public const int Port = 45478;
-
-        public static bool IsSupported { get; private set; }
-
-        static WebServer()
-        {
-            try
-            {
-                listener = new HttpListener();
-
-                if (!HttpListener.IsSupported)
-                {
-                    IsSupported = false;
-                    return;
-                }
-
-                listener.Prefixes.Add("http://localhost:" + Port + "/");
-                listener.Start();
-
-
-                runningTask = Task.Run(() => Listen());
-
-                IsSupported = true;
-            }
-            catch
-            {
-                IsSupported = false;
-            }
-        }
-
-        public static string SceneFilename { get; set; }
-        public static string SceneFolder { get; set; }
-        static Random r = new Random();
-        static void Listen()
-        {
-            try
-            {
-                while (listener.IsListening)
-                {
-                    var context = listener.GetContext();
-                    var request = context.Request;
-                    var url = request.Url;
-
-                    context.Response.AddHeader("Cache-Control", "no-cache");
-                    if (string.IsNullOrEmpty(url.LocalPath) || url.LocalPath == "/")
-                    {
-
-                        var responseText = HtmlResponseText.Replace("###SCENE###", SceneFilename+"?once="+r.Next());
-                        WriteResponse(context, responseText);
-                    }
-                    else
-                    {
-                        try
-                        {
-                            var path = Path.Combine(SceneFolder, HttpUtility.UrlDecode(url.PathAndQuery.Substring(1)));
-                            var questionMarkIndex = path.IndexOf("?");
-                            if (questionMarkIndex != -1)
-                            {
-                                path = path.Substring(0, questionMarkIndex);
-                            }
-                            var hashIndex = path.IndexOf("#");
-                            if (hashIndex != -1)
-                            {
-                                path = path.Substring(0, hashIndex);
-                            }
-                            var buffer = File.ReadAllBytes(path);
-                            WriteResponse(context, buffer);
-                        }
-                        catch
-                        {
-                            context.Response.StatusCode = 404;
-                            context.Response.Close();
-                        }
-                    }
-
-                }
-            }
-            catch
-            {
-            }
-        }
-
-        static void WriteResponse(HttpListenerContext context, string s)
-        {
-            WriteResponse(context.Response, s);
-        }
-
-        static void WriteResponse(HttpListenerContext context, byte[] buffer)
-        {
-            WriteResponse(context.Response, buffer);
-        }
-
-        static void WriteResponse(HttpListenerResponse response, string s)
-        {
-            byte[] buffer = Encoding.UTF8.GetBytes(s);
-            WriteResponse(response, buffer);
-        }
-
-        static void WriteResponse(HttpListenerResponse response, byte[] buffer)
-        {
-            response.ContentLength64 = buffer.Length;
-            Stream output = response.OutputStream;
-            output.Write(buffer, 0, buffer.Length);
-            output.Close();
-        }
-    }
-}
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace Max2Babylon
+{
+    public static class WebServer
+    {
+        private static readonly HttpListener listener;
+        private static Task runningTask;
+
+        const string HtmlResponseText = @"
+<!doctype html>
+<html>
+
+<head>
+    <title>Babylon.js</title>
+    <script type='text/javascript' src='http://www.babylonjs.com/oimo.js'></script>
+    <script type='text/javascript' src='http://www.babylonjs.com/cannon.js'></script>
+    <script type='text/javascript' src='http://www.babylonjs.com/babylon.js'></script>
+    <style type='text/css'>
+        html, body, div, canvas {
+            width: 100%;
+            height: 100%;
+            padding: 0;
+            margin: 0;
+            overflow: hidden;
+        }
+
+        #debugLayerButton {
+            position: absolute;
+            border: white solid 1px;
+            background: rgba(128, 128, 128, 0.3);
+            color: white;
+            left: 50%;
+            width: 100px;
+            margin-left:-50px;
+            bottom: 10px;
+        }
+    </style>
+</head>
+
+<body>
+    <canvas id='canvas'></canvas>
+    <button id='debugLayerButton'>Debug layer</button>
+    <script type='text/javascript'>
+        var canvas = document.getElementById('canvas');
+        var engine = new BABYLON.Engine(canvas, true);
+       
+        BABYLON.SceneLoader.Load('', '###SCENE###', engine, function (newScene) {
+            newScene.activeCamera.attachControl(canvas);
+
+            engine.runRenderLoop(function() {
+                newScene.render();
+            });
+
+            window.addEventListener('resize', function () {
+                engine.resize();
+            });
+
+            document.getElementById('debugLayerButton').addEventListener('click', function () {
+                if (newScene.debugLayer.isVisible()) {
+                    newScene.debugLayer.hide();
+                } else {
+                    newScene.debugLayer.show();
+                }
+            });
+        });
+    </script>
+</body>
+</html>";
+
+        public const int Port = 45478;
+
+        public static bool IsSupported { get; private set; }
+
+        static WebServer()
+        {
+            try
+            {
+                listener = new HttpListener();
+
+                if (!HttpListener.IsSupported)
+                {
+                    IsSupported = false;
+                    return;
+                }
+
+                listener.Prefixes.Add("http://localhost:" + Port + "/");
+                listener.Start();
+
+
+                runningTask = Task.Run(() => Listen());
+
+                IsSupported = true;
+            }
+            catch
+            {
+                IsSupported = false;
+            }
+        }
+
+        public static string SceneFilename { get; set; }
+        public static string SceneFolder { get; set; }
+        static Random r = new Random();
+        static void Listen()
+        {
+            try
+            {
+                while (listener.IsListening)
+                {
+                    var context = listener.GetContext();
+                    var request = context.Request;
+                    var url = request.Url;
+
+                    context.Response.AddHeader("Cache-Control", "no-cache");
+                    if (string.IsNullOrEmpty(url.LocalPath) || url.LocalPath == "/")
+                    {
+
+                        var responseText = HtmlResponseText.Replace("###SCENE###", SceneFilename+"?once="+r.Next());
+                        WriteResponse(context, responseText);
+                    }
+                    else
+                    {
+                        try
+                        {
+                            var path = Path.Combine(SceneFolder, HttpUtility.UrlDecode(url.PathAndQuery.Substring(1)));
+                            var questionMarkIndex = path.IndexOf("?");
+                            if (questionMarkIndex != -1)
+                            {
+                                path = path.Substring(0, questionMarkIndex);
+                            }
+                            var hashIndex = path.IndexOf("#");
+                            if (hashIndex != -1)
+                            {
+                                path = path.Substring(0, hashIndex);
+                            }
+                            var buffer = File.ReadAllBytes(path);
+                            WriteResponse(context, buffer);
+                        }
+                        catch
+                        {
+                            context.Response.StatusCode = 404;
+                            context.Response.Close();
+                        }
+                    }
+
+                }
+            }
+            catch
+            {
+            }
+        }
+
+        static void WriteResponse(HttpListenerContext context, string s)
+        {
+            WriteResponse(context.Response, s);
+        }
+
+        static void WriteResponse(HttpListenerContext context, byte[] buffer)
+        {
+            WriteResponse(context.Response, buffer);
+        }
+
+        static void WriteResponse(HttpListenerResponse response, string s)
+        {
+            byte[] buffer = Encoding.UTF8.GetBytes(s);
+            WriteResponse(response, buffer);
+        }
+
+        static void WriteResponse(HttpListenerResponse response, byte[] buffer)
+        {
+            response.ContentLength64 = buffer.Length;
+            Stream output = response.OutputStream;
+            output.Write(buffer, 0, buffer.Length);
+            output.Close();
+        }
+    }
+}

+ 2 - 2
Tools/Gulp/package.json

@@ -1,6 +1,6 @@
 {
   "name": "BabylonJS",
-  "version": "2.2.0",
+  "version": "2.4.0",
   "description": "Babylon.js is a 3D engine based on webgl and javascript",
   "main": "",
   "repository": { "url": "https://github.com/BabylonJS/Babylon.js/" },
@@ -11,7 +11,7 @@
     "gulp-uglify": "~1.2.0",
     "gulp-sourcemaps": "~1.5.2",
     "typescript": "^1.7.5",
-    "gulp-typescript": "~2.9.0",
+    "gulp-typescript": "~2.13.0",
     "through2": "~0.6.5",
     "gulp-util": "~3.0.4",
     "gulp-concat": "~2.5.2",

文件差異過大導致無法顯示
+ 26 - 25
dist/preview release/babylon.core.js


文件差異過大導致無法顯示
+ 3332 - 3293
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 32 - 32
dist/preview release/babylon.js


文件差異過大導致無法顯示
+ 248 - 142
dist/preview release/babylon.max.js


文件差異過大導致無法顯示
+ 31 - 31
dist/preview release/babylon.noworker.js


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

@@ -3,20 +3,21 @@
     - New refraction channel for Standard material (including fresnel support). Refraction texture can be provided by a reflection probe or a refraction texture. [See demo here](http://www.babylonjs.com/Demos/refraction/) ([deltakosh](https://github.com/deltakosh))
     - Added support for HDR cubemaps ([sebavan](https://github.com/sebavan))
     - Support for shaders includes ([deltakosh](https://github.com/deltakosh))
-    - new mesh type : `LineSystem` ([jerome](https://github.com/jbousquie))
+    - New mesh type : `LineSystem` ([jerome](https://github.com/jbousquie))
     - SerializationHelper for complex classes using TypeScript decorators ([deltakosh](https://github.com/deltakosh))
     - StandardMaterial now supports Parallax and Parallax Occlusion Mapping ([nockawa](https://github.com/nockawa))
-    - Animations blending. See [demo here](http://www.babylonjs-playground.com/#2BLI9T#3). More [info here](NEED DOC!) ([deltakosh](https://github.com/deltakosh))
-    - New debuger tool: SkeletonViewer. See [demo here](Demo available here: http://www.babylonjs-playground.com/#1BZJVJ#8) (Adam & [deltakosh](https://github.com/deltakosh))
-    - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance [gleborgne](https://github.com/gleborgne)
+    - Animations blending. See [demo here](http://www.babylonjs-playground.com/#2BLI9T#3). More [info here](http://doc.babylonjs.com/tutorials/Animations#animation-blending) ([deltakosh](https://github.com/deltakosh))
+    - New debuger tool: SkeletonViewer. See [demo here](http://www.babylonjs-playground.com/#1BZJVJ#8) (Adam & [deltakosh](https://github.com/deltakosh))
+    - Added Camera Inputs Manager to manage camera inputs (mouse, touch, keyboard, gamepad, ...) in a composable way, without relying on class inheritance ([gleborgne](https://github.com/gleborgne))
     - Introduced new observable system to handle events ([nockawa](https://github.com/nockawa), [deltakosh](https://github.com/deltakosh))
-    - Added a new VR camera : VRDeviceOrientationArcRotateCamera [temechon](https://github.com/Temechon)
+    - Added a new VR camera : VRDeviceOrientationArcRotateCamera ([temechon](https://github.com/Temechon))
     - Unity3D exporter: Added support for lightmaps ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Unity3D exporter: Added support for export and run (local webserver) ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Moved PBR Material to core ([deltakosh](https://github.com/deltakosh))
+    - StandardMaterial.maxSimultaneousLights can define how many dynamic lights the material can handle ([deltakosh](https://github.com/deltakosh))
   - **Updates**
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
-    - Added skeleton.getBoneIndexByName(boneName: string) [dad72](https://github.com/dad72)
+    - Added skeleton.getBoneIndexByName(boneName: string) ([dad72](https://github.com/dad72))
     - Added node._children to track children hierarchy ([deltakosh](https://github.com/deltakosh))
     - Added Camera.ForceAttachControlToAlwaysPreventDefault to help embedding Babylon.js in iFrames ([deltakosh](https://github.com/deltakosh))
     - Support for Layer.alphaTest ([deltakosh](https://github.com/deltakosh))
@@ -31,11 +32,13 @@
     - LinesMesh class now supports Intersection. Added the intersectionThreshold property to set a tolerance margin during intersection with wire lines. ([nockawa](https://github.com/nockawa))
     - Geometry.boundingBias property to enlarge the boundingInfo objects ([nockawa](https://github.com/nockawa))
     - Tools.ExtractMinAndMax & ExtractMinAndMaxIndexed now supports an optional Bias for Extent computation.
-	- Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([nockawa](https://github.com/nockawa))
-	- Added RectanglePackingMap class to fix several rectangles in a big map in the most optimal way. ([nockawa](https://github.com/nockawa))
-	- Added DynamicFloatArray class to store float32 based elements of a given size (stride) into one big Float32Array, with allocation/free/pack operations to then access an optimal buffer that can be used to update a WebGLBuffer dynamically. ([nockawa](https://github.com/nockawa))
-	- Scene.onPointerObservable property added to enable a unique Observable event for user input (see ArcRotateCamera inputs for examples) ([nockawa](https://github.com/nockawa))
-
+	  - Added StringDictionary<T> class to implement an efficient generic typed string dictionary based on Javascript associative array. ([nockawa](https://github.com/nockawa))
+	  - Added RectanglePackingMap class to fix several rectangles in a big map in the most optimal way. ([nockawa](https://github.com/nockawa))
+	  - Added DynamicFloatArray class to store float32 based elements of a given size (stride) into one big Float32Array, with allocation/free/pack operations to then access an optimal buffer that can be used to update a WebGLBuffer dynamically. ([nockawa](https://github.com/nockawa))
+	  - Scene.onPointerObservable property added to enable a unique Observable event for user input (see ArcRotateCamera inputs for examples) ([nockawa](https://github.com/nockawa))
+  - **Exporters**
+    - Support for 3dsmax 2017 ([deltakosh](https://github.com/deltakosh))
+    - Added support for up to 8 bones influences per vertex for 3dsmax exporter ([deltakosh](https://github.com/deltakosh))
   - **API doc**
     - class `SolidParticleSystem` documented ([jerome](https://github.com/jbousquie))
     - class `MeshBuilder` documented ([jerome](https://github.com/jbousquie))
@@ -46,5 +49,5 @@
     - Fixed bug with OBJ Loader - All meshes were concatenated with the previous one ([Temechon](https://github.com/Temechon))
   - **Breaking changes**
     - `VertexData.CreateLines()` removed as `MeshBuilder.CreateLines()` now calls `MeshBuilder.CreateLineSystem()`
-    - `scene.onNewXXXAdded` and `scene.onXXXRemoved' callbacks were removed and replaced by `scene.onNewXXXAddedObservable` and `scene.onXXXRemovedObservable`
+    - `scene.onNewXXXAdded` and `scene.onXXXRemoved` callbacks were removed and replaced by `scene.onNewXXXAddedObservable` and `scene.onXXXRemovedObservable`
     - `Material.dispose` does not dispose textures by default. You have to call `material.dispose(false, true)` to get the previous behavior.

文件差異過大導致無法顯示
+ 15 - 47
materialsLibrary/dist/babylon.simpleMaterial.js


文件差異過大導致無法顯示
+ 1 - 1
materialsLibrary/dist/babylon.simpleMaterial.min.js


+ 1 - 0
materialsLibrary/dist/dts/babylon.simpleMaterial.d.ts

@@ -4,6 +4,7 @@ declare module BABYLON {
         diffuseTexture: BaseTexture;
         diffuseColor: Color3;
         disableLighting: boolean;
+        maxSimultaneousLights: number;
         private _worldViewProjectionMatrix;
         private _scaledDiffuse;
         private _renderId;

+ 17 - 50
materialsLibrary/materials/simple/babylon.simpleMaterial.ts

@@ -9,39 +9,6 @@ module BABYLON {
         public ALPHATEST = false;
         public POINTSIZE = false;
         public FOG = false;
-        public LIGHT0 = false;
-        public LIGHT1 = false;
-        public LIGHT2 = false;
-        public LIGHT3 = false;
-        public SPOTLIGHT0 = false;
-        public SPOTLIGHT1 = false;
-        public SPOTLIGHT2 = false;
-        public SPOTLIGHT3 = false;
-        public HEMILIGHT0 = false;
-        public HEMILIGHT1 = false;
-        public HEMILIGHT2 = false;
-        public HEMILIGHT3 = false;
-        public DIRLIGHT0 = false;
-        public DIRLIGHT1 = false;
-        public DIRLIGHT2 = false;
-        public DIRLIGHT3 = false;
-        public POINTLIGHT0 = false;
-        public POINTLIGHT1 = false;
-        public POINTLIGHT2 = false;
-        public POINTLIGHT3 = false;        
-        public SHADOW0 = false;
-        public SHADOW1 = false;
-        public SHADOW2 = false;
-        public SHADOW3 = false;
-        public SHADOWS = false;
-        public SHADOWVSM0 = false;
-        public SHADOWVSM1 = false;
-        public SHADOWVSM2 = false;
-        public SHADOWVSM3 = false;
-        public SHADOWPCF0 = false;
-        public SHADOWPCF1 = false;
-        public SHADOWPCF2 = false;
-        public SHADOWPCF3 = false;
         public NORMAL = false;
         public UV1 = false;
         public UV2 = false;
@@ -53,7 +20,7 @@ module BABYLON {
 
         constructor() {
             super();
-            this._keys = Object.keys(this);
+            this.rebuild();
         }
     }
 
@@ -65,7 +32,10 @@ module BABYLON {
         public diffuseColor = new Color3(1, 1, 1);
         
         @serialize()
-        public disableLighting = false;
+        public disableLighting = false;        
+        
+        @serialize()
+        public maxSimultaneousLights = 4;
 
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _scaledDiffuse = new Color3();
@@ -163,9 +133,8 @@ module BABYLON {
                 this._defines.FOG = true;
             }
 
-            var lightIndex = 0;
             if (scene.lightsEnabled && !this.disableLighting) {
-                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines);
+                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, this.maxSimultaneousLights);
             }
 
             // Attribs
@@ -211,7 +180,7 @@ module BABYLON {
                     fallbacks.addFallback(1, "FOG");
                 }
 
-                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks);
+                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, this.maxSimultaneousLights);
                 
                 if (this._defines.NUM_BONE_INFLUENCERS > 0) {
                     fallbacks.addCPUSkinningFallback(0, mesh);
@@ -241,23 +210,21 @@ module BABYLON {
 
                 var shaderName = "simple";
                 var join = this._defines.toString();
-                this._effect = scene.getEngine().createEffect(shaderName,
-                    attribs,
-                    ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vDiffuseColor",
-                        "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
-                        "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
-                        "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
-                        "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vDiffuseColor",
                         "vFogInfos", "vFogColor", "pointSize",
                         "vDiffuseInfos", 
                         "mBones",
-                        "vClipPlane", "diffuseMatrix",
-                        "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues"
-                    ],
+                        "vClipPlane", "diffuseMatrix", "depthValues"
+                    ];
+                    
+                MaterialHelper.PrepareUniformsListForList(uniforms, this._defines, this.maxSimultaneousLights);
+                
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs, uniforms,
                     ["diffuseSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
                     ],
-                    join, fallbacks, this.onCompiled, this.onError);
+                    join, fallbacks, this.onCompiled, this.onError, {maxSimultaneousLights: this.maxSimultaneousLights});
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -315,7 +282,7 @@ module BABYLON {
 
             // Lights
             if (scene.lightsEnabled && !this.disableLighting) {
-                MaterialHelper.BindLights(scene, mesh, this._effect, this._defines);          
+                MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, this.maxSimultaneousLights);          
             }
 
             // View

+ 2 - 8
materialsLibrary/materials/simple/simple.fragment.fx

@@ -16,10 +16,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..maxSimultaneousLights]
 
 
 #include<lightsFragmentFunctions>
@@ -77,10 +74,7 @@ void main(void) {
 	float shadow = 1.;
     float glossiness = 0.;
     
-#include<lightFragment>[0]
-#include<lightFragment>[1]
-#include<lightFragment>[2]
-#include<lightFragment>[3]
+#include<lightFragment>[0..maxSimultaneousLights]
 
 
 #ifdef VERTEXALPHA

文件差異過大導致無法顯示
+ 4551 - 849
materialsLibrary/test/refs/babylon.max.js


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

@@ -16,9 +16,9 @@ var BABYLON;
         };
         FreeCameraVRDeviceOrientationInput.prototype._onOrientationEvent = function (evt) {
             var camera = this.camera;
-            this._alpha = +evt.alpha | 0;
-            this._beta = +evt.beta | 0;
-            this._gamma = +evt.gamma | 0;
+            this._alpha = evt.alpha;
+            this._beta = evt.beta;
+            this._gamma = evt.gamma;
             this._dirty = true;
         };
         FreeCameraVRDeviceOrientationInput.prototype.checkInputs = function () {

+ 12 - 12
src/Cameras/Inputs/babylon.freecamera.input.vrdeviceorientation.ts

@@ -10,28 +10,28 @@ module BABYLON {
         private _beta = 0;
         private _gamma = 0;
         private _dirty = false;
-    
+
         private _offsetOrientation: { yaw: number; pitch: number; roll: number };
         private _deviceOrientationHandler;
-        
+
         constructor() {
             this._deviceOrientationHandler = this._onOrientationEvent.bind(this);
         }
 
-        attachControl(element : HTMLElement, noPreventDefault?: boolean) {
+        attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             window.addEventListener("deviceorientation", this._deviceOrientationHandler);
         }
 
         public _onOrientationEvent(evt: DeviceOrientationEvent): void {
             var camera = this.camera;
-            this._alpha = +evt.alpha | 0;
-            this._beta = +evt.beta | 0;
-            this._gamma = +evt.gamma | 0;
+            this._alpha = evt.alpha;
+            this._beta = evt.beta;
+            this._gamma = evt.gamma;
             this._dirty = true;
         }
 
         public checkInputs() {
-            if (this._dirty){
+            if (this._dirty) {
                 this._dirty = false;
                 var rotationX = this._gamma;
                 if (rotationX < 0) {
@@ -46,20 +46,20 @@ module BABYLON {
                 this.camera.rotation.y = this.alphaCorrection * -this._alpha / 180.0 * Math.PI;
                 this.camera.rotation.z = this.betaCorrection * this._beta / 180.0 * Math.PI;
             }
-        }              
+        }
 
-        detachControl(element : HTMLElement) {
+        detachControl(element: HTMLElement) {
             window.removeEventListener("deviceorientation", this._deviceOrientationHandler);
         }
 
         getTypeName(): string {
             return "FreeCameraVRDeviceOrientationInput";
         }
-        
-        getSimpleName(){
+
+        getSimpleName() {
             return "VRDeviceOrientation";
         }
     }
-    
+
     CameraInputTypes["FreeCameraVRDeviceOrientationInput"] = FreeCameraVRDeviceOrientationInput;
 }

+ 3 - 0
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -272,6 +272,9 @@ var BABYLON;
                             log += "\n\t\t" + animation.toString(fullDetails);
                         }
                     }
+                    if (parsedData.autoAnimate) {
+                        scene.beginAnimation(scene, parsedData.autoAnimateFrom, parsedData.autoAnimateTo, parsedData.autoAnimateLoop, parsedData.autoAnimateSpeed || 1.0);
+                    }
                     // Materials
                     if (parsedData.materials) {
                         for (index = 0, cache = parsedData.materials.length; index < cache; index++) {

+ 4 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -280,6 +280,10 @@
                         log += "\n\t\t" + animation.toString(fullDetails);
                     }
                 }
+
+                if (parsedData.autoAnimate) {
+                    scene.beginAnimation(scene, parsedData.autoAnimateFrom, parsedData.autoAnimateTo, parsedData.autoAnimateLoop, parsedData.autoAnimateSpeed || 1.0);
+                }
     
                 // Materials
                 if (parsedData.materials) {

+ 19 - 2
src/Materials/babylon.effect.js

@@ -49,7 +49,7 @@ var BABYLON;
     })();
     BABYLON.EffectFallbacks = EffectFallbacks;
     var Effect = (function () {
-        function Effect(baseName, attributesNames, uniformsNames, samplers, engine, defines, fallbacks, onCompiled, onError) {
+        function Effect(baseName, attributesNames, uniformsNames, samplers, engine, defines, fallbacks, onCompiled, onError, indexParameters) {
             var _this = this;
             this._isReady = false;
             this._compilationError = "";
@@ -62,6 +62,7 @@ var BABYLON;
             this._attributesNames = attributesNames;
             this.onError = onError;
             this.onCompiled = onCompiled;
+            this._indexParameters = indexParameters;
             var vertexSource;
             var fragmentSource;
             if (baseName.vertexElement) {
@@ -206,7 +207,23 @@ var BABYLON;
                         }
                     }
                     if (match[4]) {
-                        includeContent = includeContent.replace(/\{X\}/g, match[5]);
+                        var indexString = match[5];
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
+                            }
+                            for (var i = minIndex; i <= maxIndex; i++) {
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
+                            }
+                        }
+                        else {
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
                     // Replace
                     returnValue = returnValue.replace(match[0], includeContent);

+ 23 - 2
src/Materials/babylon.effect.ts

@@ -73,11 +73,12 @@
         private _attributes: number[];
         private _uniforms: WebGLUniformLocation[];
         public _key: string;
+        private _indexParameters: any;
 
         private _program: WebGLProgram;
         private _valueCache = [];
 
-        constructor(baseName: any, attributesNames: string[], uniformsNames: string[], samplers: string[], engine, defines?: string, fallbacks?: EffectFallbacks, onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void) {
+        constructor(baseName: any, attributesNames: string[], uniformsNames: string[], samplers: string[], engine, defines?: string, fallbacks?: EffectFallbacks, onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void, indexParameters?: any) {
             this._engine = engine;
             this.name = baseName;
             this.defines = defines;
@@ -88,6 +89,8 @@
             this.onError = onError;
             this.onCompiled = onCompiled;
 
+            this._indexParameters = indexParameters;
+
             var vertexSource;
             var fragmentSource;
 
@@ -260,7 +263,25 @@
                     }
 
                     if (match[4]) {
-                        includeContent = includeContent.replace(/\{X\}/g, match[5]);
+                        var indexString = match[5];
+
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
+                            }
+
+                            for (var i = minIndex; i <= maxIndex; i++) {
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
+                            }
+                        } else {
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
 
                     // Replace

+ 12 - 0
src/Materials/babylon.material.js

@@ -9,7 +9,16 @@ var BABYLON;
     var MaterialDefines = (function () {
         function MaterialDefines() {
         }
+        MaterialDefines.prototype.rebuild = function () {
+            if (this._keys) {
+                delete this._keys;
+            }
+            this._keys = Object.keys(this);
+        };
         MaterialDefines.prototype.isEqual = function (other) {
+            if (this._keys.length !== other._keys.length) {
+                return false;
+            }
             for (var index = 0; index < this._keys.length; index++) {
                 var prop = this._keys[index];
                 if (this[prop] !== other[prop]) {
@@ -19,6 +28,9 @@ var BABYLON;
             return true;
         };
         MaterialDefines.prototype.cloneTo = function (other) {
+            if (this._keys.length !== other._keys.length) {
+                other._keys = this._keys.slice(0);
+            }
             for (var index = 0; index < this._keys.length; index++) {
                 var prop = this._keys[index];
                 other[prop] = this[prop];

+ 15 - 0
src/Materials/babylon.material.ts

@@ -2,7 +2,18 @@
     export class MaterialDefines {
         _keys: string[];
 
+        public rebuild() {
+            if (this._keys) {
+                delete this._keys;
+            }
+            this._keys = Object.keys(this);
+        } 
+
         public isEqual(other: MaterialDefines): boolean {
+            if (this._keys.length !== other._keys.length) {
+                return false;
+            }
+
             for (var index = 0; index < this._keys.length; index++) {
                 var prop = this._keys[index];
 
@@ -15,6 +26,10 @@
         }
 
         public cloneTo(other: MaterialDefines): void {
+            if (this._keys.length !== other._keys.length) {
+                other._keys = this._keys.slice(0);
+            }
+
             for (var index = 0; index < this._keys.length; index++) {
                 var prop = this._keys[index];
 

+ 35 - 5
src/Materials/babylon.materialHelper.js

@@ -1,12 +1,13 @@
 var BABYLON;
 (function (BABYLON) {
-    var maxSimultaneousLights = 4;
     var MaterialHelper = (function () {
         function MaterialHelper() {
         }
-        MaterialHelper.PrepareDefinesForLights = function (scene, mesh, defines) {
+        MaterialHelper.PrepareDefinesForLights = function (scene, mesh, defines, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
             var lightIndex = 0;
             var needNormals = false;
+            var needRebuild = false;
             for (var index = 0; index < scene.lights.length; index++) {
                 var light = scene.lights[index];
                 if (!light.isEnabled()) {
@@ -36,6 +37,9 @@ var BABYLON;
                     continue;
                 }
                 needNormals = true;
+                if (defines["LIGHT" + lightIndex] === undefined) {
+                    needRebuild = true;
+                }
                 defines["LIGHT" + lightIndex] = true;
                 var type;
                 if (light instanceof BABYLON.SpotLight) {
@@ -50,6 +54,9 @@ var BABYLON;
                 else {
                     type = "DIRLIGHT" + lightIndex;
                 }
+                if (defines[type] === undefined) {
+                    needRebuild = true;
+                }
                 defines[type] = true;
                 // Specular
                 if (!light.specular.equalsFloats(0, 0, 0) && defines["SPECULARTERM"] !== undefined) {
@@ -59,12 +66,21 @@ var BABYLON;
                 if (scene.shadowsEnabled) {
                     var shadowGenerator = light.getShadowGenerator();
                     if (mesh && mesh.receiveShadows && shadowGenerator) {
+                        if (defines["SHADOW" + lightIndex] === undefined) {
+                            needRebuild = true;
+                        }
                         defines["SHADOW" + lightIndex] = true;
                         defines["SHADOWS"] = true;
                         if (shadowGenerator.useVarianceShadowMap || shadowGenerator.useBlurVarianceShadowMap) {
+                            if (defines["SHADOWVSM" + lightIndex] === undefined) {
+                                needRebuild = true;
+                            }
                             defines["SHADOWVSM" + lightIndex] = true;
                         }
                         if (shadowGenerator.usePoissonSampling) {
+                            if (defines["SHADOWPCF" + lightIndex] === undefined) {
+                                needRebuild = true;
+                            }
                             defines["SHADOWPCF" + lightIndex] = true;
                         }
                     }
@@ -73,12 +89,25 @@ var BABYLON;
                 if (lightIndex === maxSimultaneousLights)
                     break;
             }
+            if (needRebuild) {
+                defines.rebuild();
+            }
             return needNormals;
         };
-        MaterialHelper.HandleFallbacksForShadows = function (defines, fallbacks) {
+        MaterialHelper.PrepareUniformsListForList = function (uniformsList, defines, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
             for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
                 if (!defines["LIGHT" + lightIndex]) {
-                    continue;
+                    break;
+                }
+                uniformsList.push("vLightData" + lightIndex, "vLightDiffuse" + lightIndex, "vLightSpecular" + lightIndex, "vLightDirection" + lightIndex, "vLightGround" + lightIndex, "lightMatrix" + lightIndex, "shadowsInfo" + lightIndex);
+            }
+        };
+        MaterialHelper.HandleFallbacksForShadows = function (defines, fallbacks, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
+            for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                if (!defines["LIGHT" + lightIndex]) {
+                    break;
                 }
                 if (lightIndex > 0) {
                     fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
@@ -149,7 +178,8 @@ var BABYLON;
                 light.transferToEffect(effect, "vLightData" + lightIndex, "vLightGround" + lightIndex);
             }
         };
-        MaterialHelper.BindLights = function (scene, mesh, effect, defines) {
+        MaterialHelper.BindLights = function (scene, mesh, effect, defines, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
             var lightIndex = 0;
             var depthValuesAlreadySet = false;
             for (var index = 0; index < scene.lights.length; index++) {

+ 48 - 6
src/Materials/babylon.materialHelper.ts

@@ -1,10 +1,10 @@
 module BABYLON {
-    var maxSimultaneousLights = 4;
-
     export class MaterialHelper {
-        public static PrepareDefinesForLights(scene: Scene, mesh: AbstractMesh, defines: MaterialDefines): boolean {
+        public static PrepareDefinesForLights(scene: Scene, mesh: AbstractMesh, defines: MaterialDefines, maxSimultaneousLights = 4): boolean {
             var lightIndex = 0;
             var needNormals = false;
+            var needRebuild = false;
+
             for (var index = 0; index < scene.lights.length; index++) {
                 var light = scene.lights[index];
 
@@ -42,6 +42,11 @@
                     continue;
                 }
                 needNormals = true;
+
+                if (defines["LIGHT" + lightIndex] === undefined) {
+                    needRebuild = true;
+                }
+
                 defines["LIGHT" + lightIndex] = true;
 
                 var type;
@@ -55,6 +60,10 @@
                     type = "DIRLIGHT" + lightIndex;
                 }
 
+                if (defines[type] === undefined) {
+                    needRebuild = true;
+                }
+
                 defines[type] = true;
 
                 // Specular
@@ -66,15 +75,26 @@
                 if (scene.shadowsEnabled) {
                     var shadowGenerator = light.getShadowGenerator();
                     if (mesh && mesh.receiveShadows && shadowGenerator) {
+                        if (defines["SHADOW" + lightIndex] === undefined) {
+                            needRebuild = true;
+                        }
                         defines["SHADOW" + lightIndex] = true;
 
                         defines["SHADOWS"] = true;
 
                         if (shadowGenerator.useVarianceShadowMap || shadowGenerator.useBlurVarianceShadowMap) {
+                            if (defines["SHADOWVSM" + lightIndex] === undefined) {
+                                needRebuild = true;
+                            }
+
                             defines["SHADOWVSM" + lightIndex] = true;
                         }
 
                         if (shadowGenerator.usePoissonSampling) {
+                            if (defines["SHADOWPCF" + lightIndex] === undefined) {
+                                needRebuild = true;
+                            }
+
                             defines["SHADOWPCF" + lightIndex] = true;
                         }
                     }
@@ -85,13 +105,35 @@
                     break;
             }
 
+            if (needRebuild) {
+                defines.rebuild();
+            }
+
             return needNormals;
         }
 
-        public static HandleFallbacksForShadows(defines: MaterialDefines, fallbacks: EffectFallbacks): void {
+        public static PrepareUniformsListForList(uniformsList: string[], defines: MaterialDefines, maxSimultaneousLights = 4): void {
             for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
                 if (!defines["LIGHT" + lightIndex]) {
-                    continue;
+                    break;
+                }
+
+                uniformsList.push(
+                    "vLightData" + lightIndex,
+                    "vLightDiffuse" + lightIndex,
+                    "vLightSpecular" + lightIndex,
+                    "vLightDirection" + lightIndex,
+                    "vLightGround" + lightIndex,
+                    "lightMatrix" + lightIndex,
+                    "shadowsInfo" + lightIndex
+                );
+            }
+        }
+
+        public static HandleFallbacksForShadows(defines: MaterialDefines, fallbacks: EffectFallbacks, maxSimultaneousLights = 4): void {
+            for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                if (!defines["LIGHT" + lightIndex]) {
+                    break;
                 }
 
                 if (lightIndex > 0) {
@@ -169,7 +211,7 @@
             }
         }
 
-        public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines) {
+        public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines, maxSimultaneousLights = 4) {
             var lightIndex = 0;
             var depthValuesAlreadySet = false;
             for (var index = 0; index < scene.lights.length; index++) {

+ 14 - 45
src/Materials/babylon.standardMaterial.js

@@ -31,40 +31,7 @@ var BABYLON;
             this.ALPHAFROMDIFFUSE = false;
             this.POINTSIZE = false;
             this.FOG = false;
-            this.LIGHT0 = false;
-            this.LIGHT1 = false;
-            this.LIGHT2 = false;
-            this.LIGHT3 = false;
-            this.SPOTLIGHT0 = false;
-            this.SPOTLIGHT1 = false;
-            this.SPOTLIGHT2 = false;
-            this.SPOTLIGHT3 = false;
-            this.HEMILIGHT0 = false;
-            this.HEMILIGHT1 = false;
-            this.HEMILIGHT2 = false;
-            this.HEMILIGHT3 = false;
-            this.POINTLIGHT0 = false;
-            this.POINTLIGHT1 = false;
-            this.POINTLIGHT2 = false;
-            this.POINTLIGHT3 = false;
-            this.DIRLIGHT0 = false;
-            this.DIRLIGHT1 = false;
-            this.DIRLIGHT2 = false;
-            this.DIRLIGHT3 = false;
             this.SPECULARTERM = false;
-            this.SHADOW0 = false;
-            this.SHADOW1 = false;
-            this.SHADOW2 = false;
-            this.SHADOW3 = false;
-            this.SHADOWS = false;
-            this.SHADOWVSM0 = false;
-            this.SHADOWVSM1 = false;
-            this.SHADOWVSM2 = false;
-            this.SHADOWVSM3 = false;
-            this.SHADOWPCF0 = false;
-            this.SHADOWPCF1 = false;
-            this.SHADOWPCF2 = false;
-            this.SHADOWPCF3 = false;
             this.DIFFUSEFRESNEL = false;
             this.OPACITYFRESNEL = false;
             this.REFLECTIONFRESNEL = false;
@@ -100,7 +67,7 @@ var BABYLON;
             this.REFRACTION = false;
             this.REFRACTIONMAP_3D = false;
             this.REFLECTIONOVERALPHA = false;
-            this._keys = Object.keys(this);
+            this.rebuild();
         }
         return StandardMaterialDefines;
     })(BABYLON.MaterialDefines);
@@ -129,6 +96,7 @@ var BABYLON;
             this.invertRefractionY = true;
             this.useLightmapAsShadowmap = false;
             this.useGlossinessFromSpecularMapAlpha = false;
+            this.maxSimultaneousLights = 4;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
@@ -360,7 +328,7 @@ var BABYLON;
                 this._defines.FOG = true;
             }
             if (scene.lightsEnabled && !this.disableLighting) {
-                needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines);
+                needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, this.maxSimultaneousLights);
             }
             if (StandardMaterial.FresnelEnabled) {
                 // Fresnel
@@ -455,7 +423,7 @@ var BABYLON;
                 if (this._defines.LOGARITHMICDEPTH) {
                     fallbacks.addFallback(0, "LOGARITHMICDEPTH");
                 }
-                BABYLON.MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks);
+                BABYLON.MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, this.maxSimultaneousLights);
                 if (this._defines.SPECULARTERM) {
                     fallbacks.addFallback(0, "SPECULARTERM");
                 }
@@ -496,21 +464,19 @@ var BABYLON;
                     shaderName = "legacydefault";
                 }
                 var join = this._defines.toString();
-                this._effect = scene.getEngine().createEffect(shaderName, attribs, ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
-                    "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
-                    "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
-                    "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
-                    "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
                     "vFogInfos", "vFogColor", "pointSize",
                     "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
                     "mBones",
                     "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                    "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues",
+                    "depthValues",
                     "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
                     "logarithmicDepthConstant"
-                ], ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
+                ];
+                BABYLON.MaterialHelper.PrepareUniformsListForList(uniforms, this._defines, this.maxSimultaneousLights);
+                this._effect = scene.getEngine().createEffect(shaderName, attribs, uniforms, ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                     "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
-                ], join, fallbacks, this.onCompiled, this.onError);
+                ], join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights - 1 });
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -649,7 +615,7 @@ var BABYLON;
                 this._effect.setColor4("vDiffuseColor", this.diffuseColor, this.alpha * mesh.visibility);
                 // Lights
                 if (scene.lightsEnabled && !this.disableLighting) {
-                    BABYLON.MaterialHelper.BindLights(scene, mesh, this._effect, this._defines);
+                    BABYLON.MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, this.maxSimultaneousLights);
                 }
                 // View
                 if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== BABYLON.Scene.FOGMODE_NONE || this.reflectionTexture || this.refractionTexture) {
@@ -851,6 +817,9 @@ var BABYLON;
         ], StandardMaterial.prototype, "useGlossinessFromSpecularMapAlpha", void 0);
         __decorate([
             BABYLON.serialize()
+        ], StandardMaterial.prototype, "maxSimultaneousLights", void 0);
+        __decorate([
+            BABYLON.serialize()
         ], StandardMaterial.prototype, "useLogarithmicDepth", null);
         return StandardMaterial;
     })(BABYLON.Material);

+ 21 - 52
src/Materials/babylon.standardMaterial.ts

@@ -16,40 +16,7 @@
         public ALPHAFROMDIFFUSE = false;
         public POINTSIZE = false;
         public FOG = false;
-        public LIGHT0 = false;
-        public LIGHT1 = false;
-        public LIGHT2 = false;
-        public LIGHT3 = false;
-        public SPOTLIGHT0 = false;
-        public SPOTLIGHT1 = false;
-        public SPOTLIGHT2 = false;
-        public SPOTLIGHT3 = false;
-        public HEMILIGHT0 = false;
-        public HEMILIGHT1 = false;
-        public HEMILIGHT2 = false;
-        public HEMILIGHT3 = false;
-        public POINTLIGHT0 = false;
-        public POINTLIGHT1 = false;
-        public POINTLIGHT2 = false;
-        public POINTLIGHT3 = false;
-        public DIRLIGHT0 = false;
-        public DIRLIGHT1 = false;
-        public DIRLIGHT2 = false;
-        public DIRLIGHT3 = false;
         public SPECULARTERM = false;
-        public SHADOW0 = false;
-        public SHADOW1 = false;
-        public SHADOW2 = false;
-        public SHADOW3 = false;
-        public SHADOWS = false;
-        public SHADOWVSM0 = false;
-        public SHADOWVSM1 = false;
-        public SHADOWVSM2 = false;
-        public SHADOWVSM3 = false;
-        public SHADOWPCF0 = false;
-        public SHADOWPCF1 = false;
-        public SHADOWPCF2 = false;
-        public SHADOWPCF3 = false;
         public DIFFUSEFRESNEL = false;
         public OPACITYFRESNEL = false;
         public REFLECTIONFRESNEL = false;
@@ -88,7 +55,7 @@
 
         constructor() {
             super();
-            this._keys = Object.keys(this);
+            this.rebuild();
         }
     }
 
@@ -195,6 +162,9 @@
         @serialize()
         public useGlossinessFromSpecularMapAlpha = false;
 
+        @serialize()
+        public maxSimultaneousLights = 4;
+
         private _renderTargets = new SmartArray<RenderTargetTexture>(16);
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _globalAmbientColor = new Color3(0, 0, 0);
@@ -467,7 +437,7 @@
             }
 
             if (scene.lightsEnabled && !this.disableLighting) {
-                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines);
+                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, this.maxSimultaneousLights);
             }
 
             if (StandardMaterial.FresnelEnabled) {
@@ -586,7 +556,7 @@
                     fallbacks.addFallback(0, "LOGARITHMICDEPTH");
                 }
 
-                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks);
+                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, this.maxSimultaneousLights);
 
                 if (this._defines.SPECULARTERM) {
                     fallbacks.addFallback(0, "SPECULARTERM");
@@ -640,25 +610,24 @@
                     shaderName = "legacydefault";
                 }
                 var join = this._defines.toString();
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
+                    "vFogInfos", "vFogColor", "pointSize",
+                    "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
+                    "mBones",
+                    "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
+                    "depthValues",
+                    "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
+                    "logarithmicDepthConstant"
+                ];
+
+                MaterialHelper.PrepareUniformsListForList(uniforms, this._defines, this.maxSimultaneousLights);
+
                 this._effect = scene.getEngine().createEffect(shaderName,
-                    attribs,
-                    ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
-                        "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
-                        "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
-                        "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
-                        "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
-                        "vFogInfos", "vFogColor", "pointSize",
-                        "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
-                        "mBones",
-                        "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                        "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues",
-                        "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
-                        "logarithmicDepthConstant"
-                    ],
+                    attribs, uniforms,
                     ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
                     ],
-                    join, fallbacks, this.onCompiled, this.onError);
+                    join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights - 1 });
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -838,7 +807,7 @@
 
                 // Lights
                 if (scene.lightsEnabled && !this.disableLighting) {
-                    MaterialHelper.BindLights(scene, mesh, this._effect, this._defines);
+                    MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, this.maxSimultaneousLights);
                 }
 
                 // View

+ 2 - 2
src/Math/babylon.math.js

@@ -483,8 +483,8 @@ var BABYLON;
             return new Vector2(x, y);
         };
         Vector2.Transform = function (vector, transformation) {
-            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]);
-            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]);
+            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + transformation.m[12];
+            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + transformation.m[13];
             return new Vector2(x, y);
         };
         Vector2.Distance = function (value1, value2) {

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

@@ -605,8 +605,8 @@
         }
 
         public static Transform(vector: Vector2, transformation: Matrix): Vector2 {
-            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]);
-            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]);
+            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + transformation.m[12];
+            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + transformation.m[13];
 
             return new Vector2(x, y);
         }

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

@@ -124,7 +124,7 @@ var BABYLON;
         InstancedMesh.prototype.clone = function (name, newParent, doNotCloneChildren) {
             var result = this._sourceMesh.createInstance(name);
             // Deep copy
-            BABYLON.Tools.DeepCopy(this, result, ["name"], []);
+            BABYLON.Tools.DeepCopy(this, result, ["name", "subMeshes"], []);
             // Bounding info
             this.refreshBoundingInfo();
             // Parent

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

@@ -123,7 +123,7 @@
             var result = this._sourceMesh.createInstance(name);
 
             // Deep copy
-            Tools.DeepCopy(this, result, ["name"], []);
+            Tools.DeepCopy(this, result, ["name", "subMeshes"], []);
 
             // Bounding info
             this.refreshBoundingInfo();

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

@@ -1,7 +1,7 @@
 var BABYLON;
 (function (BABYLON) {
     /**
-    * Full documentation here : http://doc.babylonjs.com/tutorials/Solid_Particle_System
+    * Full documentation here : http://doc.babylonjs.com/overviews/Solid_Particle_System
     */
     var SolidParticleSystem = (function () {
         /**
@@ -32,7 +32,7 @@ var BABYLON;
             this.counter = 0;
             /**
             * This empty object is intended to store some SPS specific or temporary values in order to lower the Garbage Collector activity.
-            * Please read : http://doc.babylonjs.com/tutorials/Solid_Particle_System#garbage-collector-concerns
+            * Please read : http://doc.babylonjs.com/overviews/Solid_Particle_System#garbage-collector-concerns
             */
             this.vars = {};
             this._positions = new Array();
@@ -327,7 +327,7 @@ var BABYLON;
         };
         /**
         * Adds some particles to the SPS from the model shape. Returns the shape id.
-        * Please read the doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#create-an-immutable-sps
+        * Please read the doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#create-an-immutable-sps
         * `mesh` is any `Mesh` object that will be used as a model for the solid particles.
         * `nb` (positive integer) the number of particles to be created from this model
         * `positionFunction` is an optional javascript function to called for each particle on SPS creation.
@@ -637,7 +637,7 @@ var BABYLON;
         };
         /**
         * Visibilty helper : Recomputes the visible size according to the mesh bounding box
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
         */
         SolidParticleSystem.prototype.refreshVisibleSize = function () {
             if (!this._isVisibilityBoxLocked) {
@@ -648,7 +648,7 @@ var BABYLON;
         * Visibility helper : Sets the size of a visibility box, this sets the underlying mesh bounding box.
         * @param size the size (float) of the visibility box
         * note : this doesn't lock the SPS mesh bounding box.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
         */
         SolidParticleSystem.prototype.setVisibilityBox = function (size) {
             var vis = size / 2;
@@ -661,7 +661,7 @@ var BABYLON;
             },
             /**
             * Sets the SPS as always visible or not
-            * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+            * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
             */
             set: function (val) {
                 this._alwaysVisible = val;
@@ -676,7 +676,7 @@ var BABYLON;
             },
             /**
             * Sets the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
-            * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+            * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
             */
             set: function (val) {
                 this._isVisibilityBoxLocked = val;
@@ -766,14 +766,14 @@ var BABYLON;
         /**
         * This function does nothing. It may be overwritten to set all the particle first values.
         * The SPS doesn't call this function, you may have to call it by your own.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#particle-management
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
         */
         SolidParticleSystem.prototype.initParticles = function () {
         };
         /**
         * This function does nothing. It may be overwritten to recycle a particle.
         * The SPS doesn't call this function, you may have to call it by your own.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#particle-management
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
         */
         SolidParticleSystem.prototype.recycleParticle = function (particle) {
             return particle;
@@ -781,7 +781,7 @@ var BABYLON;
         /**
         * Updates a particle : this function should  be overwritten by the user.
         * It is called on each particle by `setParticles()`. This is the place to code each particle behavior.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#particle-management
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
         * ex : just set a particle position or velocity and recycle conditions
         */
         SolidParticleSystem.prototype.updateParticle = function (particle) {
@@ -793,7 +793,7 @@ var BABYLON;
         * @param particle the current particle
         * @param vertex the current index of the current particle
         * @param pt the index of the current vertex in the particle shape
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#update-each-particle-shape
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#update-each-particle-shape
         * ex : just set a vertex particle position
         */
         SolidParticleSystem.prototype.updateParticleVertex = function (particle, vertex, pt) {

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

@@ -1,7 +1,7 @@
 module BABYLON {
 
     /**
-    * Full documentation here : http://doc.babylonjs.com/tutorials/Solid_Particle_System
+    * Full documentation here : http://doc.babylonjs.com/overviews/Solid_Particle_System
     */
     export class SolidParticleSystem implements IDisposable {
         // public members
@@ -32,7 +32,7 @@
         public mesh: Mesh;
         /**
         * This empty object is intended to store some SPS specific or temporary values in order to lower the Garbage Collector activity.
-        * Please read : http://doc.babylonjs.com/tutorials/Solid_Particle_System#garbage-collector-concerns
+        * Please read : http://doc.babylonjs.com/overviews/Solid_Particle_System#garbage-collector-concerns
         */
         public vars: any = {};
         /**
@@ -41,7 +41,7 @@
         * Each element of this array is an object `{idx: int, faceId: int}`.  
         * `idx` is the picked particle index in the `SPS.particles` array  
         * `faceId` is the picked face index counted within this particle.  
-        * Please read : http://doc.babylonjs.com/tutorials/Solid_Particle_System#pickable-particles  
+        * Please read : http://doc.babylonjs.com/overviews/Solid_Particle_System#pickable-particles  
         */
         public pickedParticles: { idx: number; faceId: number }[];
 
@@ -382,7 +382,7 @@
 
         /**
         * Adds some particles to the SPS from the model shape. Returns the shape id.   
-        * Please read the doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#create-an-immutable-sps
+        * Please read the doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#create-an-immutable-sps
         * `mesh` is any `Mesh` object that will be used as a model for the solid particles.
         * `nb` (positive integer) the number of particles to be created from this model
         * `positionFunction` is an optional javascript function to called for each particle on SPS creation. 
@@ -729,7 +729,7 @@
 
         /**
         * Visibilty helper : Recomputes the visible size according to the mesh bounding box
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
         */
         public refreshVisibleSize(): void {
             if (!this._isVisibilityBoxLocked) {
@@ -741,7 +741,7 @@
         * Visibility helper : Sets the size of a visibility box, this sets the underlying mesh bounding box.
         * @param size the size (float) of the visibility box
         * note : this doesn't lock the SPS mesh bounding box.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
         */
         public setVisibilityBox(size: number): void {
             var vis = size / 2;
@@ -756,7 +756,7 @@
 
         /**
         * Sets the SPS as always visible or not
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
         */
         public set isAlwaysVisible(val: boolean) {
             this._alwaysVisible = val;
@@ -765,7 +765,7 @@
 
         /**
         * Sets the SPS visibility box as locked or not. This enables/disables the underlying mesh bounding box updates.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#sps-visibility
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#sps-visibility
         */
         public set isVisibilityBoxLocked(val: boolean) {
             this._isVisibilityBoxLocked = val;
@@ -845,7 +845,7 @@
         /**
         * This function does nothing. It may be overwritten to set all the particle first values.
         * The SPS doesn't call this function, you may have to call it by your own.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#particle-management
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
         */
         public initParticles(): void {
         }
@@ -853,7 +853,7 @@
         /**
         * This function does nothing. It may be overwritten to recycle a particle.
         * The SPS doesn't call this function, you may have to call it by your own.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#particle-management
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
         */
         public recycleParticle(particle: SolidParticle): SolidParticle {
             return particle;
@@ -862,7 +862,7 @@
         /**
         * Updates a particle : this function should  be overwritten by the user.
         * It is called on each particle by `setParticles()`. This is the place to code each particle behavior.
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#particle-management
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#particle-management
         * ex : just set a particle position or velocity and recycle conditions
         */
         public updateParticle(particle: SolidParticle): SolidParticle {
@@ -875,7 +875,7 @@
         * @param particle the current particle
         * @param vertex the current index of the current particle
         * @param pt the index of the current vertex in the particle shape
-        * doc : http://doc.babylonjs.com/tutorials/Solid_Particle_System#update-each-particle-shape
+        * doc : http://doc.babylonjs.com/overviews/Solid_Particle_System#update-each-particle-shape
         * ex : just set a vertex particle position
         */
         public updateParticleVertex(particle: SolidParticle, vertex: Vector3, pt: number): Vector3 {

+ 4 - 0
src/Physics/babylon.physicsImpostor.js

@@ -324,6 +324,10 @@ var BABYLON;
         };
         PhysicsImpostor.prototype.dispose = function () {
             var _this = this;
+            //no dispose if no physics engine is available.
+            if (!this._physicsEngine) {
+                return;
+            }
             this._joints.forEach(function (j) {
                 _this._physicsEngine.removeJoint(_this, j.otherImpostor, j.joint);
             });

+ 7 - 3
src/Physics/babylon.physicsImpostor.ts

@@ -328,7 +328,7 @@ module BABYLON {
 
         //event and body object due to cannon's event-based architecture.
         public onCollide = (e: { body: any }) => {
-            if(!this._onPhysicsCollideCallbacks.length) return;
+            if (!this._onPhysicsCollideCallbacks.length) return;
             var otherImpostor = this._physicsEngine.getImpostorWithPhysicsBody(e.body);
             if (otherImpostor) {
                 this._onPhysicsCollideCallbacks.filter((obj) => {
@@ -392,11 +392,16 @@ module BABYLON {
         }
 
         public dispose(/*disposeChildren: boolean = true*/) {
+            //no dispose if no physics engine is available.
+            if (!this._physicsEngine) {
+                return;
+            }
+
             this._joints.forEach((j) => {
                 this._physicsEngine.removeJoint(this, j.otherImpostor, j.joint);
             })
             //dispose the physics body
-            this._physicsEngine.removeImpostor(this); 
+            this._physicsEngine.removeImpostor(this);
             if (this.parent) {
                 this.parent.forceUpdate();
             } else {
@@ -434,4 +439,3 @@ module BABYLON {
         public static HeightmapImpostor = 9;
     }
 }
-

+ 2 - 8
src/Shaders/default.fragment.fx

@@ -32,10 +32,7 @@ varying vec4 vColor;
 #include<helperFunctions>
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..maxSimultaneousLights]
 
 #include<lightsFragmentFunctions>
 #include<shadowsFragmentFunctions>
@@ -225,10 +222,7 @@ void main(void) {
 #endif
 	float shadow = 1.;
 
-#include<lightFragment>[0]
-#include<lightFragment>[1]
-#include<lightFragment>[2]
-#include<lightFragment>[3]
+#include<lightFragment>[0..maxSimultaneousLights]
 
 	// Refraction
 	vec3 refractionColor = vec3(0., 0., 0.);

+ 2 - 8
src/Shaders/legacydefault.fragment.fx

@@ -18,10 +18,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..3]
 
 #include<lightsFragmentFunctions>
 #include<shadowsFragmentFunctions>
@@ -136,10 +133,7 @@ void main(void) {
 #endif
 	float shadow = 1.;
 
-#include<lightFragment>[0]
-#include<lightFragment>[1]
-#include<lightFragment>[2]
-#include<lightFragment>[3]
+#include<lightFragment>[0..3]
 
 	// Reflection
 	vec3 reflectionColor = vec3(0., 0., 0.);

+ 2 - 8
src/Shaders/legacypbr.fragment.fx

@@ -43,10 +43,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..3]
 
 // Samplers
 #ifdef ALBEDO
@@ -203,10 +200,7 @@ void main(void) {
     float NdotL = -1.;
     lightingInfo info;
 
-#include<pbrLightFunctionsCall>[0]
-#include<pbrLightFunctionsCall>[1]
-#include<pbrLightFunctionsCall>[2]
-#include<pbrLightFunctionsCall>[3]
+#include<pbrLightFunctionsCall>[0..3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 2 - 8
src/Shaders/pbr.fragment.fx

@@ -55,10 +55,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..3]
 
 // Samplers
 #ifdef ALBEDO
@@ -284,10 +281,7 @@ void main(void) {
     float NdotL = -1.;
     lightingInfo info;
 
-#include<pbrLightFunctionsCall>[0]
-#include<pbrLightFunctionsCall>[1]
-#include<pbrLightFunctionsCall>[2]
-#include<pbrLightFunctionsCall>[3]
+#include<pbrLightFunctionsCall>[0..3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 139 - 68
src/Tools/babylon.dynamicFloatArray.js

@@ -1,60 +1,93 @@
 var BABYLON;
 (function (BABYLON) {
+    var DynamicFloatArrayElementInfo = (function () {
+        function DynamicFloatArrayElementInfo() {
+        }
+        return DynamicFloatArrayElementInfo;
+    })();
+    BABYLON.DynamicFloatArrayElementInfo = DynamicFloatArrayElementInfo;
     /**
     * The purpose of this class is to store float32 based elements of a given size (defined by the stride argument) in a dynamic fashion, that is, you can add/free elements. You can then access to a defragmented/packed version of the underlying Float32Array by calling the pack() method.
     * The intent is to maintain through time data that will be bound to a WebGlBuffer with the ability to change add/remove elements.
     * It was first built to effiently maintain the WebGlBuffer that contain instancing based data.
-    * Allocating an Element will return a instance of DynamicFloatArrayEntry which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
+    * Allocating an Element will return a instance of DynamicFloatArrayElement which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
     * Beware, calling pack() may change the offset of some Entries because this method will defrag the Float32Array to replace empty elements by moving allocated ones at their location.
-     * This method will retrun an ArrayBufferView on the existing Float32Array that describes the occupied elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes".
+     * This method will return an ArrayBufferView on the existing Float32Array that describes the used elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes".
     */
     var DynamicFloatArray = (function () {
         /**
          * Construct an instance of the dynamic float array
-         * @param stride size of one entry in float (i.e. not bytes!)
-         * @param initialEntryCount the number of available entries at construction
+         * @param stride size of one element in float (i.e. not bytes!)
+         * @param initialElementCount the number of available entries at construction
          */
-        function DynamicFloatArray(stride, initialEntryCount) {
-            this.stride = stride;
-            this.entryCount = initialEntryCount;
-            this.buffer = new Float32Array(stride * initialEntryCount);
-            this.allEntries = new Array(initialEntryCount);
-            this.freeEntries = new Array(initialEntryCount);
-            for (var i = 0; i < initialEntryCount; i++) {
-                var entry = new DynamicFloatArrayEntry();
-                entry.offset = i * stride;
-                this.allEntries[i] = entry;
-                this.freeEntries[initialEntryCount - i - 1] = entry;
+        function DynamicFloatArray(stride, initialElementCount) {
+            this._stride = stride;
+            this.buffer = new Float32Array(stride * initialElementCount);
+            this._lastUsed = 0;
+            this._firstFree = 0;
+            this._allEntries = new Array(initialElementCount);
+            this._freeEntries = new Array(initialElementCount);
+            for (var i = 0; i < initialElementCount; i++) {
+                var element = new DynamicFloatArrayElementInfo();
+                element.offset = i * stride;
+                this._allEntries[i] = element;
+                this._freeEntries[initialElementCount - i - 1] = element;
             }
         }
+        /**
+         * Allocate an element in the array.
+         * @return the element info instance that contains the offset into the main buffer of the element's location.
+         * Beware, this offset may change when you call pack()
+         */
         DynamicFloatArray.prototype.allocElement = function () {
-            if (this.freeEntries.length === 0) {
+            if (this._freeEntries.length === 0) {
                 this._growBuffer();
             }
-            var entry = this.freeEntries.pop();
-            return entry;
+            var el = this._freeEntries.pop();
+            this._lastUsed = Math.max(el.offset, this._lastUsed);
+            if (el.offset === this._firstFree) {
+                if (this._freeEntries.length > 0) {
+                    this._firstFree = this._freeEntries[this._freeEntries.length - 1].offset;
+                }
+                else {
+                    this._firstFree += this._stride;
+                }
+            }
+            return el;
         };
-        DynamicFloatArray.prototype.freeElement = function (entry) {
-            this.freeEntries.push(entry);
+        /**
+         * Free the element corresponding to the given element info
+         * @param elInfo the element that describe the allocated element
+         */
+        DynamicFloatArray.prototype.freeElement = function (elInfo) {
+            this._firstFree = Math.min(elInfo.offset, this._firstFree);
+            this._freeEntries.push(elInfo);
         };
         /**
-         * This method will pack all the occupied elements into a linear sequence and free the rest.
-         * Instances of DynamicFloatArrayEntry may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * This method will pack all the used elements into a linear sequence and put all the free space at the end.
+         * Instances of DynamicFloatArrayElement may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * @return the subarray that is the view of the used elements area, you can use it as a source to update a WebGLBuffer
          */
         DynamicFloatArray.prototype.pack = function () {
             // no free slot? no need to pack
-            if (this.freeEntries.length === 0) {
+            if (this._freeEntries.length === 0) {
                 return this.buffer;
             }
-            var s = this.stride;
-            var sortedFree = this.freeEntries.sort(function (a, b) { return a.offset - b.offset; });
-            var sortedAll = this.allEntries.sort(function (a, b) { return a.offset - b.offset; });
-            // Make sure there's a free element at the very end, we need it to create a range where we'll move the occupied elements that may appear before
-            var lastFree = new DynamicFloatArrayEntry();
-            lastFree.offset = this.entryCount * s;
-            sortedFree.push(lastFree);
+            // If the buffer is already packed the last used will always be lower than the first free
+            if (this._lastUsed < this._firstFree) {
+                var elementsBuffer_1 = this.buffer.subarray(0, this._lastUsed + this._stride);
+                return elementsBuffer_1;
+            }
+            var s = this._stride;
+            // Make sure there's a free element at the very end, we need it to create a range where we'll move the used elements that may appear before
+            var lastFree = new DynamicFloatArrayElementInfo();
+            lastFree.offset = this.totalElementCount * s;
+            this._freeEntries.push(lastFree);
+            var sortedFree = this._freeEntries.sort(function (a, b) { return a.offset - b.offset; });
+            var sortedAll = this._allEntries.sort(function (a, b) { return a.offset - b.offset; });
             var firstFreeSlotOffset = sortedFree[0].offset;
             var freeZoneSize = 1;
+            // The sortedFree array is sorted in reverse, first free at the end, last free at the beginning, so we loop from the end to beginning
             var prevOffset = sortedFree[0].offset;
             for (var i = 1; i < sortedFree.length; i++) {
                 var curFree = sortedFree[i];
@@ -70,27 +103,27 @@ var BABYLON;
                     continue;
                 }
                 // Distance is bigger, which means there's x element between the previous free and this one
-                var occupiedRange = (distance / s) - 1;
+                var usedRange = (distance / s) - 1;
                 // Two cases the free zone is smaller than the data to move or bigger
                 // Copy what can fit in the free zone
                 var curMoveOffset = curOffset - s;
-                var copyCount = Math.min(freeZoneSize, occupiedRange);
+                var copyCount = Math.min(freeZoneSize, usedRange);
                 for (var j = 0; j < copyCount; j++) {
                     var freeI = firstFreeSlotOffset / s;
                     var curI = curMoveOffset / s;
-                    var moveEntry = sortedAll[curI];
-                    this._moveEntry(moveEntry, firstFreeSlotOffset);
-                    var replacedEntry = sortedAll[freeI];
-                    // set the offset of the element entry we replace with a value that will make it discard at the end of the method
-                    replacedEntry.offset = curMoveOffset;
-                    // Swap the entry we moved and the one it replaced in the sorted array to reflect the action we've made
-                    sortedAll[freeI] = moveEntry;
-                    sortedAll[curI] = replacedEntry;
+                    var moveEl = sortedAll[curI];
+                    this._moveElement(moveEl, firstFreeSlotOffset);
+                    var replacedEl = sortedAll[freeI];
+                    // set the offset of the element element we replace with a value that will make it discard at the end of the method
+                    replacedEl.offset = curMoveOffset;
+                    // Swap the element we moved and the one it replaced in the sorted array to reflect the action we've made
+                    sortedAll[freeI] = moveEl;
+                    sortedAll[curI] = replacedEl;
                     curMoveOffset -= s;
                     firstFreeSlotOffset += s;
                 }
                 // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
-                if (freeZoneSize <= occupiedRange) {
+                if (freeZoneSize <= usedRange) {
                     firstFreeSlotOffset = curOffset;
                     freeZoneSize = 1;
                 }
@@ -100,43 +133,81 @@ var BABYLON;
                 // as we're about to iterate to the next, the cur becomes the prev...
                 prevOffset = curOffset;
             }
-            // Allocate a new buffer with the perfect size, copy the content, update free data
-            this.entryCount = firstFreeSlotOffset / s;
-            var optimizedBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
-            this.freeEntries.splice(0);
-            this.allEntries = sortedAll.slice(0, this.entryCount);
-            return optimizedBuffer;
+            var elementsBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
+            this._lastUsed = firstFreeSlotOffset - s;
+            this._firstFree = firstFreeSlotOffset;
+            sortedFree.pop(); // Remove the last free because that's the one we added at the start of the method
+            this._freeEntries = sortedFree.sort(function (a, b) { return b.offset - a.offset; });
+            this._allEntries = sortedAll;
+            return elementsBuffer;
         };
-        DynamicFloatArray.prototype._moveEntry = function (entry, destOffset) {
-            for (var i = 0; i < this.stride; i++) {
-                this.buffer[destOffset + i] = this.buffer[entry.offset + i];
+        DynamicFloatArray.prototype._moveElement = function (element, destOffset) {
+            for (var i = 0; i < this._stride; i++) {
+                this.buffer[destOffset + i] = this.buffer[element.offset + i];
             }
-            entry.offset = destOffset;
+            element.offset = destOffset;
         };
         DynamicFloatArray.prototype._growBuffer = function () {
             // Allocate the new buffer with 50% more entries, copy the content of the current one
-            var newEntryCount = this.entryCount * 1.5;
-            var newBuffer = new Float32Array(newEntryCount * this.stride);
+            var newElCount = this.totalElementCount * 1.5;
+            var newBuffer = new Float32Array(newElCount * this._stride);
             newBuffer.set(this.buffer);
-            var addedCount = newEntryCount - this.entryCount;
-            this.allEntries.length += addedCount;
-            this.freeEntries.length += addedCount;
-            for (var i = this.entryCount; i < newEntryCount; i++) {
-                var entry = new DynamicFloatArrayEntry();
-                entry.offset = i * this.stride;
-                this.allEntries[i] = entry;
-                this.freeEntries[i] = entry;
+            var addedCount = newElCount - this.totalElementCount;
+            this._allEntries.length += addedCount;
+            this._freeEntries.length += addedCount;
+            for (var i = this.totalElementCount; i < newElCount; i++) {
+                var el = new DynamicFloatArrayElementInfo();
+                el.offset = i * this._stride;
+                this._allEntries[i] = el;
+                this._freeEntries[i] = el;
             }
             this.buffer = newBuffer;
-            this.entryCount = newEntryCount;
         };
+        Object.defineProperty(DynamicFloatArray.prototype, "totalElementCount", {
+            /**
+             * Get the total count of entries that can fit in the current buffer
+             * @returns the elements count
+             */
+            get: function () {
+                return this._allEntries.length;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(DynamicFloatArray.prototype, "freeElementCount", {
+            /**
+             * Get the count of free entries that can still be allocated without resizing the buffer
+             * @returns the free elements count
+             */
+            get: function () {
+                return this._freeEntries.length;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(DynamicFloatArray.prototype, "usedElementCount", {
+            /**
+             * Get the count of allocated elements
+             * @returns the allocated elements count
+             */
+            get: function () {
+                return this._allEntries.length - this._freeEntries.length;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(DynamicFloatArray.prototype, "stride", {
+            /**
+             * Return the size of one element in float
+             * @returns the size in float
+             */
+            get: function () {
+                return this._stride;
+            },
+            enumerable: true,
+            configurable: true
+        });
         return DynamicFloatArray;
     })();
     BABYLON.DynamicFloatArray = DynamicFloatArray;
-    var DynamicFloatArrayEntry = (function () {
-        function DynamicFloatArrayEntry() {
-        }
-        return DynamicFloatArrayEntry;
-    })();
-    BABYLON.DynamicFloatArrayEntry = DynamicFloatArrayEntry;
 })(BABYLON || (BABYLON = {}));

+ 142 - 73
src/Tools/babylon.dynamicFloatArray.ts

@@ -1,70 +1,104 @@
 module BABYLON {
+
+    export class DynamicFloatArrayElementInfo {
+        offset: number;
+    }
+
     /**
     * The purpose of this class is to store float32 based elements of a given size (defined by the stride argument) in a dynamic fashion, that is, you can add/free elements. You can then access to a defragmented/packed version of the underlying Float32Array by calling the pack() method.
     * The intent is to maintain through time data that will be bound to a WebGlBuffer with the ability to change add/remove elements.
     * It was first built to effiently maintain the WebGlBuffer that contain instancing based data.
-    * Allocating an Element will return a instance of DynamicFloatArrayEntry which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
+    * Allocating an Element will return a instance of DynamicFloatArrayElement which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
     * Beware, calling pack() may change the offset of some Entries because this method will defrag the Float32Array to replace empty elements by moving allocated ones at their location.
-     * This method will retrun an ArrayBufferView on the existing Float32Array that describes the occupied elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes". 
+     * This method will return an ArrayBufferView on the existing Float32Array that describes the used elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes". 
     */
     export class DynamicFloatArray {
         /**
          * Construct an instance of the dynamic float array
-         * @param stride size of one entry in float (i.e. not bytes!)
-         * @param initialEntryCount the number of available entries at construction
+         * @param stride size of one element in float (i.e. not bytes!)
+         * @param initialElementCount the number of available entries at construction
          */
-        constructor(stride: number, initialEntryCount: number) {
-            this.stride = stride;
-            this.entryCount = initialEntryCount;
-            this.buffer = new Float32Array(stride * initialEntryCount);
-
-            this.allEntries = new Array<DynamicFloatArrayEntry>(initialEntryCount);
-            this.freeEntries = new Array<DynamicFloatArrayEntry>(initialEntryCount);
-
-            for (let i = 0; i < initialEntryCount; i++) {
-                let entry = new DynamicFloatArrayEntry();
-                entry.offset = i * stride;
-
-                this.allEntries[i] = entry;
-                this.freeEntries[initialEntryCount - i - 1] = entry;
+        constructor(stride: number, initialElementCount: number) {
+            this._stride = stride;
+            this.buffer = new Float32Array(stride * initialElementCount);
+            this._lastUsed = 0;
+            this._firstFree = 0;
+            this._allEntries = new Array<DynamicFloatArrayElementInfo>(initialElementCount);
+            this._freeEntries = new Array<DynamicFloatArrayElementInfo>(initialElementCount);
+
+            for (let i = 0; i < initialElementCount; i++) {
+                let element = new DynamicFloatArrayElementInfo();
+                element.offset = i * stride;
+
+                this._allEntries[i] = element;
+                this._freeEntries[initialElementCount - i - 1] = element;
             }
         }
 
-        allocElement(): DynamicFloatArrayEntry {
-            if (this.freeEntries.length === 0) {
+        /**
+         * Allocate an element in the array.
+         * @return the element info instance that contains the offset into the main buffer of the element's location.
+         * Beware, this offset may change when you call pack()
+         */
+        allocElement(): DynamicFloatArrayElementInfo {
+            if (this._freeEntries.length === 0) {
                 this._growBuffer();
             }
 
-            var entry = this.freeEntries.pop();
-            return entry;
+            let el = this._freeEntries.pop();
+            this._lastUsed = Math.max(el.offset, this._lastUsed);
+
+            if (el.offset === this._firstFree) {
+                if (this._freeEntries.length > 0) {
+                    this._firstFree = this._freeEntries[this._freeEntries.length - 1].offset;
+                } else {
+                    this._firstFree += this._stride;
+                }
+            }
+            return el;
         }
 
-        freeElement(entry: DynamicFloatArrayEntry) {
-            this.freeEntries.push(entry);
+        /**
+         * Free the element corresponding to the given element info
+         * @param elInfo the element that describe the allocated element
+         */
+        freeElement(elInfo: DynamicFloatArrayElementInfo) {
+            this._firstFree = Math.min(elInfo.offset, this._firstFree);
+            this._freeEntries.push(elInfo);
         }
 
         /**
-         * This method will pack all the occupied elements into a linear sequence and free the rest.
-         * Instances of DynamicFloatArrayEntry may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * This method will pack all the used elements into a linear sequence and put all the free space at the end.
+         * Instances of DynamicFloatArrayElement may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * @return the subarray that is the view of the used elements area, you can use it as a source to update a WebGLBuffer
          */
         pack(): Float32Array {
+
             // no free slot? no need to pack
-            if (this.freeEntries.length === 0) {
+            if (this._freeEntries.length === 0) {
                 return this.buffer;
             }
 
-            let s = this.stride;
-            let sortedFree = this.freeEntries.sort((a, b) => a.offset - b.offset);
-            let sortedAll = this.allEntries.sort((a, b) => a.offset - b.offset);
+            // If the buffer is already packed the last used will always be lower than the first free
+            if (this._lastUsed < this._firstFree) {
+                let elementsBuffer = this.buffer.subarray(0, this._lastUsed + this._stride);
+                return elementsBuffer;
+            }
+
+            let s = this._stride;
+
+            // Make sure there's a free element at the very end, we need it to create a range where we'll move the used elements that may appear before
+            let lastFree = new DynamicFloatArrayElementInfo();
+            lastFree.offset = this.totalElementCount * s;
+            this._freeEntries.push(lastFree);
 
-            // Make sure there's a free element at the very end, we need it to create a range where we'll move the occupied elements that may appear before
-            let lastFree = new DynamicFloatArrayEntry();
-            lastFree.offset = this.entryCount * s;
-            sortedFree.push(lastFree);
+            let sortedFree = this._freeEntries.sort((a, b) => a.offset - b.offset);
+            let sortedAll = this._allEntries.sort((a, b) => a.offset - b.offset);
 
             let firstFreeSlotOffset = sortedFree[0].offset;
             let freeZoneSize = 1;
 
+            // The sortedFree array is sorted in reverse, first free at the end, last free at the beginning, so we loop from the end to beginning
             let prevOffset = sortedFree[0].offset;
             for (let i = 1; i < sortedFree.length; i++) {
                 let curFree = sortedFree[i];
@@ -85,34 +119,34 @@
                 }
 
                 // Distance is bigger, which means there's x element between the previous free and this one
-                let occupiedRange = (distance / s) - 1;
+                let usedRange = (distance / s) - 1;
 
                 // Two cases the free zone is smaller than the data to move or bigger
 
                 // Copy what can fit in the free zone
                 let curMoveOffset = curOffset - s;
-                let copyCount = Math.min(freeZoneSize, occupiedRange);
+                let copyCount = Math.min(freeZoneSize, usedRange);
                 for (let j = 0; j < copyCount; j++) {
                     let freeI = firstFreeSlotOffset / s;
                     let curI = curMoveOffset / s;
 
-                    let moveEntry = sortedAll[curI];
-                    this._moveEntry(moveEntry, firstFreeSlotOffset);
-                    let replacedEntry = sortedAll[freeI];
+                    let moveEl = sortedAll[curI];
+                    this._moveElement(moveEl, firstFreeSlotOffset);
+                    let replacedEl = sortedAll[freeI];
 
-                    // set the offset of the element entry we replace with a value that will make it discard at the end of the method
-                    replacedEntry.offset = curMoveOffset;
+                    // set the offset of the element element we replace with a value that will make it discard at the end of the method
+                    replacedEl.offset = curMoveOffset;
 
-                    // Swap the entry we moved and the one it replaced in the sorted array to reflect the action we've made
-                    sortedAll[freeI] = moveEntry;
-                    sortedAll[curI] = replacedEntry;
+                    // Swap the element we moved and the one it replaced in the sorted array to reflect the action we've made
+                    sortedAll[freeI] = moveEl;
+                    sortedAll[curI] = replacedEl;
 
                     curMoveOffset -= s;
                     firstFreeSlotOffset += s;
                 }
 
                 // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
-                if (freeZoneSize <= occupiedRange) {
+                if (freeZoneSize <= usedRange) {
                     firstFreeSlotOffset = curOffset;
                     freeZoneSize = 1;
                 }
@@ -126,52 +160,87 @@
                 prevOffset = curOffset;
             }
 
-            // Allocate a new buffer with the perfect size, copy the content, update free data
-            this.entryCount = firstFreeSlotOffset / s;
-            let optimizedBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
-            this.freeEntries.splice(0);
-            this.allEntries = sortedAll.slice(0, this.entryCount);
-            return optimizedBuffer;
+            let elementsBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
+            this._lastUsed = firstFreeSlotOffset - s;
+            this._firstFree = firstFreeSlotOffset;
+            sortedFree.pop();             // Remove the last free because that's the one we added at the start of the method
+            this._freeEntries = sortedFree.sort((a, b) => b.offset - a.offset);
+            this._allEntries = sortedAll;
+
+            return elementsBuffer;
         }
 
-        private _moveEntry(entry: DynamicFloatArrayEntry, destOffset: number) {
-            for (let i = 0; i < this.stride; i++) {
-                this.buffer[destOffset + i] = this.buffer[entry.offset + i];
+        private _moveElement(element: DynamicFloatArrayElementInfo, destOffset: number) {
+            for (let i = 0; i < this._stride; i++) {
+                this.buffer[destOffset + i] = this.buffer[element.offset + i];
             }
 
-            entry.offset = destOffset;
+            element.offset = destOffset;
         }
 
         private _growBuffer() {
             // Allocate the new buffer with 50% more entries, copy the content of the current one
-            let newEntryCount = this.entryCount * 1.5;
-            let newBuffer = new Float32Array(newEntryCount * this.stride);
+            let newElCount = this.totalElementCount * 1.5;
+            let newBuffer = new Float32Array(newElCount * this._stride);
             newBuffer.set(this.buffer);
 
-            let addedCount = newEntryCount - this.entryCount;
-            this.allEntries.length += addedCount;
-            this.freeEntries.length += addedCount;
+            let addedCount = newElCount - this.totalElementCount;
+            this._allEntries.length += addedCount;
+            this._freeEntries.length += addedCount;
 
-            for (let i = this.entryCount; i < newEntryCount; i++) {
-                let entry = new DynamicFloatArrayEntry();
-                entry.offset = i * this.stride;
+            for (let i = this.totalElementCount; i < newElCount; i++) {
+                let el = new DynamicFloatArrayElementInfo();
+                el.offset = i * this._stride;
 
-                this.allEntries[i] = entry;
-                this.freeEntries[i] = entry;
+                this._allEntries[i] = el;
+                this._freeEntries[i] = el;
             }
 
             this.buffer = newBuffer;
-            this.entryCount = newEntryCount;
         }
 
+        /**
+         * This is the main buffer, all elements are stored inside, you use the DynamicFloatArrayElement instance of a given element to know its location into this buffer, then you have the responsability to perform write operations in this buffer at the right location!
+         * Don't use this buffer for a WebGL bufferSubData() operation, but use the one returned by the pack() method.
+         */
         buffer: Float32Array;
-        entryCount: number;
-        stride: number;
-        allEntries: Array<DynamicFloatArrayEntry>;
-        freeEntries: Array<DynamicFloatArrayEntry>;
-    }
 
-    export class DynamicFloatArrayEntry {
-        offset: number;
+        /**
+         * Get the total count of entries that can fit in the current buffer
+         * @returns the elements count
+         */
+        get totalElementCount(): number {
+            return this._allEntries.length;
+        }
+
+        /**
+         * Get the count of free entries that can still be allocated without resizing the buffer
+         * @returns the free elements count
+         */
+        get freeElementCount(): number {
+            return this._freeEntries.length;
+        }
+
+        /**
+         * Get the count of allocated elements
+         * @returns the allocated elements count
+         */
+        get usedElementCount(): number {
+            return this._allEntries.length - this._freeEntries.length;
+        }
+
+        /**
+         * Return the size of one element in float
+         * @returns the size in float
+         */
+        get stride(): number {
+            return this._stride;
+        }
+
+        private _allEntries: Array<DynamicFloatArrayElementInfo>;
+        private _freeEntries: Array<DynamicFloatArrayElementInfo>;
+        private _stride: number;
+        private _lastUsed: number;
+        private _firstFree: number;
     }
 }

+ 100 - 0
src/Tools/babylon.smartCollection.js

@@ -0,0 +1,100 @@
+var BABYLON;
+(function (BABYLON) {
+    var SmartCollection = (function () {
+        function SmartCollection(capacity) {
+            if (capacity === void 0) { capacity = 10; }
+            this.count = 0;
+            this._initialCapacity = capacity;
+            this.items = {};
+            this._keys = new Array(this._initialCapacity);
+        }
+        SmartCollection.prototype.add = function (key, item) {
+            if (this.items[key] != undefined) {
+                return -1;
+            }
+            this.items[key] = item;
+            //literal keys are always strings, but we keep source type of key in _keys array
+            this._keys[this.count++] = key;
+            if (this.count > this._keys.length) {
+                this._keys.length *= 2;
+            }
+            return this.count;
+        };
+        SmartCollection.prototype.remove = function (key) {
+            if (this.items[key] == undefined) {
+                return -1;
+            }
+            return this.removeItemOfIndex(this.indexOf(key));
+        };
+        SmartCollection.prototype.removeItemOfIndex = function (index) {
+            if (index < this.count && index > -1) {
+                delete this.items[this._keys[index]];
+                //here, shifting by hand is better optimised than .splice
+                while (index < this.count) {
+                    this._keys[index] = this._keys[index + 1];
+                    index++;
+                }
+            }
+            else {
+                return -1;
+            }
+            return --this.count;
+        };
+        SmartCollection.prototype.indexOf = function (key) {
+            for (var i = 0; i !== this.count; i++) {
+                if (this._keys[i] === key) {
+                    return i;
+                }
+            }
+            return -1;
+        };
+        SmartCollection.prototype.item = function (key) {
+            return this.items[key];
+        };
+        SmartCollection.prototype.getAllKeys = function () {
+            if (this.count > 0) {
+                var keys = new Array(this.count);
+                for (var i = 0; i < this.count; i++) {
+                    keys[i] = this._keys[i];
+                }
+                return keys;
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.getKeyByIndex = function (index) {
+            if (index < this.count && index > -1) {
+                return this._keys[index];
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.getItemByIndex = function (index) {
+            if (index < this.count && index > -1) {
+                return this.items[this._keys[index]];
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.empty = function () {
+            if (this.count > 0) {
+                this.count = 0;
+                this.items = {};
+                this._keys = new Array(this._initialCapacity);
+            }
+        };
+        SmartCollection.prototype.forEach = function (block) {
+            var key;
+            for (key in this.items) {
+                if (this.items.hasOwnProperty(key)) {
+                    block(this.items[key]);
+                }
+            }
+        };
+        return SmartCollection;
+    })();
+    BABYLON.SmartCollection = SmartCollection;
+})(BABYLON || (BABYLON = {}));

+ 2 - 2
src/babylon.engine.js

@@ -869,14 +869,14 @@ var BABYLON;
                 }
             }
         };
-        Engine.prototype.createEffect = function (baseName, attributesNames, uniformsNames, samplers, defines, fallbacks, onCompiled, onError) {
+        Engine.prototype.createEffect = function (baseName, attributesNames, uniformsNames, samplers, defines, fallbacks, onCompiled, onError, indexParameters) {
             var vertex = baseName.vertexElement || baseName.vertex || baseName;
             var fragment = baseName.fragmentElement || baseName.fragment || baseName;
             var name = vertex + "+" + fragment + "@" + defines;
             if (this._compiledEffects[name]) {
                 return this._compiledEffects[name];
             }
-            var effect = new BABYLON.Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError);
+            var effect = new BABYLON.Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters);
             effect._key = name;
             this._compiledEffects[name] = effect;
             return effect;

+ 2 - 2
src/babylon.engine.ts

@@ -1040,7 +1040,7 @@
         }
 
         public createEffect(baseName: any, attributesNames: string[], uniformsNames: string[], samplers: string[], defines: string, fallbacks?: EffectFallbacks,
-            onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void): Effect {
+            onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void, indexParameters?: any): Effect {
             var vertex = baseName.vertexElement || baseName.vertex || baseName;
             var fragment = baseName.fragmentElement || baseName.fragment || baseName;
 
@@ -1049,7 +1049,7 @@
                 return this._compiledEffects[name];
             }
 
-            var effect = new Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError);
+            var effect = new Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters);
             effect._key = name;
             this._compiledEffects[name] = effect;