Explorar el Código

First release of Max2Babylon exporter
new property for .babylon files: light.excludedMeshesIds

David Catuhe hace 11 años
padre
commit
2e1237fbea
Se han modificado 65 ficheros con 4734 adiciones y 0 borrados
  1. 1 0
      Babylon/Lights/babylon.light.js
  2. 1 0
      Babylon/Lights/babylon.light.ts
  3. 4 0
      Babylon/Loading/Plugins/babylon.babylonFileLoader.js
  4. 12 0
      Babylon/Materials/babylon.standardMaterial.js
  5. 12 0
      Babylon/Materials/babylon.standardMaterial.ts
  6. 34 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonAnimation.cs
  7. 14 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonAnimationKey.cs
  8. 23 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonBone.cs
  9. 82 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs
  10. 77 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj
  11. 20 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonLensFlare.cs
  12. 17 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonLensFlareSystem.cs
  13. 55 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonLight.cs
  14. 66 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonMaterial.cs
  15. 122 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs
  16. 17 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonMultiMaterial.cs
  17. 86 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonParticleSystem.cs
  18. 163 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs
  19. 21 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonShadowGenerator.cs
  20. 17 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonSkeleton.cs
  21. 24 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonSubMesh.cs
  22. 87 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonTexture.cs
  23. 36 0
      Exporters/3ds Max/BabylonExport.Entities/Properties/AssemblyInfo.cs
  24. BIN
      Exporters/3ds Max/Max2Babylon-0.4.2.zip
  25. 28 0
      Exporters/3ds Max/Max2Babylon.sln
  26. 8 0
      Exporters/3ds Max/Max2Babylon/BabylonActionCallback.cs
  27. 68 0
      Exporters/3ds Max/Max2Babylon/BabylonExportActionItem.cs
  28. 84 0
      Exporters/3ds Max/Max2Babylon/BabylonPropertiesActionItem.cs
  29. 52 0
      Exporters/3ds Max/Max2Babylon/Descriptor.cs
  30. 61 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Camera.cs
  31. 113 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Light.cs
  32. 105 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Material.cs
  33. 266 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs
  34. 44 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.ShadowGenerator.cs
  35. 112 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Texture.cs
  36. 154 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs
  37. 50 0
      Exporters/3ds Max/Max2Babylon/Exporter/GlobalVertex.cs
  38. 228 0
      Exporters/3ds Max/Max2Babylon/Forms/CameraPropertiesForm.Designer.cs
  39. 49 0
      Exporters/3ds Max/Max2Babylon/Forms/CameraPropertiesForm.cs
  40. 120 0
      Exporters/3ds Max/Max2Babylon/Forms/CameraPropertiesForm.resx
  41. 143 0
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.Designer.cs
  42. 117 0
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.cs
  43. 123 0
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx
  44. 143 0
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.Designer.cs
  45. 39 0
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.cs
  46. 120 0
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.resx
  47. 124 0
      Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.Designer.cs
  48. 24 0
      Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.cs
  49. 120 0
      Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.resx
  50. 152 0
      Exporters/3ds Max/Max2Babylon/Forms/Vector3Control.Designer.cs
  51. 30 0
      Exporters/3ds Max/Max2Babylon/Forms/Vector3Control.cs
  52. 120 0
      Exporters/3ds Max/Max2Babylon/Forms/Vector3Control.resx
  53. 117 0
      Exporters/3ds Max/Max2Babylon/GlobalUtility.cs
  54. 29 0
      Exporters/3ds Max/Max2Babylon/Loader.cs
  55. 159 0
      Exporters/3ds Max/Max2Babylon/Max2Babylon.csproj
  56. 36 0
      Exporters/3ds Max/Max2Babylon/Properties/AssemblyInfo.cs
  57. 63 0
      Exporters/3ds Max/Max2Babylon/Properties/Resources.Designer.cs
  58. 120 0
      Exporters/3ds Max/Max2Babylon/Properties/Resources.resx
  59. BIN
      Exporters/3ds Max/Max2Babylon/Refs/Autodesk.Max.dll
  60. BIN
      Exporters/3ds Max/Max2Babylon/Refs/ManagedServices.dll
  61. BIN
      Exporters/3ds Max/Max2Babylon/Refs/MaxCustomControls.dll
  62. BIN
      Exporters/3ds Max/Max2Babylon/Refs/MaxSharp.dll
  63. BIN
      Exporters/3ds Max/Max2Babylon/Refs/SharpDX.dll
  64. 287 0
      Exporters/3ds Max/Max2Babylon/Tools.cs
  65. 135 0
      Exporters/3ds Max/readme.md

+ 1 - 0
Babylon/Lights/babylon.light.js

@@ -15,6 +15,7 @@ var BABYLON;
             this.intensity = 1.0;
             this.range = Number.MAX_VALUE;
             this.excludedMeshes = new Array();
+            this._excludedMeshesIds = new Array();
 
             scene.lights.push(this);
         }

+ 1 - 0
Babylon/Lights/babylon.light.ts

@@ -8,6 +8,7 @@
 
         public _shadowGenerator: ShadowGenerator;
         private _parentedWorldMatrix: Matrix;
+        public _excludedMeshesIds = new Array<string>();
 
         constructor(name: string, scene: Scene) {
             super(name, scene);

+ 4 - 0
Babylon/Loading/Plugins/babylon.babylonFileLoader.js

@@ -299,6 +299,10 @@ var BABYLON = BABYLON || {};
 
         light.diffuse = BABYLON.Color3.FromArray(parsedLight.diffuse);
         light.specular = BABYLON.Color3.FromArray(parsedLight.specular);
+
+        if (parsedLight.excludedMeshesIds) {
+            light._excludedMeshesIds = parsedLight.excludedMeshesIds;
+        }
     };
 
     var parseCamera = function (parsedCamera, scene) {

+ 12 - 0
Babylon/Materials/babylon.standardMaterial.js

@@ -167,6 +167,18 @@ var BABYLON;
                         continue;
                     }
 
+                    if (light._excludedMeshesIds.length > 0) {
+                        for (var excludedIndex = 0; excludedIndex < light._excludedMeshesIds.length; excludedIndex++) {
+                            var excludedMesh = scene.getMeshByID(light._excludedMeshesIds[excludedIndex]);
+
+                            if (excludedMesh) {
+                                light.excludedMeshes.push(excludedMesh);
+                            }
+                        }
+
+                        light._excludedMeshesIds = [];
+                    }
+
                     if (mesh && light.excludedMeshes.indexOf(mesh) !== -1) {
                         continue;
                     }

+ 12 - 0
Babylon/Materials/babylon.standardMaterial.ts

@@ -170,6 +170,18 @@
                         continue;
                     }
 
+                    if (light._excludedMeshesIds.length > 0) {
+                        for (var excludedIndex = 0; excludedIndex < light._excludedMeshesIds.length; excludedIndex++) {
+                            var excludedMesh = scene.getMeshByID(light._excludedMeshesIds[excludedIndex]);
+
+                            if (excludedMesh) {
+                                light.excludedMeshes.push(excludedMesh);
+                            }
+                        }
+
+                        light._excludedMeshesIds = [];
+                    }
+
                     if (mesh && light.excludedMeshes.indexOf(mesh) !== -1) {
                         continue;
                     }

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

@@ -0,0 +1,34 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonAnimation
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string property { get; set; }
+
+        [DataMember]
+        public DataType dataType { get; set; }
+
+        [DataMember]
+        public int loopBehavior { get; set; }
+
+        [DataMember]
+        public int framePerSecond { get; set; }
+
+        [DataMember]
+        public BabylonAnimationKey[] keys { get; set; }
+
+        public enum DataType
+        {
+            Float = 0,
+            Vector3 = 1,
+            Quaternion = 2,
+            Matrix = 3
+        }
+    }
+}

+ 14 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonAnimationKey.cs

@@ -0,0 +1,14 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonAnimationKey
+    {
+        [DataMember]
+        public float frame { get; set; }
+
+        [DataMember]
+        public float[] values { get; set; }
+    }
+}

+ 23 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonBone.cs

@@ -0,0 +1,23 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonBone
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public int index { get; set; }
+
+        [DataMember]
+        public int parentBoneIndex { get; set; }
+
+        [DataMember]
+        public float[] matrix { get; set; }
+
+        [DataMember]
+        public BabylonAnimation animation { get; set; }
+    }
+}

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

@@ -0,0 +1,82 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonCamera
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
+        public string parentId { get; set; }
+
+        [DataMember]
+        public string lockedTargetId { get; set; }
+
+        [DataMember]
+        public float[] position { get; set; }
+
+        [DataMember]
+        public float[] rotation { get; set; }
+
+        [DataMember]
+        public float[] target { get; set; }
+
+        [DataMember]
+        public float fov { get; set; }
+
+        [DataMember]
+        public float minZ { get; set; }
+
+        [DataMember]
+        public float maxZ { get; set; }
+
+        [DataMember]
+        public float speed { get; set; }
+
+        [DataMember]
+        public float inertia { get; set; }
+
+        [DataMember]
+        public bool checkCollisions { get; set; }
+
+        [DataMember]
+        public bool applyGravity { get; set; }
+
+        [DataMember]
+        public float[] ellipsoid { get; set; }
+
+        [DataMember]
+        public bool autoAnimate { get; set; }
+
+        [DataMember]
+        public int autoAnimateFrom { get; set; }
+
+        [DataMember]
+        public int autoAnimateTo { get; set; }
+
+        [DataMember]
+        public bool autoAnimateLoop { get; set; }
+
+        [DataMember]
+        public BabylonAnimation[] animations { get; set; }
+
+
+        public BabylonCamera()
+        {
+            position = new[] { 0f, 0f, 0f };
+            rotation = new[] { 0f, 0f, 0f };
+
+            // Default values
+            fov = 0.8f;
+            minZ = 0.1f;
+            maxZ = 5000.0f;
+            speed = 1.0f;
+            inertia = 0.9f;
+        }
+    }
+}

+ 77 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj

@@ -0,0 +1,77 @@
+<?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>{6150965A-658C-4263-89AD-4F980EB0675D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>BabylonExport.Entities</RootNamespace>
+    <AssemblyName>BabylonExport.Entities</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SccProjectName>SAK</SccProjectName>
+    <SccLocalPath>SAK</SccLocalPath>
+    <SccAuxPath>SAK</SccAuxPath>
+    <SccProvider>SAK</SccProvider>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="SharpDX">
+      <HintPath>..\Max2Babylon\Refs\SharpDX.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="BabylonAnimation.cs" />
+    <Compile Include="BabylonAnimationKey.cs" />
+    <Compile Include="BabylonBone.cs" />
+    <Compile Include="BabylonCamera.cs" />
+    <Compile Include="BabylonLensFlare.cs" />
+    <Compile Include="BabylonLensFlareSystem.cs" />
+    <Compile Include="BabylonLight.cs" />
+    <Compile Include="BabylonMaterial.cs" />
+    <Compile Include="BabylonMesh.cs" />
+    <Compile Include="BabylonMultiMaterial.cs" />
+    <Compile Include="BabylonParticleSystem.cs" />
+    <Compile Include="BabylonScene.cs" />
+    <Compile Include="BabylonShadowGenerator.cs" />
+    <Compile Include="BabylonSkeleton.cs" />
+    <Compile Include="BabylonSubMesh.cs" />
+    <Compile Include="BabylonTexture.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- 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>

+ 20 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonLensFlare.cs

@@ -0,0 +1,20 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonLensFlare
+    {
+        [DataMember]
+        public float position { get; set; }
+
+        [DataMember]
+        public float size { get; set; }
+
+        [DataMember]
+        public float[] color { get; set; }
+
+        [DataMember]
+        public string textureName { get; set; }
+    }
+}

+ 17 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonLensFlareSystem.cs

@@ -0,0 +1,17 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonLensFlareSystem
+    {
+        [DataMember]
+        public string emitterId { get; set; }
+
+        [DataMember]
+        public int borderLimit { get; set; }
+
+        [DataMember]
+        public BabylonLensFlare[] flares { get; set; }
+    }
+}

+ 55 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonLight.cs

@@ -0,0 +1,55 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonLight
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
+        public float[] position { get; set; }
+
+        [DataMember]
+        public float[] direction { get; set; }
+
+        [DataMember]
+        public int type { get; set; }
+
+        [DataMember]
+        public float[] diffuse { get; set; }
+
+        [DataMember]
+        public float[] specular { get; set; }
+
+        [DataMember]
+        public float intensity { get; set; }
+
+        [DataMember]
+        public float range { get; set; }
+
+        [DataMember]
+        public float exponent { get; set; }
+
+        [DataMember]
+        public float angle { get; set; }
+
+        [DataMember]
+        public float[] groundColor { get; set; }
+
+        [DataMember]
+        public string[] excludedMeshesIds { get; set; }
+
+        public BabylonLight()
+        {
+            diffuse = new[] {1.0f, 1.0f, 1.0f};
+            specular = new[] { 1.0f, 1.0f, 1.0f };
+            intensity = 1.0f;
+            range = float.MaxValue;
+        }
+    }
+}

+ 66 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMaterial.cs

@@ -0,0 +1,66 @@
+using System.Runtime.Serialization;
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonMaterial
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
+        public bool backFaceCulling { get; set; }
+
+        [DataMember]
+        public float[] ambient { get; set; }
+
+        [DataMember]
+        public float[] diffuse { get; set; }
+
+        [DataMember]
+        public float[] specular { get; set; }
+
+        [DataMember]
+        public float[] emissive { get; set; }
+
+        [DataMember]
+        public float specularPower { get; set; }
+
+        [DataMember]
+        public float alpha { get; set; }
+
+        [DataMember]
+        public BabylonTexture diffuseTexture { get; set; }
+
+        [DataMember]
+        public BabylonTexture ambientTexture { get; set; }
+
+        [DataMember]
+        public BabylonTexture opacityTexture { get; set; }
+
+        [DataMember]
+        public BabylonTexture reflectionTexture { get; set; }
+
+        [DataMember]
+        public BabylonTexture emissiveTexture { get; set; }
+
+        [DataMember]
+        public BabylonTexture specularTexture { get; set; }
+
+        [DataMember]
+        public BabylonTexture bumpTexture { get; set; }
+
+        public BabylonMaterial()
+        {
+            backFaceCulling = true;
+            ambient = new[] {1.0f, 1.0f, 1.0f};
+            diffuse = new[] { 1.0f, 1.0f, 1.0f };
+            specular = new[] { 1.0f, 1.0f, 1.0f };
+            emissive = new[] { 0f, 0f, 0f };
+            specularPower = 64;
+            alpha = 1.0f;
+        }
+    }
+}

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

@@ -0,0 +1,122 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonMesh
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
+        public string parentId { get; set; }
+
+        [DataMember]
+        public string materialId { get; set; }
+
+        [DataMember]
+        public bool isEnabled { get; set; }
+
+        [DataMember]
+        public bool isVisible { get; set; }
+
+        [DataMember]
+        public bool pickable { get; set; }
+
+        [DataMember]
+        public float[] position { get; set; }
+
+        [DataMember]
+        public float[] rotation { get; set; }
+
+        [DataMember]
+        public float[] scaling { get; set; }
+
+        [DataMember]
+        public float[] rotationQuaternion { get; set; }
+
+        [DataMember]
+        public float[] localMatrix { get; set; }
+
+        [DataMember]
+        public float[] positions { get; set; }
+
+        [DataMember]
+        public float[] normals { get; set; }
+
+        [DataMember]
+        public float[] uvs { get; set; }
+
+        [DataMember]
+        public float[] uvs2 { get; set; }
+
+        [DataMember]
+        public float[] colors { get; set; }
+
+        [DataMember]
+        public int[] matricesIndices { get; set; }
+
+        [DataMember]
+        public float[] matricesWeights { get; set; }
+
+        [DataMember]
+        public int[] indices { get; set; }
+
+        [DataMember]
+        public bool checkCollisions { get; set; }
+
+        [DataMember]
+        public bool receiveShadows { get; set; }    
+    
+        [DataMember]
+        public bool infiniteDistance { get; set; }
+        
+        [DataMember]
+        public int billboardMode { get; set; }
+
+        [DataMember]
+        public float visibility { get; set; }
+
+        [DataMember]
+        public BabylonSubMesh[] subMeshes { get; set; }
+
+        [DataMember]
+        public int skeletonId { get; set; }
+
+        [DataMember]
+        public bool autoAnimate { get; set; }
+
+        [DataMember]
+        public int autoAnimateFrom { get; set; }
+
+        [DataMember]
+        public int autoAnimateTo { get; set; }
+
+        [DataMember]
+        public bool autoAnimateLoop { get; set; }
+
+        [DataMember]
+        public BabylonAnimation[] animations { get; set; }
+
+        public BabylonMesh()
+        {
+            isEnabled = true;
+            isVisible = true;
+
+            position = new[] { 0f, 0f, 0f };
+            rotation = new[] { 0f, 0f, 0f };
+            scaling = new[] { 1f, 1f, 1f };
+
+            billboardMode = 0;
+
+            visibility = 1.0f;
+
+            skeletonId = -1;
+
+            pickable = true;
+        }
+    }
+}

+ 17 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMultiMaterial.cs

@@ -0,0 +1,17 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonMultiMaterial
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
+        public string[] materials { get; set; }        
+    }
+}

+ 86 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonParticleSystem.cs

@@ -0,0 +1,86 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonParticleSystem
+    {
+        [DataMember]
+        public string emitterId { get; set; }
+
+        [DataMember]
+        public float[] gravity { get; set; }
+
+        [DataMember]
+        public float[] direction1 { get; set; }
+
+        [DataMember]
+        public float[] direction2 { get; set; }
+
+        [DataMember]
+        public float[] minEmitBox { get; set; }
+
+        [DataMember]
+        public float[] maxEmitBox { get; set; }
+
+        [DataMember]
+        public float[] color1 { get; set; }
+
+        [DataMember]
+        public float[] color2 { get; set; }
+
+        [DataMember]
+        public float[] colorDead { get; set; }
+
+        [DataMember]
+        public float deadAlpha { get; set; }
+
+        [DataMember]
+        public float emitRate { get; set; }
+
+        [DataMember]
+        public float updateSpeed { get; set; }
+
+        [DataMember]
+        public int targetStopFrame { get; set; }
+
+        [DataMember]
+        public float minEmitPower { get; set; }
+
+        [DataMember]
+        public float maxEmitPower { get; set; }
+
+        [DataMember]
+        public float minLifeTime { get; set; }
+
+        [DataMember]
+        public float maxLifeTime { get; set; }
+
+        [DataMember]
+        public float minSize { get; set; }
+
+        [DataMember]
+        public float maxSize { get; set; }
+
+        [DataMember]
+        public float minAngularSpeed { get; set; }
+
+        [DataMember]
+        public float maxAngularSpeed { get; set; }
+
+        [DataMember]
+        public string textureName { get; set; }
+
+        [DataMember]
+        public int blendMode { get; set; }
+
+        [DataMember]
+        public int capacity { get; set; }
+
+        [DataMember]
+        public float[] textureMask { get; set; }
+
+        [DataMember]
+        public bool linkToEmitter { get; set; }
+    }
+}

+ 163 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs

@@ -0,0 +1,163 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization;
+using SharpDX;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonScene
+    {
+        [DataMember]
+        public bool autoClear { get; set; }
+
+        [DataMember]
+        public float[] clearColor { get; set; }
+
+        [DataMember]
+        public float[] ambientColor { get; set; }
+
+        [DataMember]
+        public int fogMode { get; set; }
+
+        [DataMember]
+        public float[] fogColor { get; set; }
+
+        [DataMember]
+        public float fogStart { get; set; }
+
+        [DataMember]
+        public float fogEnd { get; set; }
+
+        [DataMember]
+        public float fogDensity { get; set; }
+
+        [DataMember]
+        public float[] gravity { get; set; }
+
+        [DataMember]
+        public BabylonCamera[] cameras { get; set; }
+
+        [DataMember]
+        public string activeCameraID { get; set; }
+
+        [DataMember]
+        public BabylonLight[] lights { get; set; }
+
+        [DataMember]
+        public BabylonMesh[] meshes { get; set; }
+
+        [DataMember]
+        public BabylonMaterial[] materials { get; set; }
+
+        [DataMember]
+        public BabylonMultiMaterial[] multiMaterials { get; set; }
+
+        [DataMember]
+        public BabylonParticleSystem[] particleSystems { get; set; }
+
+        [DataMember]
+        public BabylonLensFlareSystem[] lensFlareSystems { get; set; }
+
+        [DataMember]
+        public BabylonShadowGenerator[] shadowGenerators { get; set; }
+
+        [DataMember]
+        public BabylonSkeleton[] skeletons { get; set; }
+        
+        public Vector3 MaxVector { get; set; }
+        public Vector3 MinVector { get; set; }
+
+        public string OutputPath { get; private set; }
+
+        public List<BabylonMesh> MeshesList { get; private set; }
+        public List<BabylonCamera> CamerasList { get; private set; }
+        public List<BabylonLight> LightsList { get; private set; }
+        public List<BabylonMaterial> MaterialsList { get; private set; }
+        public List<BabylonMultiMaterial> MultiMaterialsList { get; private set; }
+        public List<BabylonShadowGenerator> ShadowGeneratorsList { get; private set; }
+        public List<BabylonSkeleton> SkeletonsList { get; private set; }
+
+        readonly List<string> exportedTextures = new List<string>();
+
+        public BabylonScene(string outputPath)
+        {
+            OutputPath = outputPath;
+
+            MeshesList = new List<BabylonMesh>();
+            MaterialsList = new List<BabylonMaterial>();
+            CamerasList = new List<BabylonCamera>();
+            LightsList = new List<BabylonLight>();
+            MultiMaterialsList = new List<BabylonMultiMaterial>();
+            ShadowGeneratorsList = new List<BabylonShadowGenerator>();
+            SkeletonsList = new List<BabylonSkeleton>();
+
+            // Default values
+            autoClear = true;
+            clearColor = new[] { 0.2f, 0.2f, 0.3f };
+            ambientColor = new[] {0f, 0f, 0f };
+            gravity = new[] {0f, 0f, -0.9f};
+        }
+
+        public void Prepare(bool generateDefaultLight = true)
+        {
+            meshes = MeshesList.ToArray();
+
+            materials = MaterialsList.ToArray();
+            multiMaterials = MultiMaterialsList.ToArray();
+            shadowGenerators = ShadowGeneratorsList.ToArray();
+            skeletons = SkeletonsList.ToArray();
+
+            if (CamerasList.Count == 0)
+            {
+                var camera = new BabylonCamera {name = "Default camera", id = Guid.NewGuid().ToString()};
+
+                var distanceVector = MaxVector - MinVector;
+                var midPoint = MinVector +distanceVector / 2;
+                camera.target = midPoint.ToArray();
+                camera.position = (midPoint + distanceVector).ToArray();
+
+                var distance = distanceVector.Length();
+                camera.speed =  distance/ 50.0f;
+                camera.maxZ = distance * 4f;
+
+                camera.minZ = distance < 100.0f ? 0.1f : 1.0f;
+
+                CamerasList.Add(camera);
+            }
+
+            if (LightsList.Count == 0 && generateDefaultLight)
+            {
+                var light = new BabylonLight {name = "Default light", id = Guid.NewGuid().ToString()};
+
+                var midPoint = MinVector + (MaxVector - MinVector) / 2;
+                light.type = 0;
+                light.position = (midPoint + (MaxVector - MinVector)).ToArray();
+
+                light.diffuse = new Vector3(1, 1, 1).ToArray();
+                light.specular = new Vector3(1, 1, 1).ToArray();
+
+                LightsList.Add(light);
+            }
+            
+            cameras = CamerasList.ToArray();
+            lights = LightsList.ToArray();
+
+            if (activeCameraID == null)
+            {
+                activeCameraID = CamerasList[0].id;
+            }
+        }
+
+        public void AddTexture(string diffuseTexture)
+        {
+            if (exportedTextures.Contains(diffuseTexture))
+                return;
+
+            exportedTextures.Add(diffuseTexture);
+
+            File.Copy(diffuseTexture, Path.Combine(OutputPath, Path.GetFileName(diffuseTexture)), true);
+        }
+    }
+}

+ 21 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonShadowGenerator.cs

@@ -0,0 +1,21 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonShadowGenerator
+    {
+        [DataMember]
+        public int mapSize { get; set; }
+
+        [DataMember]
+        public string lightId { get; set; }
+
+        [DataMember]
+        public bool useVarianceShadowMap { get; set; }
+
+        [DataMember]
+        public string[] renderList { get; set; }
+
+    }
+}

+ 17 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonSkeleton.cs

@@ -0,0 +1,17 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonSkeleton
+    {
+        [DataMember]
+        public int id { get; set; }
+        
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public BabylonBone[] bones { get; set; }
+    }
+}

+ 24 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonSubMesh.cs

@@ -0,0 +1,24 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonSubMesh
+    {
+        [DataMember]
+        public int materialIndex { get; set; }
+
+        [DataMember]
+        public int verticesStart { get; set; }
+
+        [DataMember]
+        public int verticesCount { get; set; }
+
+        [DataMember]
+        public int indexStart { get; set; }
+
+        [DataMember]
+        public int indexCount { get; set; }
+
+    }
+}

+ 87 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonTexture.cs

@@ -0,0 +1,87 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonTexture
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public float level { get; set; }
+
+        [DataMember]
+        public bool hasAlpha { get; set; }
+
+        [DataMember]
+        public bool getAlphaFromRGB { get; set; }
+
+        [DataMember]
+        public int coordinatesMode { get; set; }
+        
+        [DataMember]
+        public bool isCube { get; set; }
+
+        [DataMember]
+        public float uOffset { get; set; }
+
+        [DataMember]
+        public float vOffset { get; set; }
+
+        [DataMember]
+        public float uScale { get; set; }
+
+        [DataMember]
+        public float vScale { get; set; }
+
+        [DataMember]
+        public float uAng { get; set; }
+
+        [DataMember]
+        public float vAng { get; set; }
+
+        [DataMember]
+        public float wAng { get; set; }
+
+        [DataMember]
+        public int wrapU { get; set; }
+
+        [DataMember]
+        public int wrapV { get; set; }
+
+        [DataMember]
+        public int coordinatesIndex { get; set; }
+
+        [DataMember]
+        public bool isRenderTarget { get; set; }
+
+        [DataMember]
+        public int renderTargetSize { get; set; }
+
+        [DataMember]
+        public float[] mirrorPlane { get; set; }
+
+        [DataMember]
+        public string[] renderList { get; set; }
+
+        [DataMember]
+        public BabylonAnimation[] animations { get; set; }
+        
+        public BabylonTexture()
+        {
+            level = 1.0f;
+            uOffset = 0;
+            vOffset = 0;
+            uScale = 1.0f;
+            vScale = 1.0f;
+            uAng = 0;
+            vAng = 0;
+            wAng = 0;
+            wrapU = 1;
+            wrapV = 1;
+            hasAlpha = false;
+            coordinatesIndex = 0;
+        }
+    }
+}

+ 36 - 0
Exporters/3ds Max/BabylonExport.Entities/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("BabylonExport.Entities")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BabylonExport.Entities")]
+[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("629990a5-774d-43d7-a3b2-ab29eb6697ca")]
+
+// 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")]

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


+ 28 - 0
Exporters/3ds Max/Max2Babylon.sln

@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.30501.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon", "Max2Babylon\Max2Babylon.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC489}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabylonExport.Entities", "BabylonExport.Entities\BabylonExport.Entities.csproj", "{6150965A-658C-4263-89AD-4F980EB0675D}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

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

@@ -0,0 +1,8 @@
+using Autodesk.Max.Plugins;
+
+namespace Max2Babylon
+{
+    class BabylonActionCallback : ActionCallback
+    {
+    }
+}

+ 68 - 0
Exporters/3ds Max/Max2Babylon/BabylonExportActionItem.cs

@@ -0,0 +1,68 @@
+using System;
+using Autodesk.Max;
+using Autodesk.Max.IQuadMenuContext;
+using MaxSharp;
+using ActionItem = Autodesk.Max.Plugins.ActionItem;
+
+namespace Max2Babylon
+{
+    public class BabylonExportActionItem : ActionItem
+    {
+        private ExporterForm form;
+
+        public override bool ExecuteAction()
+        {
+            if (form == null)
+                form = new ExporterForm(this);
+            form.Show();
+
+            return true;
+        }
+
+        public void Close()
+        {
+            form.Dispose();
+            form = null;
+        }
+
+        public override int Id_
+        {
+            get { return 1; }
+        }
+
+        public override string ButtonText
+        {
+            get { return "Babylon File Exporter"; }
+        }
+
+        public override string MenuText
+        {
+            get { return "&Babylon File Exporter..."; }
+        }
+
+        public override string DescriptionText
+        {
+            get { return "Generate a babylon.js scene file2"; }
+        }
+
+        public override string CategoryText
+        {
+            get { return "Babylon"; }
+        }
+
+        public override bool IsChecked_
+        {
+            get { return false; }
+        }
+
+        public override bool IsItemVisible
+        {
+            get { return true; }
+        }
+
+        public override bool IsEnabled_
+        {
+            get { return true; }
+        }
+    }
+}

+ 84 - 0
Exporters/3ds Max/Max2Babylon/BabylonPropertiesActionItem.cs

@@ -0,0 +1,84 @@
+using Autodesk.Max;
+using ActionItem = Autodesk.Max.Plugins.ActionItem;
+
+namespace Max2Babylon
+{
+    public class BabylonPropertiesActionItem : ActionItem
+    {
+        public override bool ExecuteAction()
+        {
+            if (Loader.Core.SelNodeCount == 0)
+            {
+                using (var frm = new ScenePropertiesForm())
+                {
+                    frm.ShowDialog();
+                    return true;
+                }
+            }
+
+            var firstNode = Loader.Core.GetSelNode(0);
+
+            if (firstNode.ObjectRef != null && firstNode.ObjectRef.SuperClassID == SClass_ID.Camera)
+            {
+                using (var frm = new CameraPropertiesForm())
+                {
+                    frm.ShowDialog();
+                    return true;
+                }
+            }
+
+            if (firstNode.ObjectRef != null && firstNode.ObjectRef.SuperClassID == SClass_ID.Geomobject)
+            {
+                using (var frm = new ObjectPropertiesForm())
+                {
+                    frm.ShowDialog();
+                    return true;
+                }
+            }
+
+            Loader.Core.PushPrompt("Selected entity does not have Babylon.js specific properties");
+
+            return true;
+        }
+
+        public override int Id_
+        {
+            get { return 1; }
+        }
+
+        public override string ButtonText
+        {
+            get { return "Babylon Properties"; }
+        }
+
+        public override string MenuText
+        {
+            get { return "Babylon Properties"; }
+        }
+
+        public override string DescriptionText
+        {
+            get { return "UI for setting Babylon.js specific properties"; }
+        }
+
+        public override string CategoryText
+        {
+            get { return "Babylon"; }
+        }
+
+        public override bool IsChecked_
+        {
+            get { return false; }
+        }
+
+        public override bool IsItemVisible
+        {
+            get { return true; }
+        }
+
+        public override bool IsEnabled_
+        {
+            get { return true; }
+        }
+    }
+}

+ 52 - 0
Exporters/3ds Max/Max2Babylon/Descriptor.cs

@@ -0,0 +1,52 @@
+using Autodesk.Max;
+
+namespace Max2Babylon
+{
+    public class Descriptor : Autodesk.Max.Plugins.ClassDesc2
+    {
+        public override object Create(bool loading)
+        {
+            return new GlobalUtility();
+        }
+
+        public override bool IsPublic
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public override string ClassName
+        {
+            get
+            {
+                return "Babylon File Exporter";
+            }
+        }
+
+        public override SClass_ID SuperClassID
+        {
+            get
+            {
+                return SClass_ID.Gup;
+            }
+        }
+
+        public override IClass_ID ClassID
+        {
+            get
+            {
+                return Loader.Class_ID;
+            }
+        }
+
+        public override string Category
+        {
+            get
+            {
+                return "Babylon";
+            }
+        }
+    }
+}

+ 61 - 0
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Camera.cs

@@ -0,0 +1,61 @@
+using System;
+using BabylonExport.Entities;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private BabylonCamera ExportCamera(Node cameraNode, BabylonScene babylonScene)
+        {
+            var maxCamera = (cameraNode.Object as Camera)._Camera;
+            var babylonCamera = new BabylonCamera();
+
+            RaiseMessage(cameraNode.Name, true);
+            babylonCamera.name = cameraNode.Name;
+            babylonCamera.id = cameraNode.GetGuid().ToString();
+            if (cameraNode.HasParent())
+            {
+                babylonCamera.parentId = cameraNode.Parent.GetGuid().ToString();
+            }
+
+            babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Interval.Forever._IInterval));
+            babylonCamera.minZ = maxCamera.GetEnvRange(0, 0, Interval.Forever._IInterval);
+            babylonCamera.maxZ = maxCamera.GetEnvRange(0, 1, Interval.Forever._IInterval);
+
+            if (babylonCamera.minZ == 0.0f)
+            {
+                babylonCamera.minZ = 0.1f;
+            }
+
+            // Control
+            babylonCamera.speed = cameraNode._Node.GetFloatProperty("babylonjs_speed");
+            babylonCamera.inertia = cameraNode._Node.GetFloatProperty("babylonjs_inertia");
+
+            // Collisions
+            babylonCamera.checkCollisions = cameraNode._Node.GetBoolProperty("babylonjs_checkcollisions");
+            babylonCamera.applyGravity = cameraNode._Node.GetBoolProperty("babylonjs_applygravity");
+            babylonCamera.ellipsoid = cameraNode._Node.GetVector3Property("babylonjs_ellipsoid");
+
+            // Position
+            var wm = cameraNode.GetWorldMatrix(0, cameraNode.HasParent());
+            var position = wm.Trans;
+            babylonCamera.position = position.ToArraySwitched();
+
+            // Target
+            var target = cameraNode._Node.Target;
+            if (target != null)
+            {
+                babylonCamera.lockedTargetId = target.GetGuid().ToString();
+            }
+            else
+            {
+                var dir = wm.GetRow(2).MultiplyBy(-1);
+                babylonCamera.target = dir.ToArraySwitched();
+            }
+
+            babylonScene.CamerasList.Add(babylonCamera);
+            return babylonCamera;
+        }
+    }
+}

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

@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using Autodesk.Max;
+using BabylonExport.Entities;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private BabylonLight ExportLight(Node lightNode, BabylonScene babylonScene)
+        {
+            var maxLight = (lightNode.Object as Light);
+            var babylonLight = new BabylonLight();
+
+            RaiseMessage(maxLight.Name, true);
+            babylonLight.name = lightNode.Name;
+            babylonLight.id = lightNode.GetGuid().ToString();
+
+            // Type
+            var lightState = Loader.Global.LightState.Create();
+            maxLight._Light.EvalLightState(0, Interval.Forever._IInterval, lightState);
+            var directionScale = -1;
+
+            switch (lightState.Type)
+            {
+                case LightType.OmniLgt:
+                    babylonLight.type = 0;
+                    break;
+                case LightType.SpotLgt:
+                    babylonLight.type = 2;
+                    babylonLight.angle = (float)(maxLight.GetFallOffSize(0, Interval.Forever) * Math.PI / 180.0f);
+                    babylonLight.exponent = 1;
+                    break;
+                case LightType.DirectLgt:
+                    babylonLight.type = 1;
+
+                    // Shadows
+                    if (maxLight.ShadowMethod == 1)
+                    {
+                        ExportShadowGenerator(lightNode, babylonScene);
+                    }
+
+                    break;
+                case LightType.AmbientLgt:
+                    babylonLight.type = 3;
+                    babylonLight.groundColor = new float[] { 0, 0, 0 };
+                    directionScale = 1;
+                    break;
+            }
+
+            // Position
+            var wm = lightNode.GetWorldMatrix(0, false);
+            var position = wm.Trans;
+            babylonLight.position = position.ToArraySwitched();
+
+            // Direction
+            var target = lightNode._Node.Target;
+            if (target != null)
+            {
+                var targetWm = target.GetObjTMBeforeWSM(0, Interval.Forever._IInterval);
+                var targetPosition = targetWm.Trans;
+
+                var direction = targetPosition.Subtract(position);
+                babylonLight.direction = direction.ToArraySwitched();
+            }
+            else
+            {
+                var dir = wm.GetRow(2).MultiplyBy(directionScale);
+                babylonLight.direction = dir.ToArraySwitched();
+            }
+
+            // Exclusion
+            var maxScene = Kernel.Scene;
+            var inclusion = maxLight._Light.ExclList.TestFlag(1); //NT_INCLUDE 
+            var checkExclusionList = maxLight._Light.ExclList.TestFlag(2); //NT_AFFECT_ILLUM
+
+            if (checkExclusionList)
+            {
+                var list = new List<string>();
+
+                foreach (var meshNode in maxScene.NodesListBySuperClass(SuperClassID.GeometricObject))
+                {
+                    if (meshNode._Node.CastShadows == 1)
+                    {
+                        var inList = maxLight._Light.ExclList.FindNode(meshNode._Node) != -1;
+
+                        if ((!inList && inclusion) || (inList && !inclusion))
+                        {
+                            list.Add(meshNode.GetGuid().ToString());
+                        }
+                    }
+                }
+
+                babylonLight.excludedMeshesIds = list.ToArray();
+            }
+
+            // Other fields
+            babylonLight.intensity = maxLight.GetIntensity(0, Interval.Forever);
+
+            babylonLight.diffuse = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Interval.Forever).ToArray() : new float[] { 0, 0, 0 };
+            babylonLight.specular = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Interval.Forever).ToArray() : new float[] { 0, 0, 0 };
+
+            if (maxLight.UseAttenuation)
+            {
+                babylonLight.range = maxLight.GetAttenuation(0, 1, Interval.Forever);
+            }
+
+            babylonScene.LightsList.Add(babylonLight);
+            return babylonLight;
+        }
+    }
+}

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

@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using Autodesk.Max;
+using BabylonExport.Entities;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        readonly List<Material> referencedMaterials = new List<Material>();
+
+        private void ExportMaterial(Material materialNode, BabylonScene babylonScene)
+        {
+            var name = materialNode._Mtl.Name;
+            var id = materialNode.GetGuid().ToString();
+
+            RaiseMessage(name, true);
+
+            if (materialNode.NumSubMaterials > 0)
+            {
+                var babylonMultimaterial = new BabylonMultiMaterial();
+                babylonMultimaterial.name = name;
+                babylonMultimaterial.id = id;
+
+                var guids = new List<string>();
+
+                for (var index = 0; index < materialNode.NumSubMaterials; index++)
+                {
+                    var subMat = materialNode.GetSubMaterial(index) as Material;
+
+                    if (subMat != null)
+                    {
+                        guids.Add(subMat.GetGuid().ToString());
+
+                        if (!referencedMaterials.Contains(subMat))
+                        {
+                            referencedMaterials.Add(subMat);
+                            ExportMaterial(subMat, babylonScene);
+                        }
+                    }
+                    else
+                    {
+                        guids.Add(Guid.Empty.ToString());
+                    }
+                }
+
+                babylonMultimaterial.materials = guids.ToArray();
+
+                babylonScene.MultiMaterialsList.Add(babylonMultimaterial);
+            }
+
+
+            var babylonMaterial = new BabylonMaterial();
+            babylonMaterial.name = name;
+            babylonMaterial.id = id;
+
+            babylonMaterial.ambient = materialNode.GetAmbient(0).ToArray();
+            babylonMaterial.diffuse = materialNode.GetDiffuse(0).ToArray();
+            babylonMaterial.specular = materialNode.GetSpecular(0).Scale(materialNode.GetShininess(0));
+            babylonMaterial.specularPower = materialNode.GetShinyStrength(0);
+
+            babylonMaterial.emissive = materialNode.SelfIlluminationColorOn ? materialNode.GetSelfIllumColor(0).ToArray() : materialNode.GetDiffuse(0).Scale(materialNode.GetSelfIllumination(0));
+            babylonMaterial.alpha = 1.0f - materialNode.GetTransparency(0);
+
+            var stdMat = materialNode._Mtl.GetParamBlock(0).Owner as IStdMat2;
+
+            if (stdMat != null)
+            {
+                // Textures
+                babylonMaterial.ambientTexture = ExportTexture(stdMat, 0, babylonScene);    // Ambient
+                babylonMaterial.diffuseTexture = ExportTexture(stdMat, 1, babylonScene);    // Diffuse
+                babylonMaterial.specularTexture = ExportTexture(stdMat, 2, babylonScene);   // Specular
+                babylonMaterial.emissiveTexture = ExportTexture(stdMat, 5, babylonScene);   // Emissive
+                babylonMaterial.opacityTexture = ExportTexture(stdMat, 6, babylonScene);    // Opacity
+                babylonMaterial.bumpTexture = ExportTexture(stdMat, 8, babylonScene);       // Bump
+                babylonMaterial.reflectionTexture = ExportTexture(stdMat, 9, babylonScene); // Reflection
+
+                // Constraints
+                if (babylonMaterial.diffuseTexture != null)
+                {
+                    babylonMaterial.emissive = new [] { 1.0f, 1.0f, 1.0f };
+                }
+
+                if (babylonMaterial.emissiveTexture != null)
+                {
+                    babylonMaterial.emissive = new float[]{0, 0, 0};
+                }
+
+                if (babylonMaterial.opacityTexture != null && babylonMaterial.diffuseTexture != null &&
+                    babylonMaterial.diffuseTexture.name == babylonMaterial.opacityTexture.name &&
+                    babylonMaterial.diffuseTexture.hasAlpha && !babylonMaterial.opacityTexture.getAlphaFromRGB)
+                {
+                    // This is a alpha testing purpose
+                    babylonMaterial.opacityTexture = null;
+                    babylonMaterial.diffuseTexture.hasAlpha = true;
+                    RaiseWarning("Opacity texture was removed because alpha from diffuse texture can be use instead", true);
+                    RaiseWarning("If you do not want this behavior, just set Alpha Source = None on your diffuse texture", true);
+                }
+            }
+
+            babylonScene.MaterialsList.Add(babylonMaterial);
+        }
+    }
+}

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

@@ -0,0 +1,266 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Autodesk.Max;
+using BabylonExport.Entities;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private BabylonMesh ExportMesh(Node meshNode, BabylonScene babylonScene)
+        {
+            var babylonMesh = new BabylonMesh();
+            int vx1, vx2, vx3;
+
+            RaiseMessage(meshNode.Name, true);
+            babylonMesh.name = meshNode.Name;
+            babylonMesh.id = meshNode.GetGuid().ToString();
+            if (meshNode.HasParent())
+            {
+                babylonMesh.parentId = meshNode.Parent.GetGuid().ToString();
+            }
+
+            // Misc.
+            babylonMesh.isVisible = meshNode._Node.Renderable == 1;
+            babylonMesh.pickable = meshNode._Node.GetBoolProperty("babylonjs_checkpickable");
+            babylonMesh.receiveShadows = meshNode._Node.RcvShadows == 1;
+
+            // Collisions
+            babylonMesh.checkCollisions = meshNode._Node.GetBoolProperty("babylonjs_checkcollisions");            
+
+            // Position / rotation / scaling
+            var wm = meshNode.GetWorldMatrix(0, meshNode.HasParent());
+            babylonMesh.position = wm.Trans.ToArraySwitched();
+
+            var parts = Loader.Global.AffineParts.Create();
+            Loader.Global.DecompAffine(wm, parts);
+
+            //var rotate = new float[3];
+
+            //IntPtr xPtr = Marshal.AllocHGlobal(sizeof(float));
+            //IntPtr yPtr = Marshal.AllocHGlobal(sizeof(float));
+            //IntPtr zPtr = Marshal.AllocHGlobal(sizeof(float));
+            //parts.Q.GetEuler(xPtr, yPtr, zPtr);
+
+            //Marshal.Copy(xPtr, rotate, 0, 1);
+            //Marshal.Copy(yPtr, rotate, 1, 1);
+            //Marshal.Copy(zPtr, rotate, 2, 1);
+
+            //var temp = -rotate[1];
+            //rotate[0] = rotate[0] * parts.F;
+            //rotate[1] = -rotate[2] * parts.F;
+            //rotate[2] = temp * parts.F;
+
+            //babylonMesh.rotation = rotate;
+
+            babylonMesh.rotationQuaternion = parts.Q.ToArray();
+
+            babylonMesh.scaling = parts.K.ToArraySwitched();
+
+            if (wm.Parity)
+            {
+                vx1 = 2;
+                vx2 = 1;
+                vx3 = 0;
+            }
+            else
+            {
+                vx1 = 0;
+                vx2 = 1;
+                vx3 = 2;
+            }
+
+            // Pivot
+            //var pivotMatrix = Matrix3.Identity._IMatrix3;
+            //pivotMatrix.PreTranslate(meshNode._Node.ObjOffsetPos);
+            //Loader.Global.PreRotateMatrix(pivotMatrix, meshNode._Node.ObjOffsetRot);
+            //Loader.Global.ApplyScaling(pivotMatrix, meshNode._Node.ObjOffsetScale);
+            //babylonMesh.localMatrix = pivotMatrix.ToArray();
+
+            // Mesh
+            var objectState = meshNode._Node.EvalWorldState(0, false);
+            var mesh = objectState.Obj.GetMesh();
+            var computedMesh = meshNode.GetMesh();
+
+            if (mesh != null)
+            {
+                mesh.BuildNormals();
+
+                if (mesh.NumFaces < 1)
+                {
+                    RaiseError(string.Format("Mesh {0} has no face", babylonMesh.name));
+                }
+
+                if (mesh.NumVerts < 3)
+                {
+                    RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name));
+                }
+
+                if (mesh.NumVerts >= 65536)
+                {
+                    RaiseError(string.Format("Mesh {0} has too many vertices (more than 65535)", babylonMesh.name));
+                }
+
+                // Material
+                var mtl = meshNode.Material;
+                var multiMatsCount = 1;
+
+                if (mtl != null)
+                {
+                    babylonMesh.materialId = mtl.GetGuid().ToString();
+
+                    if (!referencedMaterials.Contains(mtl))
+                    {
+                        referencedMaterials.Add(mtl);
+                    }
+
+                    multiMatsCount = Math.Max(mtl.NumSubMaterials, 1);
+                }
+
+                babylonMesh.visibility = meshNode._Node.GetVisibility(0, Interval.Forever._IInterval);
+
+                var vertices = new List<GlobalVertex>();
+                var indices = new List<int>();
+                var matIDs = new List<int>();
+
+                var hasUV = mesh.NumTVerts > 0;
+                var hasUV2 = mesh.GetNumMapVerts(2) > 0;
+
+                for (var face = 0; face < mesh.NumFaces; face++)
+                {
+                    indices.Add(CreateGlobalVertex(mesh, computedMesh, face, vx1, vertices, hasUV, hasUV2));
+                    indices.Add(CreateGlobalVertex(mesh, computedMesh, face, vx2, vertices, hasUV, hasUV2));
+                    indices.Add(CreateGlobalVertex(mesh, computedMesh, face, vx3, vertices, hasUV, hasUV2));
+                    matIDs.Add(mesh.Faces[face].MatID % multiMatsCount);
+                }
+
+                // Buffers
+                babylonMesh.positions = vertices.SelectMany(v => v.Position.ToArraySwitched()).ToArray();
+                babylonMesh.normals = vertices.SelectMany(v => v.Normal.ToArraySwitched()).ToArray();
+                if (hasUV)
+                {
+                    babylonMesh.uvs = vertices.SelectMany(v => v.UV.ToArray()).ToArray();
+                }
+                if (hasUV2)
+                {
+                    babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2.ToArray()).ToArray();
+                }
+
+                // Submeshes
+                var sortedIndices = new List<int>();
+                var subMeshes = new List<BabylonSubMesh>();
+                var indexStart = 0;
+                for (var index = 0; index < multiMatsCount; index++)
+                {
+                    var subMesh = new BabylonSubMesh();
+                    var indexCount = 0;
+                    var minVertexIndex = int.MaxValue;
+                    var maxVertexIndex = int.MinValue;
+
+                    subMesh.indexStart = indexStart;
+                    subMesh.materialIndex = index;
+                    
+                    for (var face = 0; face < matIDs.Count; face++)
+                    {
+                        if (matIDs[face] == index)
+                        {
+                            var a = indices[3*face];
+                            var b = indices[3*face + 1];
+                            var c = indices[3*face + 2];
+
+                            sortedIndices.Add(a);
+                            sortedIndices.Add(b);
+                            sortedIndices.Add(c);
+                            indexCount += 3;
+
+                            if (a < minVertexIndex)
+                            {
+                                minVertexIndex = a;
+                            }
+                            
+                            if (b < minVertexIndex)
+                            {
+                                minVertexIndex = b;
+                            }
+
+                            if (c < minVertexIndex)
+                            {
+                                minVertexIndex = c;
+                            }
+
+                            if (a > maxVertexIndex)
+                            {
+                                maxVertexIndex = a;
+                            }
+
+                            if (b > maxVertexIndex)
+                            {
+                                maxVertexIndex = b;
+                            }
+
+                            if (c > maxVertexIndex)
+                            {
+                                maxVertexIndex = c;
+                            }
+                        }
+                    }
+                    subMesh.indexCount = indexCount;
+                    subMesh.verticesStart = minVertexIndex;
+                    subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1;
+
+                    indexStart += indexCount;
+
+                    subMeshes.Add(subMesh);
+                }
+                babylonMesh.subMeshes = subMeshes.ToArray();
+
+
+                // Buffers - Indices
+                babylonMesh.indices = sortedIndices.ToArray();
+            }
+
+            babylonScene.MeshesList.Add(babylonMesh);
+
+            return babylonMesh;
+        }
+
+        int CreateGlobalVertex(IMesh mesh, Mesh computedMesh, int face, int facePart, List<GlobalVertex> vertices, bool hasUV, bool hasUV2)
+        {
+            var vertexIndex = (int)mesh.Faces[face].V[facePart];
+
+
+            var vertex = new GlobalVertex
+            {
+                Position = mesh.Verts[vertexIndex],
+                Normal = computedMesh.vnormals[vertexIndex]._IPoint3
+            };
+
+            if (hasUV)
+            {
+                var tvertexIndex = (int)mesh.TvFace[face].T[facePart];
+                vertex.UV = Loader.Global.Point2.Create(mesh.TVerts[tvertexIndex].X, mesh.TVerts[tvertexIndex].Y);
+            }
+
+            if (hasUV2)
+            {
+                var tvertexIndex = (int)mesh.MapFaces(2)[face].T[facePart];
+                vertex.UV2 = Loader.Global.Point2.Create(mesh.MapVerts(2)[tvertexIndex].X, mesh.MapVerts(2)[tvertexIndex].Y);
+            }
+
+
+            var index = vertices.IndexOf(vertex);
+
+            if (index > -1)
+            {
+                return index;
+            }
+
+            vertices.Add(vertex);
+
+            return vertices.Count - 1;
+        }
+    }
+}

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

@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using BabylonExport.Entities;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private BabylonShadowGenerator ExportShadowGenerator(Node lightNode, BabylonScene babylonScene)
+        {
+            var maxLight = (lightNode.Object as Light);
+            var babylonShadowGenerator = new BabylonShadowGenerator();
+
+            RaiseMessage("Exporting shadow map", true, false, true);
+
+            babylonShadowGenerator.lightId = lightNode.GetGuid().ToString();
+
+            babylonShadowGenerator.mapSize = maxLight.GetMapSize(0, Interval.Forever);
+
+            var maxScene = Kernel.Scene;
+            var list = new List<string>();
+
+            var inclusion = maxLight._Light.ExclList.TestFlag(1); //NT_INCLUDE 
+            var checkExclusionList = maxLight._Light.ExclList.TestFlag(4); //NT_AFFECT_SHADOWCAST 
+
+            foreach (var meshNode in maxScene.NodesListBySuperClass(SuperClassID.GeometricObject))
+            {
+                if (meshNode._Node.CastShadows == 1)
+                {
+                    var inList = maxLight._Light.ExclList.FindNode(meshNode._Node) != -1;
+
+                    if (!checkExclusionList || (inList && inclusion) || (!inList && !inclusion))
+                    {
+                        list.Add(meshNode.GetGuid().ToString());
+                    }
+                }
+            }
+            babylonShadowGenerator.renderList = list.ToArray();
+
+            babylonScene.ShadowGeneratorsList.Add(babylonShadowGenerator);
+            return babylonShadowGenerator;
+        }
+    }
+}

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

@@ -0,0 +1,112 @@
+using System.Collections.Generic;
+using System.IO;
+using Autodesk.Max;
+using Autodesk.Max.MaxSDK.Util;
+using BabylonExport.Entities;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private BabylonTexture ExportTexture(IStdMat2 stdMat, int index, BabylonScene babylonScene)
+        {
+            if (!stdMat.MapEnabled(index))
+            {
+                return null;
+            }
+            var babylonTexture = new BabylonTexture();
+
+            var texMap = stdMat.GetSubTexmap(index);
+            var texture = (IBitmapTex) texMap.GetParamBlock(0).Owner;
+
+            if (texture == null)
+            {
+                return null;
+            }
+
+            babylonTexture.hasAlpha = (texture.AlphaSource != 3);
+            babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2);
+            babylonTexture.level = stdMat.GetTexmapAmt(index, 0);
+
+            var uvGen = texture.UVGen;
+
+            switch (uvGen.GetCoordMapping(0))
+            {
+                case 1: //MAP_SPHERICAL
+                    babylonTexture.coordinatesMode = 1;
+                    break;
+                case 2: //MAP_PLANAR
+                    babylonTexture.coordinatesMode = 2;
+                    break;
+                default:
+                    babylonTexture.coordinatesMode = 0;
+                    break;
+            }
+
+            babylonTexture.coordinatesIndex = uvGen.MapChannel - 1;
+            if (uvGen.MapChannel > 2)
+            {
+                RaiseWarning(string.Format("Unsupported map channel, Only channel 1 and 2 are supported."));
+            }
+
+            babylonTexture.uOffset = uvGen.GetUOffs(0);
+            babylonTexture.vOffset = uvGen.GetVOffs(0);
+
+            babylonTexture.uScale = uvGen.GetUScl(0);
+            babylonTexture.vScale = uvGen.GetVScl(0);
+
+            babylonTexture.uAng = uvGen.GetUAng(0);
+            babylonTexture.vAng = uvGen.GetVAng(0);
+            babylonTexture.wAng = uvGen.GetWAng(0);
+
+            babylonTexture.wrapU = 0; // CLAMP
+            if ((uvGen.TextureTiling & 1) != 0) // WRAP
+            {
+                babylonTexture.wrapU = 1;
+            }
+            else if ((uvGen.TextureTiling & 4) != 0) // MIRROR
+            {
+                babylonTexture.wrapU = 2;
+            }
+
+            babylonTexture.wrapV = 0; // CLAMP
+            if ((uvGen.TextureTiling & 2) != 0) // WRAP
+            {
+                babylonTexture.wrapV = 1;
+            }
+            else if ((uvGen.TextureTiling & 8) != 0) // MIRROR
+            {
+                babylonTexture.wrapV = 2;
+            }
+
+            babylonTexture.name = Path.GetFileName(texture.MapName);
+
+            try
+            {
+                if (File.Exists(texture.MapName))
+                {
+                    File.Copy(texture.MapName, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true);
+                }
+                else
+                {
+                    var texturepath = Path.Combine(Path.GetDirectoryName(Loader.Core.CurFilePath), babylonTexture.name);
+                    if (File.Exists(texturepath))
+                    {
+                        File.Copy(texturepath, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true);
+                    }
+                    else
+                    {
+                        RaiseWarning(string.Format("Texture {0} not found.", babylonTexture.name), true);
+                    }
+                }
+            }
+            catch
+            {
+                RaiseWarning(string.Format("Unable to copy {0} to output folder.", babylonTexture.name), true);
+            }
+
+            return babylonTexture;
+        }
+    }
+}

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

@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization.Json;
+using Autodesk.Max;
+using BabylonExport.Entities;
+using MaxSharp;
+using Animatable = MaxSharp.Animatable;
+
+namespace Max2Babylon
+{
+    internal partial class BabylonExporter
+    {
+        public event Action<int> OnImportProgressChanged;
+        public event Action<string, bool> OnWarning;
+        public event Action<string, bool, bool, bool> OnMessage;
+        public event Action<string, bool> OnError;
+
+        readonly List<string> alreadyExportedTextures = new List<string>();
+
+        void ReportProgressChanged(int progress)
+        {
+            if (OnImportProgressChanged != null)
+            {
+                OnImportProgressChanged(progress);
+            }
+        }
+
+        void RaiseError(string error, bool asChild = false)
+        {
+            if (OnError != null)
+            {
+                OnError(error, asChild);
+            }
+        }
+
+        void RaiseWarning(string warning, bool asChild = false)
+        {
+            if (OnWarning != null)
+            {
+                OnWarning(warning, asChild);
+            }
+        }
+
+        void RaiseMessage(string message, bool asChild = false, bool emphasis = false, bool embed = false)
+        {
+            if (OnMessage != null)
+            {
+                OnMessage(message, asChild, emphasis, embed);
+            }
+        }
+
+        public void Export(string outputFile)
+        {
+            RaiseMessage("Exportation started");
+            ReportProgressChanged(25);
+            var babylonScene = new BabylonScene(Path.GetDirectoryName(outputFile));
+            var maxScene = Kernel.Scene;
+            alreadyExportedTextures.Clear();
+
+            if (!Directory.Exists(babylonScene.OutputPath))
+            {
+                RaiseError("Exportation stopped: Output folder does not exist");
+                ReportProgressChanged(100);
+                return;
+            }
+
+            // Global
+            babylonScene.autoClear = true;
+            babylonScene.clearColor = Loader.Core.GetBackGround(0, Interval.Forever._IInterval).ToArray();
+            babylonScene.ambientColor = Loader.Core.GetAmbient(0, Interval.Forever._IInterval).ToArray();
+
+            babylonScene.gravity = maxScene.RootNode._Node.GetVector3Property("babylonjs_gravity");
+
+            // Cameras
+            BabylonCamera mainCamera = null;
+
+            RaiseMessage("Exporting cameras");
+            foreach (var cameraNode in maxScene.NodesListBySuperClass(SuperClassID.Camera))
+            {
+                var babylonCamera = ExportCamera(cameraNode, babylonScene);
+
+                if (mainCamera == null)
+                {
+                    mainCamera = babylonCamera;
+                    babylonScene.activeCameraID = mainCamera.id;
+                    RaiseMessage("Active camera set to " + mainCamera.name, true, true);
+                }
+            }
+
+            if (mainCamera == null)
+            {
+                RaiseWarning("No camera defined", true);
+            }
+
+            // Fog
+            for (var index = 0; index < Loader.Core.NumAtmospheric; index++)
+            {
+                var atmospheric = Loader.Core.GetAtmospheric(index);
+
+                if (atmospheric.Active(0) && atmospheric.ClassName == "Fog")
+                {
+                    RaiseMessage("Exporting fog");
+                    var reference = atmospheric.GetReference(0);
+                    var parameters = Animatable.CreateWrapper<ParameterBlock1>(reference);
+
+                    babylonScene.fogColor = (parameters["fog color"].Value as IColor).ToArray();
+                    babylonScene.fogDensity = (float)parameters["density"].Value;
+                    babylonScene.fogMode = ((int)parameters["fog type"].Value) == 0 ? 3 : 1;
+
+                    if (mainCamera != null)
+                    {
+                        babylonScene.fogStart = mainCamera.minZ * (float)parameters["near %"].Value;
+                        babylonScene.fogEnd = mainCamera.maxZ * (float)parameters["far %"].Value;
+                    }
+                }
+            }
+
+            // Meshes
+            RaiseMessage("Exporting meshes");
+            foreach (var meshNode in maxScene.NodesListBySuperClass(SuperClassID.GeometricObject))
+            {
+                ExportMesh(meshNode, babylonScene);
+            }
+
+            // Materials
+            RaiseMessage("Exporting materials");
+            var matsToExport = referencedMaterials.ToArray(); // Snapshot because multimaterials can export new materials
+            foreach (var mat in matsToExport)
+            {
+                ExportMaterial(mat, babylonScene);
+            }
+
+            // Lights
+            RaiseMessage("Exporting lights");
+            foreach (var lightNode in maxScene.NodesListBySuperClass(SuperClassID.Light))
+            {
+                ExportLight(lightNode, babylonScene);
+            }
+
+            // Output
+            babylonScene.Prepare(false);
+            using (var outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write))
+            {
+                var ser = new DataContractJsonSerializer(typeof(BabylonScene));
+                ser.WriteObject(outputStream, babylonScene);
+            }
+            ReportProgressChanged(100);
+
+            RaiseMessage("Exportation done");
+        }             
+    }
+}

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

@@ -0,0 +1,50 @@
+using Autodesk.Max;
+
+namespace Max2Babylon
+{
+    public struct GlobalVertex
+    {
+        const float epsilon = 0.001f;
+        public IPoint3 Position { get; set; }
+        public IPoint3 Normal { get; set; }
+        public IPoint2 UV { get; set; }
+        public IPoint2 UV2 { get; set; }
+
+        public override int GetHashCode()
+        {
+            return base.GetHashCode();
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is GlobalVertex))
+            {
+                return false;
+            }
+
+            var other = (GlobalVertex)obj;
+
+            if (!other.Position.IsAlmostEqualTo(Position, epsilon))
+            {
+                return false;
+            }
+
+            if (!other.Normal.IsAlmostEqualTo(Normal, epsilon))
+            {
+                return false;
+            }
+
+            if (UV != null && !other.UV.IsAlmostEqualTo(UV, epsilon))
+            {
+                return false;
+            }
+
+            if (UV2 != null && !other.UV2.IsAlmostEqualTo(UV2, epsilon))
+            {
+                return false;
+            }
+
+            return true;
+        }
+    }
+}

+ 228 - 0
Exporters/3ds Max/Max2Babylon/Forms/CameraPropertiesForm.Designer.cs

@@ -0,0 +1,228 @@
+namespace Max2Babylon
+{
+    partial class CameraPropertiesForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.chkGravity = new System.Windows.Forms.CheckBox();
+            this.chkCollisions = new System.Windows.Forms.CheckBox();
+            this.butOK = new System.Windows.Forms.Button();
+            this.butCancel = new System.Windows.Forms.Button();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.nupInertia = new System.Windows.Forms.NumericUpDown();
+            this.label1 = new System.Windows.Forms.Label();
+            this.nupSpeed = new System.Windows.Forms.NumericUpDown();
+            this.label3 = new System.Windows.Forms.Label();
+            this.ellipsoidControl = new Max2Babylon.Vector3Control();
+            this.groupBox1.SuspendLayout();
+            this.groupBox2.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupInertia)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupSpeed)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.ellipsoidControl);
+            this.groupBox1.Controls.Add(this.label3);
+            this.groupBox1.Controls.Add(this.chkGravity);
+            this.groupBox1.Controls.Add(this.chkCollisions);
+            this.groupBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox1.Location = new System.Drawing.Point(12, 12);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(319, 138);
+            this.groupBox1.TabIndex = 0;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Collisions";
+            // 
+            // chkGravity
+            // 
+            this.chkGravity.AutoSize = true;
+            this.chkGravity.Location = new System.Drawing.Point(21, 51);
+            this.chkGravity.Name = "chkGravity";
+            this.chkGravity.Size = new System.Drawing.Size(86, 17);
+            this.chkGravity.TabIndex = 1;
+            this.chkGravity.Text = "Apply gravity";
+            this.chkGravity.ThreeState = true;
+            this.chkGravity.UseVisualStyleBackColor = true;
+            // 
+            // chkCollisions
+            // 
+            this.chkCollisions.AutoSize = true;
+            this.chkCollisions.Location = new System.Drawing.Point(21, 28);
+            this.chkCollisions.Name = "chkCollisions";
+            this.chkCollisions.Size = new System.Drawing.Size(102, 17);
+            this.chkCollisions.TabIndex = 0;
+            this.chkCollisions.Text = "Check collisions";
+            this.chkCollisions.ThreeState = true;
+            this.chkCollisions.UseVisualStyleBackColor = true;
+            // 
+            // butOK
+            // 
+            this.butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.butOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+            this.butOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butOK.Location = new System.Drawing.Point(93, 304);
+            this.butOK.Name = "butOK";
+            this.butOK.Size = new System.Drawing.Size(75, 23);
+            this.butOK.TabIndex = 1;
+            this.butOK.Text = "OK";
+            this.butOK.UseVisualStyleBackColor = true;
+            this.butOK.Click += new System.EventHandler(this.butOK_Click);
+            // 
+            // butCancel
+            // 
+            this.butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butCancel.Location = new System.Drawing.Point(174, 304);
+            this.butCancel.Name = "butCancel";
+            this.butCancel.Size = new System.Drawing.Size(75, 23);
+            this.butCancel.TabIndex = 2;
+            this.butCancel.Text = "Cancel";
+            this.butCancel.UseVisualStyleBackColor = true;
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Controls.Add(this.label2);
+            this.groupBox2.Controls.Add(this.nupInertia);
+            this.groupBox2.Controls.Add(this.label1);
+            this.groupBox2.Controls.Add(this.nupSpeed);
+            this.groupBox2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox2.Location = new System.Drawing.Point(12, 156);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(319, 140);
+            this.groupBox2.TabIndex = 3;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "Control";
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(21, 81);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(36, 13);
+            this.label2.TabIndex = 3;
+            this.label2.Text = "Inertia";
+            // 
+            // nupInertia
+            // 
+            this.nupInertia.DecimalPlaces = 2;
+            this.nupInertia.Location = new System.Drawing.Point(24, 103);
+            this.nupInertia.Name = "nupInertia";
+            this.nupInertia.Size = new System.Drawing.Size(120, 20);
+            this.nupInertia.TabIndex = 2;
+            this.nupInertia.Value = new decimal(new int[] {
+            9,
+            0,
+            0,
+            65536});
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(21, 28);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(41, 13);
+            this.label1.TabIndex = 1;
+            this.label1.Text = "Speed:";
+            // 
+            // nupSpeed
+            // 
+            this.nupSpeed.DecimalPlaces = 1;
+            this.nupSpeed.Location = new System.Drawing.Point(24, 50);
+            this.nupSpeed.Name = "nupSpeed";
+            this.nupSpeed.Size = new System.Drawing.Size(120, 20);
+            this.nupSpeed.TabIndex = 0;
+            this.nupSpeed.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(18, 79);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(48, 13);
+            this.label3.TabIndex = 2;
+            this.label3.Text = "Ellipsoid:";
+            // 
+            // ellipsoidControl
+            // 
+            this.ellipsoidControl.Location = new System.Drawing.Point(21, 95);
+            this.ellipsoidControl.Name = "ellipsoidControl";
+            this.ellipsoidControl.Size = new System.Drawing.Size(294, 28);
+            this.ellipsoidControl.TabIndex = 3;
+            this.ellipsoidControl.X = 0F;
+            this.ellipsoidControl.Y = 0F;
+            this.ellipsoidControl.Z = 0F;
+            // 
+            // CameraPropertiesForm
+            // 
+            this.AcceptButton = this.butOK;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.butCancel;
+            this.ClientSize = new System.Drawing.Size(343, 339);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.butCancel);
+            this.Controls.Add(this.butOK);
+            this.Controls.Add(this.groupBox1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
+            this.Name = "CameraPropertiesForm";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "Babylon.js - Camera Properties";
+            this.Load += new System.EventHandler(this.CameraPropertiesForm_Load);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.groupBox2.ResumeLayout(false);
+            this.groupBox2.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupInertia)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupSpeed)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.Button butOK;
+        private System.Windows.Forms.Button butCancel;
+        private System.Windows.Forms.CheckBox chkCollisions;
+        private System.Windows.Forms.CheckBox chkGravity;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.NumericUpDown nupSpeed;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.NumericUpDown nupInertia;
+        private System.Windows.Forms.Label label3;
+        private Vector3Control ellipsoidControl;
+    }
+}

+ 49 - 0
Exporters/3ds Max/Max2Babylon/Forms/CameraPropertiesForm.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using Autodesk.Max;
+
+namespace Max2Babylon
+{
+    public partial class CameraPropertiesForm : Form
+    {
+        private readonly List<IINode> cameras = new List<IINode>();
+
+        public CameraPropertiesForm()
+        {
+            InitializeComponent();
+        }
+
+        private void CameraPropertiesForm_Load(object sender, EventArgs e)
+        {
+            for (var index = 0; index < Loader.Core.SelNodeCount; index++)
+            {
+                var node = Loader.Core.GetSelNode(index);
+
+                if (node.ObjectRef != null && node.ObjectRef.SuperClassID == SClass_ID.Camera)
+                {
+                    cameras.Add(node);
+                }
+            }
+
+            Tools.PrepareCheckBox(chkCollisions, cameras, "babylonjs_checkcollisions");
+            Tools.PrepareCheckBox(chkGravity, cameras, "babylonjs_applygravity");
+
+            Tools.PrepareNumericUpDown(nupSpeed, cameras, "babylonjs_speed", 1.0f);
+            Tools.PrepareNumericUpDown(nupInertia, cameras, "babylonjs_inertia", 0.9f);
+
+            Tools.PrepareVector3Control(ellipsoidControl, cameras[0], "babylonjs_ellipsoid", 0.5f, 1.0f, 0.5f);
+        }
+
+        private void butOK_Click(object sender, EventArgs e)
+        {
+            Tools.UpdateCheckBox(chkCollisions, cameras, "babylonjs_checkcollisions");
+            Tools.UpdateCheckBox(chkGravity, cameras, "babylonjs_applygravity");
+
+            Tools.UpdateNumericUpDown(nupSpeed, cameras, "babylonjs_speed");
+            Tools.UpdateNumericUpDown(nupInertia, cameras, "babylonjs_inertia");
+
+            Tools.UpdateVector3Control(ellipsoidControl, cameras, "babylonjs_ellipsoid");
+        }
+    }
+}

+ 120 - 0
Exporters/3ds Max/Max2Babylon/Forms/CameraPropertiesForm.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>

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

@@ -0,0 +1,143 @@
+namespace Max2Babylon
+{
+    partial class ExporterForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.butExport = new System.Windows.Forms.Button();
+            this.label1 = new System.Windows.Forms.Label();
+            this.txtFilename = new System.Windows.Forms.TextBox();
+            this.butBrowse = new System.Windows.Forms.Button();
+            this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
+            this.progressBar = new System.Windows.Forms.ProgressBar();
+            this.treeView = new System.Windows.Forms.TreeView();
+            this.SuspendLayout();
+            // 
+            // butExport
+            // 
+            this.butExport.Anchor = System.Windows.Forms.AnchorStyles.Top;
+            this.butExport.Enabled = false;
+            this.butExport.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butExport.Location = new System.Drawing.Point(321, 50);
+            this.butExport.Name = "butExport";
+            this.butExport.Size = new System.Drawing.Size(80, 23);
+            this.butExport.TabIndex = 0;
+            this.butExport.Text = "Export";
+            this.butExport.UseVisualStyleBackColor = true;
+            this.butExport.Click += new System.EventHandler(this.butExport_Click);
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(12, 9);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(52, 13);
+            this.label1.TabIndex = 2;
+            this.label1.Text = "Filename:";
+            // 
+            // txtFilename
+            // 
+            this.txtFilename.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtFilename.Location = new System.Drawing.Point(12, 25);
+            this.txtFilename.Name = "txtFilename";
+            this.txtFilename.Size = new System.Drawing.Size(650, 20);
+            this.txtFilename.TabIndex = 3;
+            this.txtFilename.TextChanged += new System.EventHandler(this.txtFilename_TextChanged);
+            // 
+            // butBrowse
+            // 
+            this.butBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+            this.butBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butBrowse.Location = new System.Drawing.Point(668, 22);
+            this.butBrowse.Name = "butBrowse";
+            this.butBrowse.Size = new System.Drawing.Size(43, 23);
+            this.butBrowse.TabIndex = 4;
+            this.butBrowse.Text = "...";
+            this.butBrowse.UseVisualStyleBackColor = true;
+            this.butBrowse.Click += new System.EventHandler(this.butBrowse_Click);
+            // 
+            // saveFileDialog
+            // 
+            this.saveFileDialog.DefaultExt = "babylon";
+            this.saveFileDialog.Filter = "Babylon files|*.babylon";
+            this.saveFileDialog.RestoreDirectory = true;
+            // 
+            // progressBar
+            // 
+            this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.progressBar.Location = new System.Drawing.Point(12, 486);
+            this.progressBar.Name = "progressBar";
+            this.progressBar.Size = new System.Drawing.Size(699, 23);
+            this.progressBar.TabIndex = 5;
+            // 
+            // treeView
+            // 
+            this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.treeView.Location = new System.Drawing.Point(12, 80);
+            this.treeView.Name = "treeView";
+            this.treeView.Size = new System.Drawing.Size(699, 401);
+            this.treeView.TabIndex = 6;
+            // 
+            // ExporterForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(723, 525);
+            this.Controls.Add(this.treeView);
+            this.Controls.Add(this.progressBar);
+            this.Controls.Add(this.butExport);
+            this.Controls.Add(this.butBrowse);
+            this.Controls.Add(this.txtFilename);
+            this.Controls.Add(this.label1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
+            this.MinimumSize = new System.Drawing.Size(500, 300);
+            this.Name = "ExporterForm";
+            this.ShowInTaskbar = true;
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "Babylon.js - Export scene to .babylon file";
+            this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ExporterForm_FormClosed);
+            this.Load += new System.EventHandler(this.ExporterForm_Load);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Button butExport;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.TextBox txtFilename;
+        private System.Windows.Forms.Button butBrowse;
+        private System.Windows.Forms.SaveFileDialog saveFileDialog;
+        private System.Windows.Forms.ProgressBar progressBar;
+        private System.Windows.Forms.TreeView treeView;
+    }
+}

+ 117 - 0
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.cs

@@ -0,0 +1,117 @@
+using System;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using MaxCustomControls;
+using MaxSharp;
+using Color = System.Drawing.Color;
+
+namespace Max2Babylon
+{
+    public partial class ExporterForm : MaxForm
+    {
+        private readonly BabylonExportActionItem babylonExportAction;
+
+        public ExporterForm(BabylonExportActionItem babylonExportAction)
+        {
+            InitializeComponent();
+
+            this.babylonExportAction = babylonExportAction;
+        }
+
+        private void ExporterForm_Load(object sender, EventArgs e)
+        {
+            txtFilename.Text = Kernel.Scene.RootNode.GetLocalData();
+        }
+
+        private void butBrowse_Click(object sender, EventArgs e)
+        {
+            if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
+            {
+                txtFilename.Text = saveFileDialog.FileName;
+            }
+        }
+
+        private async void butExport_Click(object sender, EventArgs e)
+        {
+            Kernel.Scene.RootNode.SetLocalData(txtFilename.Text);
+
+            var exporter = new BabylonExporter();
+            TreeNode currentNode = null;
+            TreeNode previousNode = null;
+
+            treeView.Nodes.Clear();
+
+            exporter.OnImportProgressChanged += progress => Invoke(new Action(() =>
+            {
+                progressBar.Value = progress;
+            }));
+
+            exporter.OnWarning += (warning, asChild) => Invoke(new Action(() =>
+            {
+                previousNode = new TreeNode(warning) { ForeColor = Color.Orange };
+
+                currentNode = CreateTreeNode(asChild, currentNode, previousNode);
+
+                previousNode.EnsureVisible();
+            }));
+
+            exporter.OnError += (error, asChild) => Invoke(new Action(() =>
+            {
+                previousNode = new TreeNode(error) { ForeColor = Color.Red };
+
+                currentNode = CreateTreeNode(asChild, currentNode, previousNode);
+
+                previousNode.EnsureVisible();
+            }));
+
+            exporter.OnMessage += (message, asChild, emphasis, embed) => Invoke(new Action(() =>
+            {
+                var oldPrevious = previousNode;
+
+                previousNode = new TreeNode(message);
+
+                if (emphasis)
+                {
+                    previousNode.ForeColor = Color.Green;
+                }
+
+                currentNode = CreateTreeNode(asChild || embed, embed ? oldPrevious : currentNode, previousNode);
+
+                if (emphasis)
+                {
+                    previousNode.EnsureVisible();
+                }
+            }));
+
+            await Task.Run(() => exporter.Export(txtFilename.Text));
+        }
+
+        private TreeNode CreateTreeNode(bool asChild, TreeNode currentNode, TreeNode treeNode)
+        {
+            if (asChild)
+            {
+                currentNode.Nodes.Add(treeNode);
+            }
+            else
+            {
+                treeView.Nodes.Add(treeNode);
+            }
+
+            if (!asChild)
+            {
+                currentNode = treeNode;
+            }
+            return currentNode;
+        }
+
+        private void ExporterForm_FormClosed(object sender, FormClosedEventArgs e)
+        {
+            babylonExportAction.Close();
+        }
+
+        private void txtFilename_TextChanged(object sender, EventArgs e)
+        {
+            butExport.Enabled = !string.IsNullOrEmpty(txtFilename.Text.Trim());
+        }
+    }
+}

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

@@ -0,0 +1,123 @@
+<?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>
+  <metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>

+ 143 - 0
Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.Designer.cs

@@ -0,0 +1,143 @@
+namespace Max2Babylon
+{
+    partial class ObjectPropertiesForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.chkCollisions = new System.Windows.Forms.CheckBox();
+            this.butCancel = new System.Windows.Forms.Button();
+            this.butOK = new System.Windows.Forms.Button();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.chkPickable = new System.Windows.Forms.CheckBox();
+            this.groupBox1.SuspendLayout();
+            this.groupBox2.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.chkCollisions);
+            this.groupBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox1.Location = new System.Drawing.Point(12, 12);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(319, 59);
+            this.groupBox1.TabIndex = 1;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Collisions";
+            // 
+            // chkCollisions
+            // 
+            this.chkCollisions.AutoSize = true;
+            this.chkCollisions.Location = new System.Drawing.Point(21, 28);
+            this.chkCollisions.Name = "chkCollisions";
+            this.chkCollisions.Size = new System.Drawing.Size(102, 17);
+            this.chkCollisions.TabIndex = 0;
+            this.chkCollisions.Text = "Check collisions";
+            this.chkCollisions.ThreeState = true;
+            this.chkCollisions.UseVisualStyleBackColor = true;
+            // 
+            // butCancel
+            // 
+            this.butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butCancel.Location = new System.Drawing.Point(174, 226);
+            this.butCancel.Name = "butCancel";
+            this.butCancel.Size = new System.Drawing.Size(75, 23);
+            this.butCancel.TabIndex = 6;
+            this.butCancel.Text = "Cancel";
+            this.butCancel.UseVisualStyleBackColor = true;
+            // 
+            // butOK
+            // 
+            this.butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.butOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+            this.butOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butOK.Location = new System.Drawing.Point(93, 226);
+            this.butOK.Name = "butOK";
+            this.butOK.Size = new System.Drawing.Size(75, 23);
+            this.butOK.TabIndex = 5;
+            this.butOK.Text = "OK";
+            this.butOK.UseVisualStyleBackColor = true;
+            this.butOK.Click += new System.EventHandler(this.butOK_Click);
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Controls.Add(this.chkPickable);
+            this.groupBox2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox2.Location = new System.Drawing.Point(12, 77);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(319, 59);
+            this.groupBox2.TabIndex = 2;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "Misc.";
+            // 
+            // chkPickable
+            // 
+            this.chkPickable.AutoSize = true;
+            this.chkPickable.Location = new System.Drawing.Point(21, 28);
+            this.chkPickable.Name = "chkPickable";
+            this.chkPickable.Size = new System.Drawing.Size(67, 17);
+            this.chkPickable.TabIndex = 0;
+            this.chkPickable.Text = "Pickable";
+            this.chkPickable.ThreeState = true;
+            this.chkPickable.UseVisualStyleBackColor = true;
+            // 
+            // ObjectPropertiesForm
+            // 
+            this.AcceptButton = this.butOK;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.butCancel;
+            this.ClientSize = new System.Drawing.Size(343, 261);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.butCancel);
+            this.Controls.Add(this.butOK);
+            this.Controls.Add(this.groupBox1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
+            this.Name = "ObjectPropertiesForm";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "Babylon.js - Object Properties";
+            this.Load += new System.EventHandler(this.ObjectPropertiesForm_Load);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.groupBox2.ResumeLayout(false);
+            this.groupBox2.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.CheckBox chkCollisions;
+        private System.Windows.Forms.Button butCancel;
+        private System.Windows.Forms.Button butOK;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.CheckBox chkPickable;
+    }
+}

+ 39 - 0
Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using Autodesk.Max;
+
+namespace Max2Babylon
+{
+    public partial class ObjectPropertiesForm : Form
+    {
+        private readonly List<IINode> objects = new List<IINode>();
+
+        public ObjectPropertiesForm()
+        {
+            InitializeComponent();
+        }
+
+        private void butOK_Click(object sender, EventArgs e)
+        {
+            Tools.UpdateCheckBox(chkCollisions, objects, "babylonjs_checkcollisions");
+            Tools.UpdateCheckBox(chkPickable, objects, "babylonjs_checkpickable");            
+        }
+
+        private void ObjectPropertiesForm_Load(object sender, EventArgs e)
+        {
+            for (var index = 0; index < Loader.Core.SelNodeCount; index++)
+            {
+                var node = Loader.Core.GetSelNode(index);
+
+                if (node.ObjectRef != null && node.ObjectRef.SuperClassID == SClass_ID.Geomobject)
+                {
+                    objects.Add(node);
+                }
+            }
+
+            Tools.PrepareCheckBox(chkCollisions, objects, "babylonjs_checkcollisions");
+            Tools.PrepareCheckBox(chkPickable, objects, "babylonjs_checkpickable");
+        }
+    }
+}

+ 120 - 0
Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.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>

+ 124 - 0
Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.Designer.cs

@@ -0,0 +1,124 @@
+namespace Max2Babylon
+{
+    partial class ScenePropertiesForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.label3 = new System.Windows.Forms.Label();
+            this.butCancel = new System.Windows.Forms.Button();
+            this.butOK = new System.Windows.Forms.Button();
+            this.gravityControl = new Max2Babylon.Vector3Control();
+            this.groupBox1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.gravityControl);
+            this.groupBox1.Controls.Add(this.label3);
+            this.groupBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox1.Location = new System.Drawing.Point(12, 12);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(319, 87);
+            this.groupBox1.TabIndex = 0;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Collisions";
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(18, 28);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(43, 13);
+            this.label3.TabIndex = 4;
+            this.label3.Text = "Gravity:";
+            // 
+            // butCancel
+            // 
+            this.butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butCancel.Location = new System.Drawing.Point(174, 355);
+            this.butCancel.Name = "butCancel";
+            this.butCancel.Size = new System.Drawing.Size(75, 23);
+            this.butCancel.TabIndex = 4;
+            this.butCancel.Text = "Cancel";
+            this.butCancel.UseVisualStyleBackColor = true;
+            // 
+            // butOK
+            // 
+            this.butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+            this.butOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+            this.butOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.butOK.Location = new System.Drawing.Point(93, 355);
+            this.butOK.Name = "butOK";
+            this.butOK.Size = new System.Drawing.Size(75, 23);
+            this.butOK.TabIndex = 3;
+            this.butOK.Text = "OK";
+            this.butOK.UseVisualStyleBackColor = true;
+            this.butOK.Click += new System.EventHandler(this.butOK_Click);
+            // 
+            // gravityControl
+            // 
+            this.gravityControl.Location = new System.Drawing.Point(21, 44);
+            this.gravityControl.Name = "gravityControl";
+            this.gravityControl.Size = new System.Drawing.Size(294, 28);
+            this.gravityControl.TabIndex = 5;
+            this.gravityControl.X = 0F;
+            this.gravityControl.Y = 0F;
+            this.gravityControl.Z = 0F;
+            // 
+            // ScenePropertiesForm
+            // 
+            this.AcceptButton = this.butOK;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.butCancel;
+            this.ClientSize = new System.Drawing.Size(343, 390);
+            this.Controls.Add(this.butCancel);
+            this.Controls.Add(this.butOK);
+            this.Controls.Add(this.groupBox1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
+            this.Name = "ScenePropertiesForm";
+            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+            this.Text = "Babylon.js - Scene Properties";
+            this.Load += new System.EventHandler(this.ScenePropertiesForm_Load);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox groupBox1;
+        private Vector3Control gravityControl;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.Button butCancel;
+        private System.Windows.Forms.Button butOK;
+    }
+}

+ 24 - 0
Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Windows.Forms;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    public partial class ScenePropertiesForm : Form
+    {
+        public ScenePropertiesForm()
+        {
+            InitializeComponent();
+        }
+
+        private void butOK_Click(object sender, EventArgs e)
+        {
+            Tools.UpdateVector3Control(gravityControl, Kernel.Scene.RootNode._Node, "babylonjs_gravity");
+        }
+
+        private void ScenePropertiesForm_Load(object sender, EventArgs e)
+        {
+            Tools.PrepareVector3Control(gravityControl, Kernel.Scene.RootNode._Node, "babylonjs_gravity", 0, -0.9f, 0);
+        }
+    }
+}

+ 120 - 0
Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.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>

+ 152 - 0
Exporters/3ds Max/Max2Babylon/Forms/Vector3Control.Designer.cs

@@ -0,0 +1,152 @@
+namespace Max2Babylon
+{
+    partial class Vector3Control
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.label1 = new System.Windows.Forms.Label();
+            this.nupX = new System.Windows.Forms.NumericUpDown();
+            this.nupY = new System.Windows.Forms.NumericUpDown();
+            this.label2 = new System.Windows.Forms.Label();
+            this.nupZ = new System.Windows.Forms.NumericUpDown();
+            this.label3 = new System.Windows.Forms.Label();
+            ((System.ComponentModel.ISupportInitialize)(this.nupX)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupY)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupZ)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(4, 6);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(20, 13);
+            this.label1.TabIndex = 0;
+            this.label1.Text = "X: ";
+            // 
+            // nupX
+            // 
+            this.nupX.DecimalPlaces = 2;
+            this.nupX.Location = new System.Drawing.Point(30, 4);
+            this.nupX.Maximum = new decimal(new int[] {
+            1000000,
+            0,
+            0,
+            0});
+            this.nupX.Minimum = new decimal(new int[] {
+            1000000,
+            0,
+            0,
+            -2147483648});
+            this.nupX.Name = "nupX";
+            this.nupX.Size = new System.Drawing.Size(57, 20);
+            this.nupX.TabIndex = 1;
+            // 
+            // nupY
+            // 
+            this.nupY.DecimalPlaces = 2;
+            this.nupY.Location = new System.Drawing.Point(123, 4);
+            this.nupY.Maximum = new decimal(new int[] {
+            1000000,
+            0,
+            0,
+            0});
+            this.nupY.Minimum = new decimal(new int[] {
+            1000000,
+            0,
+            0,
+            -2147483648});
+            this.nupY.Name = "nupY";
+            this.nupY.Size = new System.Drawing.Size(57, 20);
+            this.nupY.TabIndex = 3;
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(97, 6);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(17, 13);
+            this.label2.TabIndex = 2;
+            this.label2.Text = "Y:";
+            // 
+            // nupZ
+            // 
+            this.nupZ.DecimalPlaces = 2;
+            this.nupZ.Location = new System.Drawing.Point(216, 4);
+            this.nupZ.Maximum = new decimal(new int[] {
+            1000000,
+            0,
+            0,
+            0});
+            this.nupZ.Minimum = new decimal(new int[] {
+            1000000,
+            0,
+            0,
+            -2147483648});
+            this.nupZ.Name = "nupZ";
+            this.nupZ.Size = new System.Drawing.Size(57, 20);
+            this.nupZ.TabIndex = 5;
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(190, 6);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(17, 13);
+            this.label3.TabIndex = 4;
+            this.label3.Text = "Z:";
+            // 
+            // Vector3Control
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.nupZ);
+            this.Controls.Add(this.label3);
+            this.Controls.Add(this.nupY);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.nupX);
+            this.Controls.Add(this.label1);
+            this.Name = "Vector3Control";
+            this.Size = new System.Drawing.Size(294, 28);
+            ((System.ComponentModel.ISupportInitialize)(this.nupX)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupY)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupZ)).EndInit();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.NumericUpDown nupX;
+        private System.Windows.Forms.NumericUpDown nupY;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.NumericUpDown nupZ;
+        private System.Windows.Forms.Label label3;
+    }
+}

+ 30 - 0
Exporters/3ds Max/Max2Babylon/Forms/Vector3Control.cs

@@ -0,0 +1,30 @@
+using System.Windows.Forms;
+
+namespace Max2Babylon
+{
+    public partial class Vector3Control : UserControl
+    {
+        public Vector3Control()
+        {
+            InitializeComponent();
+        }
+
+        public float X
+        {
+            get { return (float)nupX.Value; }
+            set { nupX.Value = (decimal)value; }
+        }
+
+        public float Y
+        {
+            get { return (float)nupY.Value; }
+            set { nupY.Value = (decimal)value; }
+        }
+
+        public float Z
+        {
+            get { return (float)nupZ.Value; }
+            set { nupZ.Value = (decimal)value; }
+        }
+    }
+}

+ 120 - 0
Exporters/3ds Max/Max2Babylon/Forms/Vector3Control.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>

+ 117 - 0
Exporters/3ds Max/Max2Babylon/GlobalUtility.cs

@@ -0,0 +1,117 @@
+using Autodesk.Max;
+using Autodesk.Max.IQuadMenuContext;
+using Autodesk.Max.Plugins;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    class GlobalUtility : GUP
+    {
+        IIMenu menu;
+        IIMenuItem menuItem;
+        IIMenuItem menuItemBabylon;
+        uint idActionTable;
+        IActionTable actionTable;
+        IActionCallback actionCallback;
+
+        public override void Stop()
+        {
+            if (actionTable != null)
+            {
+                Loader.Global.COREInterface.ActionManager.DeactivateActionTable(actionCallback, idActionTable);
+            }
+
+            // Clean up menu
+            if (menu != null)
+            {
+                Loader.Global.COREInterface.MenuManager.UnRegisterMenu(menu);
+                Loader.Global.ReleaseIMenu(menu);
+                Loader.Global.ReleaseIMenuItem(menuItemBabylon);
+                Loader.Global.ReleaseIMenuItem(menuItem);
+
+                menu = null;
+                menuItem = null;
+            }
+        }
+
+        public override uint Start
+        {
+            get
+            {
+                IIActionManager actionManager = Loader.Core.ActionManager;
+                IIMenuManager menuManager = Loader.Core.MenuManager;
+
+                // Set up global actions
+                idActionTable = (uint)actionManager.NumActionTables;
+
+                string actionTableName = "Babylon Actions";
+                actionTable = Loader.Global.ActionTable.Create(idActionTable, 0, ref actionTableName);
+                actionTable.AppendOperation(new BabylonExportActionItem());
+                actionTable.AppendOperation(new BabylonPropertiesActionItem());
+                actionCallback = new BabylonActionCallback();
+
+                actionManager.RegisterActionTable(actionTable);
+                actionManager.ActivateActionTable(actionCallback as ActionCallback, idActionTable);
+
+                // Set up menu
+                menu = menuManager.FindMenu("Babylon");
+
+                if (menu != null)
+                {
+                    menuManager.UnRegisterMenu(menu);
+                    Loader.Global.ReleaseIMenu(menu);
+                    menu = null;
+                }
+
+                // Main menu
+                menu = Loader.Global.IMenu;
+                menu.Title = "Babylon";
+                menuManager.RegisterMenu(menu, 0);
+
+                // Launch option
+                menuItemBabylon = Loader.Global.IMenuItem;
+                menuItemBabylon.Title = "&File Exporter";
+                menuItemBabylon.ActionItem = actionTable[0];
+
+                menu.AddItem(menuItemBabylon, -1);
+
+                menuItem = Loader.Global.IMenuItem;
+                menuItem.SubMenu = menu;
+
+                menuManager.MainMenuBar.AddItem(menuItem, -1);
+
+                // Quad
+                var rootQuadMenu = menuManager.GetViewportRightClickMenu(RightClickContext.NonePressed);
+                var quadMenu = rootQuadMenu.GetMenu(0);
+
+                menu = menuManager.FindMenu("Babylon...");
+
+                if (menu != null)
+                {
+                    menuManager.UnRegisterMenu(menu);
+                    Loader.Global.ReleaseIMenu(menu);
+                    menu = null;
+                }
+
+                menu = Loader.Global.IMenu;
+                menu.Title = "Babylon...";
+                menuManager.RegisterMenu(menu, 0);
+
+                menuItemBabylon = Loader.Global.IMenuItem;
+                menuItemBabylon.Title = "Babylon Properties";
+                menuItemBabylon.ActionItem = actionTable[1];
+
+                menu.AddItem(menuItemBabylon, -1);
+
+                menuItem = Loader.Global.IMenuItem;
+                menuItem.SubMenu = menu;
+
+                quadMenu.AddItem(menuItem, -1);
+
+                Loader.Global.COREInterface.MenuManager.UpdateMenuBar();
+
+                return 0;
+            }
+        }
+    }
+}

+ 29 - 0
Exporters/3ds Max/Max2Babylon/Loader.cs

@@ -0,0 +1,29 @@
+using System;
+using System.ComponentModel;
+using Autodesk.Max;
+using Autodesk.Max.Plugins;
+
+namespace Max2Babylon
+{
+    public class Loader
+    {
+        public static IGlobal Global;
+        public static IInterface14 Core;
+        public static IClass_ID Class_ID;
+
+        public static void AssemblyMain()
+        {
+            Global = GlobalInterface.Instance;
+            Core = Global.COREInterface14;
+            Class_ID = Global.Class_ID.Create(0x8217f123, 0xef980456);
+            Core.AddClass(new Descriptor());
+        }
+
+        public static void AssemblyShutdown()
+        {
+
+        }
+
+     
+    }
+}

+ 159 - 0
Exporters/3ds Max/Max2Babylon/Max2Babylon.csproj

@@ -0,0 +1,159 @@
+<?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>{DD7C931A-8FAF-4318-BB74-71DC858CC489}</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>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>C:\Program Files\Autodesk\3ds Max 2013\bin\assemblies\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Autodesk.Max">
+      <HintPath>Refs\Autodesk.Max.dll</HintPath>
+    </Reference>
+    <Reference Include="MaxCustomControls">
+      <HintPath>Refs\MaxCustomControls.dll</HintPath>
+    </Reference>
+    <Reference Include="MaxSharp">
+      <HintPath>Refs\MaxSharp.dll</HintPath>
+    </Reference>
+    <Reference Include="SharpDX">
+      <HintPath>Refs\SharpDX.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Runtime.Serialization" />
+    <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" />
+    <Compile Include="BabylonPropertiesActionItem.cs" />
+    <Compile Include="Exporter\BabylonExporter.ShadowGenerator.cs" />
+    <Compile Include="Forms\CameraPropertiesForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\CameraPropertiesForm.Designer.cs">
+      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Descriptor.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Forms\ExporterForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\ExporterForm.Designer.cs">
+      <DependentUpon>ExporterForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Exporter\BabylonExporter.Texture.cs" />
+    <Compile Include="Exporter\BabylonExporter.Material.cs" />
+    <Compile Include="Exporter\BabylonExporter.Light.cs" />
+    <Compile Include="Exporter\BabylonExporter.cs" />
+    <Compile Include="Exporter\BabylonExporter.Camera.cs" />
+    <Compile Include="Exporter\BabylonExporter.Mesh.cs" />
+    <Compile Include="Exporter\GlobalVertex.cs" />
+    <Compile Include="GlobalUtility.cs" />
+    <Compile Include="Loader.cs" />
+    <Compile Include="BabylonExportActionItem.cs" />
+    <Compile Include="Forms\ObjectPropertiesForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\ObjectPropertiesForm.Designer.cs">
+      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Forms\ScenePropertiesForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\ScenePropertiesForm.Designer.cs">
+      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Tools.cs" />
+    <Compile Include="Forms\Vector3Control.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Forms\Vector3Control.Designer.cs">
+      <DependentUpon>Vector3Control.cs</DependentUpon>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Refs\Autodesk.Max.dll" />
+    <Content Include="Refs\ManagedServices.dll" />
+    <Content Include="Refs\MaxCustomControls.dll" />
+    <Content Include="Refs\MaxSharp.dll" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Forms\CameraPropertiesForm.resx">
+      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\ExporterForm.resx">
+      <DependentUpon>ExporterForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\ObjectPropertiesForm.resx">
+      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\ScenePropertiesForm.resx">
+      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Forms\Vector3Control.resx">
+      <DependentUpon>Vector3Control.cs</DependentUpon>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\BabylonExport.Entities\BabylonExport.Entities.csproj">
+      <Project>{6150965a-658c-4263-89ad-4f980eb0675d}</Project>
+      <Name>BabylonExport.Entities</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- 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/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/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/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>

BIN
Exporters/3ds Max/Max2Babylon/Refs/Autodesk.Max.dll


BIN
Exporters/3ds Max/Max2Babylon/Refs/ManagedServices.dll


BIN
Exporters/3ds Max/Max2Babylon/Refs/MaxCustomControls.dll


BIN
Exporters/3ds Max/Max2Babylon/Refs/MaxSharp.dll


BIN
Exporters/3ds Max/Max2Babylon/Refs/SharpDX.dll


+ 287 - 0
Exporters/3ds Max/Max2Babylon/Tools.cs

@@ -0,0 +1,287 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using Autodesk.Max;
+using Autodesk.Max.IMXSDebugger;
+using MaxSharp;
+
+namespace Max2Babylon
+{
+    public static class Tools
+    {
+        public static float[] ToArray(this IQuat value)
+        {
+            return new[] { value.X, value.Z, value.Y, value.W };
+        }
+
+        public static float[] Scale(this Color value, float scale)
+        {
+            return new[] { value.r * scale, value.g * scale, value.b * scale };
+        }
+        public static float[] ToArray(this Color value)
+        {
+            return new[] { value.r, value.g, value.b };
+        }
+
+        public static float[] ToArray(this IPoint3 value)
+        {
+            return new[] { value.X, value.Y, value.Z };
+        }
+
+        public static float[] ToArray(this IPoint2 value)
+        {
+            return new[] { value.X, value.Y };
+        }
+
+        public static float[] ToArraySwitched(this IPoint3 value)
+        {
+            return new[] { value.X, value.Z, value.Y };
+        }
+
+        public static float[] ToArray(this IColor value)
+        {
+            return new[] { value.R, value.G, value.B };
+        }
+
+        public static IEnumerable<Node> NodesListBySuperClass(this Scene scene, SuperClassID sid)
+        {
+            return from n in scene.NodeTree where n.Object != null && n.Object.SuperClassID == sid select n;
+        }
+
+        public static float ConvertFov(float fov)
+        {
+            return (float)(2.0f * Math.Atan(Math.Tan(fov / 2.0f) / Loader.Core.ImageAspRatio));
+        }
+
+        public static bool HasParent(this Node node)
+        {
+            return node.Parent != null && node.Parent.Object != null;
+        }
+
+        public static Guid GetGuid(this Animatable node)
+        {
+            var appData = node.GetAppData(new ClassID(Loader.Class_ID), SuperClassID.BaseNode);
+
+            var uidData = appData.GetChunk(0);
+            Guid uid;
+
+            if (uidData != null)
+            {
+                uid = new Guid(uidData);
+            }
+            else
+            {
+                uid = Guid.NewGuid();
+                appData.AddChunk(0, uid.ToByteArray());
+            }
+
+            return uid;
+        }
+
+        public static Guid GetGuid(this IINode node)
+        {
+            return GetGuid(Animatable.CreateWrapper<Node>(node));
+        }
+
+        public static string GetLocalData(this Node node)
+        {
+            var appData = node.GetAppData(new ClassID(Loader.Class_ID), SuperClassID.BaseNode);
+
+            var uidData = appData.GetChunk(1);
+
+            if (uidData != null)
+            {
+                return System.Text.Encoding.UTF8.GetString(uidData);
+            }
+
+            return "";
+        }
+
+        public static void SetLocalData(this Node node, string value)
+        {
+            var appData = node.GetAppData(new ClassID(Loader.Class_ID), SuperClassID.BaseNode);
+
+            var uidData = appData.GetChunk(1);
+
+            if (uidData != null)
+            {
+                appData.RemoveChunk(1);
+            }
+
+            appData.AddChunk(1, System.Text.Encoding.UTF8.GetBytes(value));
+        }
+
+        public static IMatrix3 GetWorldMatrix(this Node node, TimeValue t, bool parent)
+        {
+            var innerNode = node._Node;
+
+            var tm = innerNode.GetNodeTM(t, Interval.Forever._IInterval);
+            var ptm = innerNode.ParentNode.GetNodeTM(t, Interval.Forever._IInterval);
+
+            if (!parent)
+                return tm;
+
+            if (innerNode.ParentNode.SuperClassID == SuperClassID.Camera)
+            {
+                var r = ptm.GetRow(3);
+                ptm.IdentityMatrix();
+                ptm.SetRow(3, r);
+            }
+
+            ptm.Invert();
+            return tm.Multiply(ptm);
+        }
+
+        public static IMesh GetMesh(this IObject obj)
+        {
+            if (obj.CanConvertToType(ClassID.TriObject._IClass_ID) == 0)
+                return null;
+
+            var tri = obj.ConvertToType(0, ClassID.TriObject._IClass_ID) as ITriObject;
+            return tri == null ? null : tri.Mesh;
+        }
+
+        public static bool IsAlmostEqualTo(this IPoint3 current, IPoint3 other, float epsilon)
+        {
+            if (Math.Abs(current.X - other.X) > epsilon)
+            {
+                return false;
+            }
+
+            if (Math.Abs(current.Y - other.Y) > epsilon)
+            {
+                return false;
+            }
+
+            if (Math.Abs(current.Z - other.Z) > epsilon)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        public static bool IsAlmostEqualTo(this IPoint2 current, IPoint2 other, float epsilon)
+        {
+            if (Math.Abs(current.X - other.X) > epsilon)
+            {
+                return false;
+            }
+
+            if (Math.Abs(current.Y - other.Y) > epsilon)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        public static bool GetBoolProperty(this IINode node, string propertyName, int defaultState = 0)
+        {
+            int state = defaultState;
+            node.GetUserPropBool(ref propertyName, ref state);
+
+            return state == 1;
+        }
+
+        public static float GetFloatProperty(this IINode node, string propertyName, float defaultState = 0)
+        {
+            float state = defaultState;
+            node.GetUserPropFloat(ref propertyName, ref state);
+
+            return state;
+        }
+
+        public static float[] GetVector3Property(this IINode node, string propertyName)
+        {
+            float state0 = 0;
+            string name = propertyName + "_x";
+            node.GetUserPropFloat(ref name, ref state0);
+
+            float state1 = 0;
+            name = propertyName + "_y";
+            node.GetUserPropFloat(ref name, ref state1);
+
+            float state2 = 0;
+            name = propertyName + "_z";
+            node.GetUserPropFloat(ref name, ref state2);
+
+            return new[] { state0, state1, state2 };
+        }
+
+        public static void PrepareCheckBox(CheckBox checkBox, List<IINode> nodes, string propertyName, int defaultState = 0)
+        {
+            checkBox.CheckState = CheckState.Indeterminate;
+            foreach (var node in nodes)
+            {
+                var state = node.GetBoolProperty(propertyName, defaultState);
+
+                if (checkBox.CheckState == CheckState.Indeterminate)
+                {
+                    checkBox.CheckState = state ? CheckState.Checked : CheckState.Unchecked;
+                }
+                else
+                {
+                    if (!state && checkBox.CheckState == CheckState.Checked ||
+                        state && checkBox.CheckState == CheckState.Unchecked)
+                    {
+                        checkBox.CheckState = CheckState.Indeterminate;
+                        break;
+                    }
+                }
+            }
+        }
+
+        public static void UpdateCheckBox(CheckBox checkBox, List<IINode> nodes, string propertyName)
+        {
+            foreach (var node in nodes)
+            {
+                if (checkBox.CheckState != CheckState.Indeterminate)
+                {
+                    node.SetUserPropBool(ref propertyName, checkBox.CheckState == CheckState.Checked);
+                }
+            }
+        }
+
+        public static void PrepareNumericUpDown(NumericUpDown nup, List<IINode> nodes, string propertyName, float defaultState = 0)
+        {
+            nup.Value = (decimal)nodes[0].GetFloatProperty(propertyName, defaultState);
+        }
+
+        public static void UpdateNumericUpDown(NumericUpDown nup, List<IINode> nodes, string propertyName)
+        {
+            foreach (var node in nodes)
+            {
+                node.SetUserPropFloat(ref propertyName, (float)nup.Value);
+            }
+        }
+
+        public static void PrepareVector3Control(Vector3Control vector3Control, IINode node, string propertyName, float defaultX = 0, float defaultY = 0, float defaultZ = 0)
+        {
+            vector3Control.X = node.GetFloatProperty(propertyName + "_x", defaultX);
+            vector3Control.Y = node.GetFloatProperty(propertyName + "_y", defaultY);
+            vector3Control.Z = node.GetFloatProperty(propertyName + "_z", defaultZ);
+        }
+
+        public static void UpdateVector3Control(Vector3Control vector3Control, IINode node, string propertyName)
+        {
+            string name = propertyName + "_x";
+            node.SetUserPropFloat(ref name, vector3Control.X);
+
+            name = propertyName + "_y";
+            node.SetUserPropFloat(ref name, vector3Control.Y);
+
+            name = propertyName + "_z";
+            node.SetUserPropFloat(ref name, vector3Control.Z);
+        }
+
+        public static void UpdateVector3Control(Vector3Control vector3Control, List<IINode> nodes, string propertyName)
+        {
+            foreach (var node in nodes)
+            {
+                UpdateVector3Control(vector3Control, node, propertyName);
+            }
+        }
+    }
+}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 135 - 0
Exporters/3ds Max/readme.md