فهرست منبع

Exporter 3ds Max > Add Node class to babylon entities | Add exporter to gltf format handling simple meshes and materials

noalak 8 سال پیش
والد
کامیت
84a726522b
41فایلهای تغییر یافته به همراه2074 افزوده شده و 388 حذف شده
  1. 1 25
      Exporters/3ds Max/BabylonExport.Entities/BabylonAbstractMesh.cs
  2. 1 25
      Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs
  3. 2 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj
  4. 1 6
      Exporters/3ds Max/BabylonExport.Entities/BabylonIAnimatable.cs
  5. 1 25
      Exporters/3ds Max/BabylonExport.Entities/BabylonLight.cs
  6. 32 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonNode.cs
  7. 15 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonQuaternion.cs
  8. 30 6
      Exporters/3ds Max/BabylonExport.Entities/BabylonTexture.cs
  9. 61 1
      Exporters/3ds Max/BabylonExport.Entities/BabylonVector3.cs
  10. 116 0
      Exporters/3ds Max/GltfExport.Entities/GLTF.cs
  11. 120 0
      Exporters/3ds Max/GltfExport.Entities/GLTFAccessor.cs
  12. 20 0
      Exporters/3ds Max/GltfExport.Entities/GLTFAsset.cs
  13. 14 0
      Exporters/3ds Max/GltfExport.Entities/GLTFBuffer.cs
  14. 22 0
      Exporters/3ds Max/GltfExport.Entities/GLTFBufferView.cs
  15. 11 0
      Exporters/3ds Max/GltfExport.Entities/GLTFChildRootProperty.cs
  16. 64 0
      Exporters/3ds Max/GltfExport.Entities/GLTFExport.Entities.csproj
  17. 17 0
      Exporters/3ds Max/GltfExport.Entities/GLTFImage.cs
  18. 10 0
      Exporters/3ds Max/GltfExport.Entities/GLTFIndexedChildRootProperty.cs
  19. 41 0
      Exporters/3ds Max/GltfExport.Entities/GLTFMaterial.cs
  20. 16 0
      Exporters/3ds Max/GltfExport.Entities/GLTFMesh.cs
  21. 47 0
      Exporters/3ds Max/GltfExport.Entities/GLTFMeshPrimitive.cs
  22. 53 0
      Exporters/3ds Max/GltfExport.Entities/GLTFNode.cs
  23. 23 0
      Exporters/3ds Max/GltfExport.Entities/GLTFPBRMetallicRoughness.cs
  24. 14 0
      Exporters/3ds Max/GltfExport.Entities/GLTFProperty.cs
  25. 43 0
      Exporters/3ds Max/GltfExport.Entities/GLTFSampler.cs
  26. 28 0
      Exporters/3ds Max/GltfExport.Entities/GLTFScene.cs
  27. 14 0
      Exporters/3ds Max/GltfExport.Entities/GLTFTexture.cs
  28. 14 0
      Exporters/3ds Max/GltfExport.Entities/GLTFTextureInfo.cs
  29. 36 0
      Exporters/3ds Max/GltfExport.Entities/Properties/AssemblyInfo.cs
  30. 43 85
      Exporters/3ds Max/Max2Babylon.sln
  31. 151 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Material.cs
  32. 427 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Mesh.cs
  33. 180 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Texture.cs
  34. 145 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.cs
  35. 13 0
      Exporters/3ds Max/Max2Babylon/2017/Exporter/GLTFGlobalVertex.cs
  36. 9 0
      Exporters/3ds Max/Max2Babylon/2017/Max2Babylon2017.csproj
  37. 6 6
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Texture.cs
  38. 12 9
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs
  39. 20 5
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.Designer.cs
  40. 9 2
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.cs
  41. 192 193
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx

+ 1 - 25
Exporters/3ds Max/BabylonExport.Entities/BabylonAbstractMesh.cs

@@ -3,15 +3,9 @@
 namespace BabylonExport.Entities
 {
     [DataContract]
-    public class BabylonAbstractMesh: BabylonIAnimatable
+    public class BabylonAbstractMesh: BabylonNode
     {
         [DataMember]
-        public string id { get; set; }
-
-        [DataMember]
-        public string name { get; set; }
-        
-        [DataMember]
         public float[] position { get; set; }
 
         [DataMember]
@@ -26,24 +20,6 @@ namespace BabylonExport.Entities
         [DataMember]
         public BabylonActions actions { get; set; }
 
-        [DataMember]
-        public BabylonAnimation[] animations { 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 string parentId { get; set; }
-
         public BabylonAbstractMesh()
         {
             position = new[] { 0f, 0f, 0f };

+ 1 - 25
Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs

@@ -3,18 +3,9 @@
 namespace BabylonExport.Entities
 {
     [DataContract]
-    public class BabylonCamera : BabylonIAnimatable
+    public class BabylonCamera : BabylonNode
     {
         [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]
@@ -57,21 +48,6 @@ namespace BabylonExport.Entities
         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; }
-
-        [DataMember]
         public int mode { get; set; }
 
         [DataMember]

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

@@ -67,6 +67,7 @@
     <Compile Include="BabylonLensFlareSystem.cs" />
     <Compile Include="BabylonLight.cs" />
     <Compile Include="BabylonMaterial.cs" />
+    <Compile Include="BabylonNode.cs" />
     <Compile Include="BabylonPBRMaterial.cs" />
     <Compile Include="BabylonShaderMaterial.cs" />
     <Compile Include="BabylonStandardMaterial.cs" />
@@ -81,6 +82,7 @@
     <Compile Include="BabylonSound.cs" />
     <Compile Include="BabylonSubMesh.cs" />
     <Compile Include="BabylonTexture.cs" />
+    <Compile Include="BabylonQuaternion.cs" />
     <Compile Include="BabylonUniversalCamera.cs" />
     <Compile Include="BabylonVector3.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 1 - 6
Exporters/3ds Max/BabylonExport.Entities/BabylonIAnimatable.cs

@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace BabylonExport.Entities
+namespace BabylonExport.Entities
 {
     public interface BabylonIAnimatable
     {

+ 1 - 25
Exporters/3ds Max/BabylonExport.Entities/BabylonLight.cs

@@ -3,18 +3,9 @@
 namespace BabylonExport.Entities
 {
     [DataContract]
-    public class BabylonLight : BabylonIAnimatable
+    public class BabylonLight : BabylonNode
     {
         [DataMember]
-        public string name { get; set; }
-
-        [DataMember]
-        public string id { get; set; }
-
-        [DataMember]
-        public string parentId { get; set; }
-
-        [DataMember]
         public float[] position { get; set; }
 
         [DataMember]
@@ -51,21 +42,6 @@ namespace BabylonExport.Entities
         public string[] includedOnlyMeshesIds { 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; }
-
-        [DataMember]
         public object metadata { get; set; }
 
         [DataMember]

+ 32 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonNode.cs

@@ -0,0 +1,32 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonNode : BabylonIAnimatable
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
+        public string parentId { get; set; }
+
+        [DataMember]
+        public BabylonAnimation[] animations { 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; }
+    }
+}

+ 15 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonQuaternion.cs

@@ -0,0 +1,15 @@
+namespace BabylonExport.Entities
+{
+    public class BabylonQuaternion
+    {
+        public float X { get; set; }
+        public float Y { get; set; }
+        public float Z { get; set; }
+        public float W { get; set; }
+
+        public float[] ToArray()
+        {
+            return new [] {X, Y, Z, W};
+        }
+    }
+}

+ 30 - 6
Exporters/3ds Max/BabylonExport.Entities/BabylonTexture.cs

@@ -5,6 +5,30 @@ namespace BabylonExport.Entities
     [DataContract]
     public class BabylonTexture
     {
+        public enum AddressMode
+        {
+            CLAMP_ADDRESSMODE = 0,
+            WRAP_ADDRESSMODE = 1,
+            MIRROR_ADDRESSMODE = 2
+        }
+
+        public enum SamplingMode
+        {
+            // Constants
+            NEAREST_NEAREST_MIPLINEAR = 1, // nearest is mag = nearest and min = nearest and mip = linear
+            LINEAR_LINEAR_MIPNEAREST = 2, // Bilinear is mag = linear and min = linear and mip = nearest
+            LINEAR_LINEAR_MIPLINEAR = 3, // Trilinear is mag = linear and min = linear and mip = linear
+            NEAREST_NEAREST_MIPNEAREST = 4,
+            NEAREST_LINEAR_MIPNEAREST = 5,
+            NEAREST_LINEAR_MIPLINEAR = 6,
+            NEAREST_LINEAR = 7,
+            NEAREST_NEAREST = 8,
+            LINEAR_NEAREST_MIPNEAREST = 9,
+            LINEAR_NEAREST_MIPLINEAR = 10,
+            LINEAR_LINEAR = 11,
+            LINEAR_NEAREST = 12
+        }
+
         [DataMember]
         public string name { get; set; }
 
@@ -45,10 +69,10 @@ namespace BabylonExport.Entities
         public float wAng { get; set; }
 
         [DataMember]
-        public int wrapU { get; set; }
+        public AddressMode wrapU { get; set; }
 
         [DataMember]
-        public int wrapV { get; set; }
+        public AddressMode wrapV { get; set; }
 
         [DataMember]
         public int coordinatesIndex { get; set; }
@@ -72,7 +96,7 @@ namespace BabylonExport.Entities
         public string[] extensions { get; set; }
 
         [DataMember]
-        public int samplingMode { get; set; }
+        public SamplingMode samplingMode { get; set; }
 
         public BabylonTexture()
         {
@@ -84,11 +108,11 @@ namespace BabylonExport.Entities
             uAng = 0;
             vAng = 0;
             wAng = 0;
-            wrapU = 1;
-            wrapV = 1;
+            wrapU = AddressMode.WRAP_ADDRESSMODE;
+            wrapV = AddressMode.WRAP_ADDRESSMODE;
             hasAlpha = false;
             coordinatesIndex = 0;
-            samplingMode = 3;
+            samplingMode = SamplingMode.LINEAR_LINEAR_MIPLINEAR;
         }
     }
 }

+ 61 - 1
Exporters/3ds Max/BabylonExport.Entities/BabylonVector3.cs

@@ -32,5 +32,65 @@ namespace BabylonExport.Entities
         {
             return new BabylonVector3 { X = a.X / b, Y = a.Y / b, Z = a.Z / b };
         }
-    }
+
+        public static BabylonVector3 operator *(BabylonVector3 a, float b)
+        {
+            return new BabylonVector3 { X = a.X * b, Y = a.Y * b, Z = a.Z * b };
+        }
+
+        /**
+         * Returns a new Quaternion object, computed from the Vector3 coordinates.
+         */
+        public BabylonQuaternion toQuaternion()
+        {
+            var result = new BabylonQuaternion();
+
+            var cosxPlusz = Math.Cos((this.X + this.Z) * 0.5);
+            var sinxPlusz = Math.Sin((this.X + this.Z) * 0.5);
+            var coszMinusx = Math.Cos((this.Z - this.X) * 0.5);
+            var sinzMinusx = Math.Sin((this.Z - this.X) * 0.5);
+            var cosy = Math.Cos(this.Y * 0.5);
+            var siny = Math.Sin(this.Y * 0.5);
+
+            result.X = (float)(coszMinusx * siny);
+            result.Y = (float)(-sinzMinusx * siny);
+            result.Z = (float)(sinxPlusz * cosy);
+            result.W = (float)(cosxPlusz * cosy);
+            return result;
+        }
+
+        ///**
+        // * Returns a new Quaternion object, computed from the Vector3 coordinates.
+        // */
+        //public BabylonQuaternion toQuaternion()
+        //{
+        //    return RotationYawPitchRollToRef(Y,X,Z);
+        //}
+
+
+        /**
+         * Sets the passed quaternion "result" from the passed float Euler angles (y, x, z).  
+         */
+        public BabylonQuaternion RotationYawPitchRollToRef(float yaw, float pitch, float roll)
+        {
+            // Produces a quaternion from Euler angles in the z-y-x orientation (Tait-Bryan angles)
+            var halfRoll = roll * 0.5;
+            var halfPitch = pitch * 0.5;
+            var halfYaw = yaw * 0.5;
+
+            var sinRoll = Math.Sin(halfRoll);
+            var cosRoll = Math.Cos(halfRoll);
+            var sinPitch = Math.Sin(halfPitch);
+            var cosPitch = Math.Cos(halfPitch);
+            var sinYaw = Math.Sin(halfYaw);
+            var cosYaw = Math.Cos(halfYaw);
+
+            var result = new BabylonQuaternion();
+            result.X = (float)((cosYaw * sinPitch * cosRoll) + (sinYaw * cosPitch * sinRoll));
+            result.Y = (float)((sinYaw * cosPitch * cosRoll) - (cosYaw * sinPitch * sinRoll));
+            result.Z = (float)((cosYaw * cosPitch * sinRoll) - (sinYaw * sinPitch * cosRoll));
+            result.W = (float)((cosYaw * cosPitch * cosRoll) + (sinYaw * sinPitch * sinRoll));
+            return result;
+        }
+}
 }

+ 116 - 0
Exporters/3ds Max/GltfExport.Entities/GLTF.cs

@@ -0,0 +1,116 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTF
+    {
+        [DataMember(IsRequired = true)]
+        public GLTFAsset asset { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int scene { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFScene[] scenes { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFNode[] nodes { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFMesh[] meshes { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFAccessor[] accessors { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFBufferView[] bufferViews { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFBuffer[] buffers { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFMaterial[] materials { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFTexture[] textures { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFImage[] images { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFSampler[] samplers { get; set; }
+
+        public string OutputPath { get; private set; }
+
+        public List<GLTFNode> NodesList { get; private set; }
+        public List<GLTFBuffer> BuffersList { get; private set; }
+        public List<GLTFBufferView> BufferViewsList { get; private set; }
+        public List<GLTFAccessor> AccessorsList { get; private set; }
+        public List<GLTFMesh> MeshesList { get; private set; }
+        public List<GLTFMaterial> MaterialsList { get; private set; }
+        public List<GLTFTexture> TexturesList { get; private set; }
+        public List<GLTFImage> ImagesList { get; private set; }
+        public List<GLTFSampler> SamplersList { get; private set; }
+
+        public GLTF(string outputPath)
+        {
+            OutputPath = outputPath;
+
+            NodesList = new List<GLTFNode>();
+            BuffersList = new List<GLTFBuffer>();
+            BufferViewsList = new List<GLTFBufferView>();
+            AccessorsList = new List<GLTFAccessor>();
+            MeshesList = new List<GLTFMesh>();
+            MaterialsList = new List<GLTFMaterial>();
+            TexturesList = new List<GLTFTexture>();
+            ImagesList = new List<GLTFImage>();
+            SamplersList = new List<GLTFSampler>();
+        }
+
+        public void Prepare()
+        {
+            scenes[0].Prepare();
+
+            // Do not export empty arrays
+            if (NodesList.Count > 0)
+            {
+                nodes = NodesList.ToArray();
+                NodesList.ForEach(node => node.Prepare());
+            }
+            if (BuffersList.Count > 0)
+            {
+                buffers = BuffersList.ToArray();
+            }
+            if (BufferViewsList.Count > 0)
+            {
+                bufferViews = BufferViewsList.ToArray();
+            }
+            if (AccessorsList.Count > 0)
+            {
+                accessors = AccessorsList.ToArray();
+            }
+            if (MeshesList.Count > 0)
+            {
+                meshes = MeshesList.ToArray();
+            }
+            if (MaterialsList.Count > 0)
+            {
+                materials = MaterialsList.ToArray();
+            }
+            if (TexturesList.Count > 0)
+            {
+                textures = TexturesList.ToArray();
+            }
+            if (ImagesList.Count > 0)
+            {
+                images = ImagesList.ToArray();
+            }
+            if (SamplersList.Count > 0)
+            {
+                samplers = SamplersList.ToArray();
+            }
+        }
+    }
+}

+ 120 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFAccessor.cs

@@ -0,0 +1,120 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFAccessor : GLTFIndexedChildRootProperty
+    {
+        public enum ComponentType
+        {
+            BYTE = 5120,
+            UNSIGNED_BYTE = 5121,
+            SHORT = 5122,
+            UNSIGNED_SHORT = 5123,
+            UNSIGNED_INT = 5125,
+            FLOAT = 5126
+        }
+
+        public enum TypeEnum
+        {
+            SCALAR,
+            VEC2,
+            VEC3,
+            VEC4,
+            MAT2,
+            MAT3,
+            MAT4
+        }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? bufferView { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int byteOffset { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public ComponentType componentType { get; set; } // EComponentType BYTE = 5120, 5121..., FLOAT = 5126
+
+        [DataMember(EmitDefaultValue = false)]
+        public bool normalized { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public int count { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float[] max { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float[] min { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public string type { get; set; } // ex: SCALAR, VEC3
+
+        [DataMember(EmitDefaultValue = false)]
+        public string sparse { get; set; } // TODO
+
+        public GLTFBufferView BufferView;
+
+        public int getByteLength()
+        {
+            return count * getElementSize();
+        }
+
+        /// <summary>
+        /// Return the size of element accessible by accessor
+        /// </summary>
+        private int getElementSize()
+        {
+            TypeEnum typeAsEnum;
+            TypeEnum.TryParse(type, out typeAsEnum);
+            return getComponentTypeSize(componentType) * getNbComponents(typeAsEnum);
+        }
+
+        /// <summary>
+        /// Return the size, in bytes, of the 'componentType'
+        /// </summary>
+        private static int getComponentTypeSize(ComponentType componentType)
+        {
+            switch (componentType)
+            {
+                case ComponentType.BYTE:
+                case ComponentType.UNSIGNED_BYTE:
+                    return 1;
+                case ComponentType.SHORT:
+                case ComponentType.UNSIGNED_SHORT:
+                    return 2;
+                case ComponentType.UNSIGNED_INT:
+                case ComponentType.FLOAT:
+                    return 4;
+                default:
+                    return 0;
+            }
+        }
+
+        /// <summary>
+        /// Return the number of components defined by 'type'
+        /// </summary>
+        private static int getNbComponents(TypeEnum type)
+        {
+            switch (type)
+            {
+                case TypeEnum.SCALAR:
+                    return 1;
+                case TypeEnum.VEC2:
+                    return 2;
+                case TypeEnum.VEC3:
+                    return 3;
+                case TypeEnum.VEC4:
+                case TypeEnum.MAT2:
+                    return 4;
+                case TypeEnum.MAT3:
+                    return 9;
+                case TypeEnum.MAT4:
+                    return 16;
+                default:
+                    return 0;
+            }
+        }
+    }
+}

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

@@ -0,0 +1,20 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFAsset : GLTFProperty
+    {
+        [DataMember(IsRequired = true)]
+        public string version { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public string generator { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public string copyright { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public string minVersion { get; set; }
+    }
+}

+ 14 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFBuffer.cs

@@ -0,0 +1,14 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFBuffer : GLTFIndexedChildRootProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public string uri { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public int byteLength { get; set; }
+    }
+}

+ 22 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFBufferView.cs

@@ -0,0 +1,22 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFBufferView : GLTFIndexedChildRootProperty
+    {
+        [DataMember(IsRequired = true)]
+        public int buffer { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int byteOffset { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public int byteLength { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? byteStride { get; set; }
+
+        public GLTFBuffer Buffer;
+    }
+}

+ 11 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFChildRootProperty.cs

@@ -0,0 +1,11 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFChildRootProperty : GLTFProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public string name { get; set; }
+    }
+}

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

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" 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>{65686998-09AC-4A14-B23F-7FCE6BA994CF}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>GLTFExport</RootNamespace>
+    <AssemblyName>GLTFExport</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <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="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="GLTF.cs" />
+    <Compile Include="GLTFAccessor.cs" />
+    <Compile Include="GLTFBufferView.cs" />
+    <Compile Include="GLTFBuffer.cs" />
+    <Compile Include="GLTFSampler.cs" />
+    <Compile Include="GLTFIndexedChildRootProperty.cs" />
+    <Compile Include="GLTFImage.cs" />
+    <Compile Include="GLTFTextureInfo.cs" />
+    <Compile Include="GLTFTexture.cs" />
+    <Compile Include="GLTFPBRMetallicRoughness.cs" />
+    <Compile Include="GLTFMaterial.cs" />
+    <Compile Include="GLTFMeshPrimitive.cs" />
+    <Compile Include="GLTFMesh.cs" />
+    <Compile Include="GLTFProperty.cs" />
+    <Compile Include="GLTFChildRootProperty.cs" />
+    <Compile Include="GLTFNode.cs" />
+    <Compile Include="GLTFScene.cs" />
+    <Compile Include="GLTFAsset.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 17 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFImage.cs

@@ -0,0 +1,17 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFImage : GLTFIndexedChildRootProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public string uri { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public string mimeType { get; set; } // "image/jpeg" or "image/png"
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? bufferView { get; set; }
+    }
+}

+ 10 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFIndexedChildRootProperty.cs

@@ -0,0 +1,10 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFIndexedChildRootProperty : GLTFChildRootProperty
+    {
+        public int index;
+    }
+}

+ 41 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFMaterial.cs

@@ -0,0 +1,41 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFMaterial : GLTFIndexedChildRootProperty
+    {
+        public enum AlphaMode
+        {
+            OPAQUE,
+            MASK,
+            BLEND
+        }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFPBRMetallicRoughness pbrMetallicRoughness { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFTextureInfo normalTexture { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFTextureInfo occlusionTexture { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFTextureInfo emissiveTexture { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float[] emissiveFactor { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public string alphaMode { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float? alphaCutoff { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public bool doubleSided { get; set; }
+
+        public string id;
+    }
+}

+ 16 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFMesh.cs

@@ -0,0 +1,16 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFMesh : GLTFIndexedChildRootProperty
+    {
+        [DataMember (IsRequired = true)]
+        public GLTFMeshPrimitive[] primitives { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float[] weights { get; set; }
+
+        public GLTFNode gltfNode;
+    }
+}

+ 47 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFMeshPrimitive.cs

@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFMeshPrimitive : GLTFProperty
+    {
+        public enum FillMode
+        {
+            POINTS = 0,
+            LINES = 1,
+            LINE_LOOP = 2,
+            LINE_STRIP = 3,
+            TRIANGLES = 4,
+            TRIANGLE_STRIP = 5,
+            TRIANGLE_FAN = 6
+        }
+
+        public enum Attribute
+        {
+            POSITION,
+            NORMAL,
+            TANGENT,
+            TEXCOORD_0,
+            TEXCOORD_1,
+            COLOR_0,
+            JOINTS_0,
+            WEIGHTS_0
+        }
+
+        [DataMember(IsRequired = true)]
+        public Dictionary<string, int> attributes { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? indices { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public FillMode? mode { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? material { get; set; }
+
+        //[DataMember]
+        //public Dictionary<string, int>[] targets { get; set; }
+    }
+}

+ 53 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFNode.cs

@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFNode : GLTFIndexedChildRootProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public int? camera { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int[] children { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? skin { get; set; }
+
+        // Either matrix or Translation+Rotation+Scale
+        //[DataMember]
+        //public float[] matrix { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? mesh { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float[] translation { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float[] rotation { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float[] scale { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float[] weights { get; set; }
+
+        public List<int> ChildrenList { get; private set; }
+
+        public GLTFNode()
+        {
+            ChildrenList = new List<int>();
+        }
+
+        public void Prepare()
+        {
+            // Do not export empty arrays
+            if (ChildrenList.Count > 0)
+            {
+                children = ChildrenList.ToArray();
+            }
+        }
+    }
+}

+ 23 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFPBRMetallicRoughness.cs

@@ -0,0 +1,23 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFPBRMetallicRoughness : GLTFProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public float[] baseColorFactor { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFTextureInfo baseColorTexture { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float? metallicFactor { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public float? roughnessFactor { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public GLTFTextureInfo metallicRoughnessTexture { get; set; }
+    }
+}

+ 14 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFProperty.cs

@@ -0,0 +1,14 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public object extensions { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public object extras { get; set; }
+    }
+}

+ 43 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFSampler.cs

@@ -0,0 +1,43 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFSampler : GLTFIndexedChildRootProperty
+    {
+        public enum TextureMagFilter
+        {
+            NEAREST = 9728,
+            LINEAR = 9729
+        }
+
+        public enum TextureMinFilter
+        {
+            NEAREST = 9728,
+            LINEAR = 9729,
+            NEAREST_MIPMAP_NEAREST = 9984,
+            LINEAR_MIPMAP_NEAREST = 9985,
+            NEAREST_MIPMAP_LINEAR = 9986,
+            LINEAR_MIPMAP_LINEAR = 9987
+        }
+
+        public enum TextureWrapMode
+        {
+            CLAMP_TO_EDGE = 33071,
+            MIRRORED_REPEAT = 33648,
+            REPEAT = 10497
+        }
+
+        [DataMember(EmitDefaultValue = false)]
+        public TextureMagFilter? magFilter { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public TextureMinFilter? minFilter { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public TextureWrapMode? wrapS { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public TextureWrapMode? wrapT { get; set; }
+    }
+}

+ 28 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFScene.cs

@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFScene : GLTFChildRootProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public int[] nodes { get; set; }
+
+        public List<int> NodesList { get; private set; }
+
+        public GLTFScene()
+        {
+            NodesList = new List<int>();
+        }
+
+        public void Prepare()
+        {
+            // Do not export empty arrays
+            if (NodesList.Count > 0)
+            {
+                nodes = NodesList.ToArray();
+            }
+        }
+    }
+}

+ 14 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFTexture.cs

@@ -0,0 +1,14 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFTexture : GLTFIndexedChildRootProperty
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public int? sampler { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? source { get; set; }
+    }
+}

+ 14 - 0
Exporters/3ds Max/GltfExport.Entities/GLTFTextureInfo.cs

@@ -0,0 +1,14 @@
+using System.Runtime.Serialization;
+
+namespace GLTFExport.Entities
+{
+    [DataContract]
+    public class GLTFTextureInfo : GLTFChildRootProperty
+    {
+        [DataMember(IsRequired = true)]
+        public int index { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public int? texCoord { get; set; }
+    }
+}

+ 36 - 0
Exporters/3ds Max/GltfExport.Entities/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Les informations générales relatives à un assembly dépendent de
+// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations
+// associées à un assembly.
+[assembly: AssemblyTitle("GLTFExport.Entities")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("GLTFExport.Entities")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly
+// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de
+// COM, affectez la valeur true à l'attribut ComVisible sur ce type.
+[assembly: ComVisible(false)]
+
+// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM
+[assembly: Guid("65686998-09ac-4a14-b23f-7fce6ba994cf")]
+
+// Les informations de version pour un assembly se composent des quatre valeurs suivantes :
+//
+//      Version principale
+//      Version secondaire
+//      Numéro de build
+//      Révision
+//
+// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut
+// en utilisant '*', comme indiqué ci-dessous :
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 43 - 85
Exporters/3ds Max/Max2Babylon.sln

@@ -1,21 +1,20 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.16
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3ds Max", "3ds Max", "{2139CC27-1C89-49C8-95AC-7715ACBADC1F}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabylonExport.Entities", "BabylonExport.Entities\BabylonExport.Entities.csproj", "{6150965A-658C-4263-89AD-4F980EB0675D}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon", "Max2Babylon\Max2Babylon.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC489}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon2015", "Max2Babylon\2015\Max2Babylon2015.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC400}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabylonFileConverter", "BabylonFileConverter\BabylonFileConverter.csproj", "{A6B76356-1D1C-4C82-8199-A6406DA85A95}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActionsBuilder", "ActionsBuilder\ActionsBuilder.csproj", "{293A754C-3D80-42FB-99D8-7C9386CD297E}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon2017", "Max2Babylon\2017\Max2Babylon2017.csproj", "{2F49C726-A1F8-40D4-859F-1355949608DC}"
+	ProjectSection(ProjectDependencies) = postProject
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF} = {65686998-09AC-4A14-B23F-7FCE6BA994CF}
+	EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GLTFExport.Entities", "GltfExport.Entities\GLTFExport.Entities.csproj", "{65686998-09AC-4A14-B23F-7FCE6BA994CF}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -64,56 +63,6 @@ Global
 		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Win32.ActiveCfg = Release|Any CPU
 		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|x64.ActiveCfg = Release|Any CPU
 		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|x86.ActiveCfg = Release|Any CPU
-		{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}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Win32.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|x64.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Any CPU.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Any CPU.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|ARM.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Win32.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|x64.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|x86.ActiveCfg = Release|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
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|ARM.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Mixed Platforms.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Win32.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|x64.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|x86.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Win32.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|x64.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Any CPU.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Any CPU.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|ARM.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Win32.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|x64.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|x86.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Any CPU.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|ARM.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Mixed Platforms.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Win32.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|x64.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|x86.ActiveCfg = Release|Any CPU
 		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -150,31 +99,6 @@ Global
 		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x64.Build.0 = Release|Any CPU
 		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x86.ActiveCfg = Release|Any CPU
 		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x86.Build.0 = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|ARM.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Win32.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|x64.Build.0 = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Any CPU.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Any CPU.Build.0 = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|ARM.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Mixed Platforms.Build.0 = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Win32.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|x64.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|x86.ActiveCfg = Debug|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|ARM.ActiveCfg = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Win32.ActiveCfg = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|x64.ActiveCfg = Release|Any CPU
-		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|x86.ActiveCfg = Release|Any CPU
 		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -211,13 +135,47 @@ Global
 		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x64.Build.0 = Release|Any CPU
 		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x86.ActiveCfg = Release|Any CPU
 		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x86.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|ARM.Build.0 = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|Win32.Build.0 = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|x64.Build.0 = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Debug|x86.Build.0 = Debug|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|Any CPU.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|Any CPU.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|ARM.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|ARM.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|Win32.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|Win32.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|x64.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|x64.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|x86.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Profile|x86.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|ARM.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|ARM.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|Win32.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|Win32.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|x64.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|x64.Build.0 = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|x86.ActiveCfg = Release|Any CPU
+		{65686998-09AC-4A14-B23F-7FCE6BA994CF}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489} = {2139CC27-1C89-49C8-95AC-7715ACBADC1F}
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400} = {2139CC27-1C89-49C8-95AC-7715ACBADC1F}
 		{2F49C726-A1F8-40D4-859F-1355949608DC} = {2139CC27-1C89-49C8-95AC-7715ACBADC1F}
 	EndGlobalSection
 EndGlobal

+ 151 - 0
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Material.cs

@@ -0,0 +1,151 @@
+using BabylonExport.Entities;
+using GLTFExport.Entities;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        //readonly List<IIGameMaterial> referencedMaterials = new List<IIGameMaterial>();
+
+        private void ExportMaterial(BabylonMaterial babylonMaterial, GLTF gltf)
+        {
+            var name = babylonMaterial.name;
+            var id = babylonMaterial.id;
+
+            RaiseMessage("GLTFExporter.Material | ExportMaterial name=" + name, 1);
+
+            if (babylonMaterial.GetType() == typeof(BabylonStandardMaterial))
+            {
+                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial != null", 1);
+
+                var babylonStandardMaterial = babylonMaterial as BabylonStandardMaterial;
+
+
+                // --- prints ---
+
+                RaiseMessage("GLTFExporter.Material | babylonMaterial data", 1);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial.alpha=" + babylonMaterial.alpha, 2);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial.backFaceCulling=" + babylonMaterial.backFaceCulling, 2);
+                RaiseMessage("GLTFExporter.Material | babylonMaterial.wireframe=" + babylonMaterial.wireframe, 2);
+
+                // Ambient
+                for (int i = 0; i < babylonStandardMaterial.ambient.Length; i++)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.ambient[" + i + "]=" + babylonStandardMaterial.ambient[i], 2);
+                }
+
+                // Diffuse
+                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuse.Length=" + babylonStandardMaterial.diffuse.Length, 2);
+                for (int i = 0; i < babylonStandardMaterial.diffuse.Length; i++)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuse[" + i + "]=" + babylonStandardMaterial.diffuse[i], 2);
+                }
+                if (babylonStandardMaterial.diffuseTexture == null)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.diffuseTexture=null", 2);
+                }
+
+                // Normal / bump
+                if (babylonStandardMaterial.bumpTexture == null)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.bumpTexture=null", 2);
+                }
+
+                // Specular
+                for (int i = 0; i < babylonStandardMaterial.specular.Length; i++)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specular[" + i + "]=" + babylonStandardMaterial.specular[i], 2);
+                }
+                RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.specularPower=" + babylonStandardMaterial.specularPower, 2);
+
+                // Occlusion
+                if (babylonStandardMaterial.ambientTexture == null)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.ambientTexture=null", 2);
+                }
+
+                // Emissive
+                for (int i = 0; i < babylonStandardMaterial.emissive.Length; i++)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.emissive[" + i + "]=" + babylonStandardMaterial.emissive[i], 2);
+                }
+                if (babylonStandardMaterial.emissiveTexture == null)
+                {
+                    RaiseMessage("GLTFExporter.Material | babylonStandardMaterial.emissiveTexture=null", 2);
+                }
+
+
+                // --------------------------------
+                // --------- gltfMaterial ---------
+                // --------------------------------
+
+                RaiseMessage("GLTFExporter.Material | create gltfMaterial", 1);
+                var gltfMaterial = new GLTFMaterial
+                {
+                    name = name
+                };
+                gltfMaterial.id = babylonMaterial.id;
+                gltfMaterial.index = gltf.MaterialsList.Count;
+                gltf.MaterialsList.Add(gltfMaterial);
+
+                // Alpha
+                string alphaMode;
+                float? alphaCutoff;
+                getAlphaMode(babylonStandardMaterial, out alphaMode, out alphaCutoff);
+                gltfMaterial.alphaMode = alphaMode;
+                gltfMaterial.alphaCutoff = alphaCutoff;
+
+                // DoubleSided
+                gltfMaterial.doubleSided = !babylonMaterial.backFaceCulling;
+
+                // Normal
+                gltfMaterial.normalTexture = ExportTexture(babylonStandardMaterial.bumpTexture, gltf);
+
+                // Occulison
+                gltfMaterial.occlusionTexture = ExportTexture(babylonStandardMaterial.ambientTexture, gltf);
+
+                // Emissive
+                gltfMaterial.emissiveFactor = babylonStandardMaterial.emissive;
+                gltfMaterial.emissiveTexture = ExportTexture(babylonStandardMaterial.emissiveTexture, gltf);
+
+
+                // --------------------------------
+                // --- gltfPbrMetallicRoughness ---
+                // --------------------------------
+
+                RaiseMessage("GLTFExporter.Material | create gltfPbrMetallicRoughness", 1);
+                var gltfPbrMetallicRoughness = new GLTFPBRMetallicRoughness();
+                gltfMaterial.pbrMetallicRoughness = gltfPbrMetallicRoughness;
+
+                // Base color
+                var babylonDiffuseColor = babylonStandardMaterial.diffuse;
+                gltfPbrMetallicRoughness.baseColorFactor = new float[4]
+                {
+                    babylonDiffuseColor[0],
+                    babylonDiffuseColor[1],
+                    babylonDiffuseColor[2],
+                    babylonMaterial.alpha
+                };
+                gltfPbrMetallicRoughness.baseColorTexture = ExportTexture(babylonStandardMaterial.diffuseTexture, gltf);
+
+                // TODO - Metallic roughness
+                gltfPbrMetallicRoughness.metallicFactor = 0; // Non metal
+                // TODO - roughnessFactor
+                // TODO - metallicRoughnessTexture
+            }
+        }
+
+        private void getAlphaMode(BabylonStandardMaterial babylonMaterial, out string alphaMode, out float? alphaCutoff)
+        {
+            if (babylonMaterial.diffuseTexture.hasAlpha)
+            {
+                alphaMode = GLTFMaterial.AlphaMode.BLEND.ToString();
+            }
+            else
+            {
+                alphaMode = GLTFMaterial.AlphaMode.OPAQUE.ToString();
+            }
+            alphaCutoff = null;
+        }
+    }
+}

+ 427 - 0
Exporters/3ds Max/Max2Babylon/2017/Exporter/BabylonExporter.GLTFExporter.Mesh.cs

@@ -0,0 +1,427 @@
+using Autodesk.Max;
+using BabylonExport.Entities;
+using GLTFExport.Entities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode)
+        {
+            RaiseMessage("GLTFExporter.Mesh | ExportMesh babylonMesh.name=" + babylonMesh.name, 1);
+
+            // --------------------------
+            // ---------- Node ----------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Mesh | Node", 1);
+            // Node
+            var gltfNode = new GLTFNode();
+            gltfNode.name = babylonMesh.name;
+            gltfNode.index = gltf.NodesList.Count;
+            gltf.NodesList.Add(gltfNode);
+
+            // Hierarchy
+            if (gltfParentNode != null)
+            {
+                RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 2);
+                gltfParentNode.ChildrenList.Add(gltfNode.index);
+            }
+            else
+            {
+                // It's a root node
+                // Only root nodes are listed in a gltf scene
+                RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 2);
+                gltf.scenes[0].NodesList.Add(gltfNode.index);
+            }
+
+            // Transform
+            gltfNode.translation = babylonMesh.position;
+            if (babylonMesh.rotationQuaternion != null)
+            {
+                gltfNode.rotation = babylonMesh.rotationQuaternion;
+            }
+            else
+            {
+                // Convert rotation vector to quaternion
+                // TODO - Fix it
+                BabylonVector3 rotationVector3 = new BabylonVector3
+                {
+                    X = babylonMesh.rotation[0],
+                    Y = babylonMesh.rotation[1],
+                    Z = babylonMesh.rotation[2]
+                };
+                gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
+
+                RaiseMessage("GLTFExporter.Mesh | rotationVector3=[" + rotationVector3.X + "; " + rotationVector3.Y + "; " + rotationVector3.Z + "]", 2);
+                RaiseMessage("GLTFExporter.Mesh | gltfNode.rotation=[" + gltfNode.rotation[0] + "; " + gltfNode.rotation[1] + "; " + gltfNode.rotation[2] + "; " + gltfNode.rotation[3] + "]", 2);
+            }
+            gltfNode.scale = babylonMesh.scaling;
+
+
+            // --------------------------
+            // --- Mesh from babylon ----
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 1);
+            // Retreive general data from babylon mesh
+            int nbVertices = babylonMesh.positions.Length / 3;
+            bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0;
+            bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0;
+            bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0;
+
+            RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 2);
+            RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 2);
+            RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 2);
+            RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 2);
+
+            // Retreive vertices data from babylon mesh
+            List<GLTFGlobalVertex> globalVertices = new List<GLTFGlobalVertex>();
+            for (int i = 0; i < nbVertices; i++)
+            {
+                GLTFGlobalVertex globalVertex = new GLTFGlobalVertex();
+                globalVertex.Position = createIPoint3(babylonMesh.positions, i);
+                globalVertex.Normal = createIPoint3(babylonMesh.normals, i);
+                if (hasUV)
+                {
+                    globalVertex.UV = createIPoint2(babylonMesh.uvs, i);
+                    // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
+                    // While for Babylon, it corresponds to the lower left corner of a texture image
+                    globalVertex.UV.Y = 1 - globalVertex.UV.Y;
+                }
+                if (hasUV2)
+                {
+                    globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, i);
+                    // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
+                    // While for Babylon, it corresponds to the lower left corner of a texture image
+                    globalVertex.UV2.Y = 1 - globalVertex.UV2.Y;
+                }
+                if (hasColor)
+                {
+                    globalVertex.Color = createIPoint4(babylonMesh.colors, i).ToArray();
+                }
+
+                globalVertices.Add(globalVertex);
+            }
+
+            // Retreive indices from babylon mesh
+            List<ushort> indices = new List<ushort>();
+            indices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
+            // Swap face side
+            for (int i = 0; i < indices.Count; i += 3)
+            { 
+                var tmp = indices[i];
+                indices[i] = indices[i + 2];
+                indices[i+2] = tmp;
+            }
+
+
+            // --------------------------
+            // ------- Init glTF --------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Mesh | Init glTF", 1);
+            // Mesh
+            var gltfMesh = new GLTFMesh { name = babylonMesh.name };
+            gltfMesh.index = gltf.MeshesList.Count;
+            gltf.MeshesList.Add(gltfMesh);
+            gltfNode.mesh = gltfMesh.index;
+            gltfMesh.gltfNode = gltfNode;
+
+            // MeshPrimitive
+            var meshPrimitives = new List<GLTFMeshPrimitive>();
+            var meshPrimitive = new GLTFMeshPrimitive
+            {
+                attributes = new Dictionary<string, int>(),
+                mode = GLTFMeshPrimitive.FillMode.TRIANGLES // TODO reteive info from babylon material
+            };
+            meshPrimitives.Add(meshPrimitive);
+
+            // Buffer
+            var buffer = new GLTFBuffer
+            {
+                uri = gltfMesh.name + ".bin"
+            };
+            buffer.index = gltf.BuffersList.Count;
+            gltf.BuffersList.Add(buffer);
+
+            // BufferView - Scalar
+            var bufferViewScalar = new GLTFBufferView
+            {
+                name = "bufferViewScalar",
+                buffer = buffer.index,
+                Buffer = buffer
+            };
+            bufferViewScalar.index = gltf.BufferViewsList.Count;
+            gltf.BufferViewsList.Add(bufferViewScalar);
+
+            // BufferView - Vector3
+            var bufferViewFloatVec3 = new GLTFBufferView
+            {
+                name = "bufferViewFloatVec3",
+                buffer = buffer.index,
+                Buffer = buffer,
+                byteOffset = 0,
+                byteStride = 12 // Field only defined for buffer views that contain vertex attributes. A vertex needs 3 * 4 bytes
+            };
+            bufferViewFloatVec3.index = gltf.BufferViewsList.Count;
+            gltf.BufferViewsList.Add(bufferViewFloatVec3);
+
+            // Accessor - Indices
+            var accessorIndices = new GLTFAccessor
+            {
+                name = "accessorIndices",
+                bufferView = bufferViewScalar.index,
+                BufferView = bufferViewScalar,
+                componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT,
+                type = GLTFAccessor.TypeEnum.SCALAR.ToString()
+            };
+            accessorIndices.index = gltf.AccessorsList.Count;
+            gltf.AccessorsList.Add(accessorIndices);
+            meshPrimitive.indices = accessorIndices.index;
+
+            // Accessor - Positions
+            var accessorPositions = new GLTFAccessor
+            {
+                name = "accessorPositions",
+                bufferView = bufferViewFloatVec3.index,
+                BufferView = bufferViewFloatVec3,
+                componentType = GLTFAccessor.ComponentType.FLOAT,
+                type = GLTFAccessor.TypeEnum.VEC3.ToString(),
+                min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue },
+                max = new float[] { float.MinValue, float.MinValue, float.MinValue }
+            };
+            accessorPositions.index = gltf.AccessorsList.Count;
+            gltf.AccessorsList.Add(accessorPositions);
+            meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
+
+            // Accessor - Normals
+            var accessorNormals = new GLTFAccessor
+            {
+                name = "accessorNormals",
+                bufferView = bufferViewFloatVec3.index,
+                BufferView = bufferViewFloatVec3,
+                componentType = GLTFAccessor.ComponentType.FLOAT,
+                type = GLTFAccessor.TypeEnum.VEC3.ToString()
+            };
+            accessorNormals.index = gltf.AccessorsList.Count;
+            gltf.AccessorsList.Add(accessorNormals);
+            meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
+
+            // BufferView - Vector4
+            GLTFBufferView bufferViewFloatVec4 = null;
+            // Accessor - Colors
+            GLTFAccessor accessorColors = null;
+            if (hasColor)
+            {
+                bufferViewFloatVec4 = new GLTFBufferView
+                {
+                    name = "bufferViewFloatVec4",
+                    buffer = buffer.index,
+                    Buffer = buffer,
+                    byteOffset = 0,
+                    byteStride = 16 // Field only defined for buffer views that contain vertex attributes. A vertex needs 4 * 4 bytes
+                };
+                bufferViewFloatVec4.index = gltf.BufferViewsList.Count;
+                gltf.BufferViewsList.Add(bufferViewFloatVec4);
+
+                accessorColors = new GLTFAccessor
+                {
+                    name = "accessorColors",
+                    bufferView = bufferViewFloatVec4.index,
+                    BufferView = bufferViewFloatVec4,
+                    componentType = GLTFAccessor.ComponentType.FLOAT,
+                    type = GLTFAccessor.TypeEnum.VEC4.ToString()
+                };
+                accessorColors.index = gltf.AccessorsList.Count;
+                gltf.AccessorsList.Add(accessorColors);
+                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
+            }
+
+            // BufferView - Vector2
+            GLTFBufferView bufferViewFloatVec2 = null;
+            if (hasUV ||hasUV2)
+            {
+                bufferViewFloatVec2 = new GLTFBufferView
+                {
+                    name = "bufferViewFloatVec2",
+                    buffer = buffer.index,
+                    Buffer = buffer,
+                    byteStride = 8 // Field only defined for buffer views that contain vertex attributes. A vertex needs 2 * 4 bytes
+                };
+                bufferViewFloatVec2.index = gltf.BufferViewsList.Count;
+                gltf.BufferViewsList.Add(bufferViewFloatVec2);
+            }
+
+            // Accessor - UV
+            GLTFAccessor accessorUVs = null;
+            if (hasUV)
+            {
+                accessorUVs = new GLTFAccessor
+                {
+                    name = "accessorUVs",
+                    bufferView = bufferViewFloatVec2.index,
+                    BufferView = bufferViewFloatVec2,
+                    componentType = GLTFAccessor.ComponentType.FLOAT,
+                    type = GLTFAccessor.TypeEnum.VEC2.ToString()
+                };
+                accessorUVs.index = gltf.AccessorsList.Count;
+                gltf.AccessorsList.Add(accessorUVs);
+                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
+            }
+
+            // Accessor - UV2
+            GLTFAccessor accessorUV2s = null;
+            if (hasUV2)
+            {
+                accessorUV2s = new GLTFAccessor
+                {
+                    name = "accessorUV2s",
+                    bufferView = bufferViewFloatVec2.index,
+                    BufferView = bufferViewFloatVec2,
+                    componentType = GLTFAccessor.ComponentType.FLOAT,
+                    type = GLTFAccessor.TypeEnum.VEC2.ToString()
+                };
+                accessorUV2s.index = gltf.AccessorsList.Count;
+                gltf.AccessorsList.Add(accessorUV2s);
+                meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
+            }
+
+
+            // --------------------------
+            // ------ Mesh as glTF ------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 1);
+            // Material
+            //TODO - Handle multimaterials
+            GLTFMaterial gltfMaterial = gltf.MaterialsList.Find(material => material.id == babylonMesh.materialId);
+            if (gltfMaterial != null)
+            {
+                meshPrimitive.material = gltfMaterial.index;
+            }
+
+            // Update min and max vertex position for each component (X, Y, Z)
+            globalVertices.ForEach((globalVertex) =>
+            {
+                var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
+                for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++)
+                {
+                    if (positionArray[indexComponent] < accessorPositions.min[indexComponent])
+                    {
+                        accessorPositions.min[indexComponent] = positionArray[indexComponent];
+                    }
+                    if (positionArray[indexComponent] > accessorPositions.max[indexComponent])
+                    {
+                        accessorPositions.max[indexComponent] = positionArray[indexComponent];
+                    }
+                }
+            });
+
+            // Update byte length and count of accessors, bufferViews and buffers
+            // Scalar
+            AddElementsToAccessor(accessorIndices, indices.Count);
+            // Vector3
+            bufferViewFloatVec3.byteOffset = buffer.byteLength;
+            AddElementsToAccessor(accessorPositions, globalVertices.Count);
+            AddElementsToAccessor(accessorNormals, globalVertices.Count);
+            // Vector4
+            if (hasColor)
+            {
+                bufferViewFloatVec4.byteOffset = buffer.byteLength;
+                AddElementsToAccessor(accessorColors, globalVertices.Count);
+            }
+            // Vector2
+            if (hasUV || hasUV2)
+            {
+                bufferViewFloatVec2.byteOffset = buffer.byteLength;
+
+                if (hasUV)
+                {
+                    AddElementsToAccessor(accessorUVs, globalVertices.Count);
+                }
+                if (hasUV2)
+                {
+                    AddElementsToAccessor(accessorUV2s, globalVertices.Count);
+                }
+            }
+
+
+            // --------------------------
+            // --------- Saving ---------
+            // --------------------------
+
+            string outputBinaryFile = Path.Combine(gltf.OutputPath,  gltfMesh.name + ".bin");
+            RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 1);
+
+            using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create)))
+            {
+                // Binary arrays
+                List<float> vertices = globalVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList();
+                List<float> normals = globalVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
+
+                List<float> colors = new List<float>();
+                if (hasColor)
+                {
+                    colors = globalVertices.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
+                }
+
+                List<float> uvs = new List<float>();
+                if (hasUV)
+                {
+                    uvs = globalVertices.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
+                }
+
+                List<float> uvs2 = new List<float>();
+                if (hasUV2)
+                {
+                    uvs2 = globalVertices.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
+                }
+
+                // Write data to binary file
+                indices.ForEach(n => writer.Write(n));
+                vertices.ForEach(n => writer.Write(n));
+                normals.ForEach(n => writer.Write(n));
+                colors.ForEach(n => writer.Write(n));
+                uvs.ForEach(n => writer.Write(n));
+            }
+
+            gltfMesh.primitives = meshPrimitives.ToArray();
+
+            return gltfMesh;
+        }
+
+        private IPoint2 createIPoint2(float[] array, int index)
+        {
+            var startIndex = index * 2;
+            return Loader.Global.Point2.Create(array[startIndex], array[startIndex + 1]);
+        }
+
+        private IPoint3 createIPoint3(float[] array, int index)
+        {
+            var startIndex = index * 3;
+            return Loader.Global.Point3.Create(array[startIndex], array[startIndex + 1], array[startIndex + 2]);
+        }
+
+        private IPoint4 createIPoint4(float[] array, int index)
+        {
+            var startIndex = index * 4;
+            return Loader.Global.Point4.Create(array[startIndex], array[startIndex + 1], array[startIndex + 2], array[startIndex + 3]);
+        }
+
+        private void AddElementsToAccessor(GLTFAccessor accessor, int count)
+        {
+            GLTFBufferView bufferView = accessor.BufferView;
+            GLTFBuffer buffer = bufferView.Buffer;
+
+            accessor.byteOffset = bufferView.byteLength;
+            accessor.count += count;
+            bufferView.byteLength += accessor.getByteLength();
+            buffer.byteLength += accessor.getByteLength();
+        }
+    }
+}

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

@@ -0,0 +1,180 @@
+using BabylonExport.Entities;
+using GLTFExport.Entities;
+
+namespace Max2Babylon
+{
+    partial class BabylonExporter
+    {
+        private GLTFTextureInfo ExportTexture(BabylonTexture babylonTexture, GLTF gltf)
+        {
+            if (babylonTexture == null)
+            {
+                return null;
+            }
+
+            RaiseMessage("GLTFExporter.Texture | ExportTexture babylonTexture.name=" + babylonTexture.name, 1);
+
+            // --------------------------
+            // -------- Sampler ---------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Texture | create sampler", 1);
+            GLTFSampler gltfSampler = new GLTFSampler();
+            gltfSampler.index = gltf.SamplersList.Count;
+            gltf.SamplersList.Add(gltfSampler);
+
+            // --- Retreive info from babylon texture ---
+            // Mag and min filters
+            GLTFSampler.TextureMagFilter? magFilter;
+            GLTFSampler.TextureMinFilter? minFilter;
+            getSamplingParameters(babylonTexture.samplingMode, out magFilter, out minFilter);
+            gltfSampler.magFilter = magFilter;
+            gltfSampler.minFilter = minFilter;
+            // WrapS and wrapT
+            gltfSampler.wrapS = getWrapMode(babylonTexture.wrapU);
+            gltfSampler.wrapT = getWrapMode(babylonTexture.wrapV);
+            
+
+            // --------------------------
+            // --------- Image ----------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Texture | create image", 1);
+            GLTFImage gltfImage = new GLTFImage
+            {
+                uri = babylonTexture.name
+            };
+
+            gltfImage.index = gltf.ImagesList.Count;
+            gltf.ImagesList.Add(gltfImage);
+
+
+            // --------------------------
+            // -------- Texture ---------
+            // --------------------------
+
+            RaiseMessage("GLTFExporter.Texture | create texture", 1);
+            var gltfTexture = new GLTFTexture
+            {
+                name = babylonTexture.name,
+                sampler = gltfSampler.index,
+                source = gltfImage.index
+            };
+            gltfTexture.index = gltf.TexturesList.Count;
+            gltf.TexturesList.Add(gltfTexture);
+
+
+            // --------------------------
+            // ------ TextureInfo -------
+            // --------------------------
+            var gltfTextureInfo = new GLTFTextureInfo
+            {
+                index = gltfTexture.index
+            };
+
+
+            // TODO - Animations
+
+            //// Copy image to output
+            //var absolutePath = texture.Map.FullFilePath;
+            //try
+            //{
+            //    if (File.Exists(absolutePath))
+            //    {
+            //        if (CopyTexturesToOutput)
+            //        {
+            //            RaiseMessage("GLTFExporter.Texture | copy image src path = "+ absolutePath + " and dest path = "+ Path.Combine(gltf.OutputPath, gltfTexture.name));
+            //            File.Copy(absolutePath, Path.Combine(gltf.OutputPath, gltfTexture.name), true);
+            //        }
+            //    }
+            //    else
+            //    {
+            //        RaiseWarning(string.Format("Texture {0} not found.", gltfTexture.name), 2);
+            //    }
+
+            //}
+            //catch
+            //{
+            //    // silently fails
+            //}
+
+            return gltfTextureInfo;
+        }
+
+        private void getSamplingParameters(BabylonTexture.SamplingMode samplingMode, out GLTFSampler.TextureMagFilter? magFilter, out GLTFSampler.TextureMinFilter? minFilter)
+        {
+            switch (samplingMode)
+            {
+                case BabylonTexture.SamplingMode.NEAREST_NEAREST_MIPLINEAR:
+                    magFilter = GLTFSampler.TextureMagFilter.NEAREST;
+                    minFilter = GLTFSampler.TextureMinFilter.NEAREST_MIPMAP_LINEAR;
+                    break;
+                case BabylonTexture.SamplingMode.LINEAR_LINEAR_MIPNEAREST:
+                    magFilter = GLTFSampler.TextureMagFilter.LINEAR;
+                    minFilter = GLTFSampler.TextureMinFilter.LINEAR_MIPMAP_NEAREST;
+                    break;
+                case BabylonTexture.SamplingMode.LINEAR_LINEAR_MIPLINEAR:
+                    magFilter = GLTFSampler.TextureMagFilter.LINEAR;
+                    minFilter = GLTFSampler.TextureMinFilter.LINEAR_MIPMAP_LINEAR;
+                    break;
+                case BabylonTexture.SamplingMode.NEAREST_NEAREST_MIPNEAREST:
+                    magFilter = GLTFSampler.TextureMagFilter.NEAREST;
+                    minFilter = GLTFSampler.TextureMinFilter.NEAREST_MIPMAP_NEAREST;
+                    break;
+                case BabylonTexture.SamplingMode.NEAREST_LINEAR_MIPNEAREST:
+                    magFilter = GLTFSampler.TextureMagFilter.NEAREST;
+                    minFilter = GLTFSampler.TextureMinFilter.LINEAR_MIPMAP_NEAREST;
+                    break;
+                case BabylonTexture.SamplingMode.NEAREST_LINEAR_MIPLINEAR:
+                    magFilter = GLTFSampler.TextureMagFilter.NEAREST;
+                    minFilter = GLTFSampler.TextureMinFilter.LINEAR_MIPMAP_LINEAR;
+                    break;
+                case BabylonTexture.SamplingMode.NEAREST_LINEAR:
+                    magFilter = GLTFSampler.TextureMagFilter.NEAREST;
+                    minFilter = GLTFSampler.TextureMinFilter.LINEAR;
+                    break;
+                case BabylonTexture.SamplingMode.NEAREST_NEAREST:
+                    magFilter = GLTFSampler.TextureMagFilter.NEAREST;
+                    minFilter = GLTFSampler.TextureMinFilter.NEAREST;
+                    break;
+                case BabylonTexture.SamplingMode.LINEAR_NEAREST_MIPNEAREST:
+                    magFilter = GLTFSampler.TextureMagFilter.LINEAR;
+                    minFilter = GLTFSampler.TextureMinFilter.NEAREST_MIPMAP_NEAREST;
+                    break;
+                case BabylonTexture.SamplingMode.LINEAR_NEAREST_MIPLINEAR:
+                    magFilter = GLTFSampler.TextureMagFilter.LINEAR;
+                    minFilter = GLTFSampler.TextureMinFilter.NEAREST_MIPMAP_LINEAR;
+                    break;
+                case BabylonTexture.SamplingMode.LINEAR_LINEAR:
+                    magFilter = GLTFSampler.TextureMagFilter.LINEAR;
+                    minFilter = GLTFSampler.TextureMinFilter.LINEAR;
+                    break;
+                case BabylonTexture.SamplingMode.LINEAR_NEAREST:
+                    magFilter = GLTFSampler.TextureMagFilter.LINEAR;
+                    minFilter = GLTFSampler.TextureMinFilter.NEAREST;
+                    break;
+                default:
+                    RaiseError("GLTFExporter.Texture | texture sampling mode not found");
+                    magFilter = null;
+                    minFilter = null;
+                    break;
+            }
+        }
+
+        private GLTFSampler.TextureWrapMode? getWrapMode(BabylonTexture.AddressMode babylonTextureAdresseMode)
+        {
+            switch (babylonTextureAdresseMode)
+            {
+                case BabylonTexture.AddressMode.CLAMP_ADDRESSMODE:
+                    return GLTFSampler.TextureWrapMode.CLAMP_TO_EDGE;
+                case BabylonTexture.AddressMode.WRAP_ADDRESSMODE:
+                    return GLTFSampler.TextureWrapMode.REPEAT;
+                case BabylonTexture.AddressMode.MIRROR_ADDRESSMODE:
+                    return GLTFSampler.TextureWrapMode.MIRRORED_REPEAT;
+                default:
+                    RaiseError("GLTFExporter.Texture | texture wrap mode not found");
+                    return null;
+            }
+        }
+    }
+}

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

@@ -0,0 +1,145 @@
+using BabylonExport.Entities;
+using GLTFExport.Entities;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using Color = System.Drawing.Color;
+
+namespace Max2Babylon
+{
+    internal partial class BabylonExporter
+    {
+        public void ExportGltf(BabylonScene babylonScene, string outputFile, bool generateBinary)
+        {
+            RaiseMessage("GLTFExporter | Export outputFile=" + outputFile + " generateBinary=" + generateBinary);
+            RaiseMessage("GLTFExporter | Exportation started", Color.Blue);
+
+            ReportProgressChanged(0);
+            var gltf = new GLTF(Path.GetDirectoryName(outputFile));
+
+            // Asset
+            gltf.asset = new GLTFAsset
+            {
+                version = "2.0",
+                generator = "Babylon2Gltf2017",
+                copyright = "2017 (c) BabylonJS"
+                // no minVersion
+            };
+
+            // Scene
+            gltf.scene = 0;
+
+            // Scenes
+            GLTFScene scene = new GLTFScene();
+            GLTFScene[] scenes = { scene };
+            gltf.scenes = scenes;
+
+            // Materials
+            RaiseMessage("GLTFExporter | Exporting materials");
+            ReportProgressChanged(10);
+            var babylonMaterials = babylonScene.MaterialsList;
+            babylonMaterials.ForEach((babylonMaterial) =>
+            {
+                ExportMaterial(babylonMaterial, gltf);
+                CheckCancelled();
+            });
+            // TODO - Handle multimaterials
+            RaiseMessage(string.Format("GLTFExporter | Total: {0}", gltf.MaterialsList.Count /*+ glTF.MultiMaterialsList.Count*/), Color.Gray, 1);
+
+            // Nodes
+            List<BabylonNode> babylonNodes = new List<BabylonNode>();
+            babylonNodes.AddRange(babylonScene.meshes);
+            babylonNodes.AddRange(babylonScene.lights);
+            babylonNodes.AddRange(babylonScene.cameras);
+
+            // Root nodes
+            RaiseMessage("GLTFExporter | Exporting root nodes");
+            List<BabylonNode> babylonRootNodes = babylonNodes.FindAll(node => node.parentId == null);
+            var progressionStep = 80.0f / babylonRootNodes.Count;
+            var progression = 20.0f;
+            ReportProgressChanged((int)progression);
+            babylonRootNodes.ForEach(babylonNode =>
+            {
+                exportNodeRec(babylonNode, gltf, babylonScene);
+                progression += progressionStep;
+                ReportProgressChanged((int)progression);
+                CheckCancelled();
+            });
+
+            // Output
+            RaiseMessage("GLTFExporter | Saving to output file");
+            // Cast lists to arrays
+            gltf.Prepare();
+            var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings()); // Standard serializer, not the optimized one
+            var sb = new StringBuilder();
+            var sw = new StringWriter(sb, CultureInfo.InvariantCulture);
+
+            using (var jsonWriter = new JsonTextWriter(sw))
+            {
+                jsonWriter.Formatting = Formatting.None;
+                jsonSerializer.Serialize(jsonWriter, gltf);
+            }
+            string outputGltfFile = Path.ChangeExtension(outputFile, "gltf");
+            File.WriteAllText(outputGltfFile, sb.ToString());
+
+            // Binary
+            if (generateBinary)
+            {
+                // TODO - Export glTF data to binary format .glb
+                RaiseError("GLTFExporter | TODO - Generating binary files");
+            }
+
+            ReportProgressChanged(100);
+        }
+
+        private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null)
+        {
+            GLTFNode gltfNode = null; 
+            if (babylonNode.GetType() == typeof(BabylonMesh))
+            {
+                GLTFMesh gltfMesh = ExportMesh(babylonNode as BabylonMesh, gltf, gltfParentNode);
+                if (gltfMesh != null)
+                {
+                    gltfNode = gltfMesh.gltfNode;
+                }
+            }
+            else if (babylonNode.GetType() == typeof(BabylonCamera))
+            {
+                // TODO - Export camera nodes
+                RaiseError($"TODO - Export camera node named {babylonNode.name}", 1);
+            }
+            else if (babylonNode.GetType() == typeof(BabylonLight))
+            {
+                // TODO - Export light nodes as empty nodes (no lights in glTF 2.0 core)
+                RaiseError($"TODO - Export light node named {babylonNode.name}", 1);
+                RaiseWarning($"GLTFExporter.Node | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1);
+            }
+            else
+            {
+                RaiseError($"Node named {babylonNode.name} as no exporter", 1);
+            }
+
+            CheckCancelled();
+
+            // If parent is exported successfully...
+            if (gltfNode != null)
+            {
+                // ...export its children
+                List<BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene);
+                babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode));
+            }
+        }
+
+        private List<BabylonNode> getDescendants(BabylonNode babylonNode, BabylonScene babylonScene)
+        {
+            List<BabylonNode> babylonNodes = new List<BabylonNode>();
+            babylonNodes.AddRange(babylonScene.meshes);
+            babylonNodes.AddRange(babylonScene.lights);
+            babylonNodes.AddRange(babylonScene.cameras);
+
+            return babylonNodes.FindAll(node => node.parentId == babylonNode.id);
+        }
+    }
+}

+ 13 - 0
Exporters/3ds Max/Max2Babylon/2017/Exporter/GLTFGlobalVertex.cs

@@ -0,0 +1,13 @@
+using Autodesk.Max;
+
+namespace Max2Babylon
+{
+    public class GLTFGlobalVertex
+    {
+        public IPoint3 Position { get; set; }
+        public IPoint3 Normal { get; set; }
+        public IPoint2 UV { get; set; }
+        public IPoint2 UV2 { get; set; }
+        public float[] Color { get; set; }
+    }
+}

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

@@ -191,6 +191,11 @@
     <Compile Include="..\Tools\WebServer.cs">
       <Link>Tools\WebServer.cs</Link>
     </Compile>
+    <Compile Include="Exporter\BabylonExporter.GLTFExporter.Material.cs" />
+    <Compile Include="Exporter\BabylonExporter.GLTFExporter.Texture.cs" />
+    <Compile Include="Exporter\BabylonExporter.GLTFExporter.Mesh.cs" />
+    <Compile Include="Exporter\BabylonExporter.GLTFExporter.cs" />
+    <Compile Include="Exporter\GLTFGlobalVertex.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\Resources.Designer.cs">
       <AutoGen>True</AutoGen>
@@ -244,6 +249,10 @@
       <Project>{a6b76356-1d1c-4c82-8199-a6406da85a95}</Project>
       <Name>BabylonFileConverter</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\GltfExport.Entities\GLTFExport.Entities.csproj">
+      <Project>{65686998-09ac-4a14-b23f-7fce6ba994cf}</Project>
+      <Name>GLTFExport.Entities</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <WCFMetadata Include="Service References\" />

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

@@ -182,24 +182,24 @@ namespace Max2Babylon
             babylonTexture.vAng = uvGen.GetVAng(0);
             babylonTexture.wAng = uvGen.GetWAng(0);
 
-            babylonTexture.wrapU = 0; // CLAMP
+            babylonTexture.wrapU = BabylonTexture.AddressMode.CLAMP_ADDRESSMODE; // CLAMP
             if ((uvGen.TextureTiling & 1) != 0) // WRAP
             {
-                babylonTexture.wrapU = 1;
+                babylonTexture.wrapU = BabylonTexture.AddressMode.WRAP_ADDRESSMODE;
             }
             else if ((uvGen.TextureTiling & 4) != 0) // MIRROR
             {
-                babylonTexture.wrapU = 2;
+                babylonTexture.wrapU = BabylonTexture.AddressMode.MIRROR_ADDRESSMODE;
             }
 
-            babylonTexture.wrapV = 0; // CLAMP
+            babylonTexture.wrapV = BabylonTexture.AddressMode.CLAMP_ADDRESSMODE; // CLAMP
             if ((uvGen.TextureTiling & 2) != 0) // WRAP
             {
-                babylonTexture.wrapV = 1;
+                babylonTexture.wrapV = BabylonTexture.AddressMode.WRAP_ADDRESSMODE;
             }
             else if ((uvGen.TextureTiling & 8) != 0) // MIRROR
             {
-                babylonTexture.wrapV = 2;
+                babylonTexture.wrapV = BabylonTexture.AddressMode.MIRROR_ADDRESSMODE;
             }
 
             babylonTexture.name = Path.GetFileName(texture.MapName);

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

@@ -1,16 +1,14 @@
-using System;
-using System.Collections.Generic;
+using Autodesk.Max;
+using BabylonExport.Entities;
+using Newtonsoft.Json;
+using System;
 using System.Diagnostics;
 using System.Globalization;
 using System.IO;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Forms;
-using Autodesk.Max;
-using BabylonExport.Entities;
-using Newtonsoft.Json;
 using Color = System.Drawing.Color;
-using System.Runtime.InteropServices;
 
 namespace Max2Babylon
 {
@@ -77,7 +75,7 @@ namespace Max2Babylon
             }
         }
 
-        public async Task ExportAsync(string outputFile, bool generateManifest, bool onlySelected, bool generateBinary, Form callerForm)
+        public async Task ExportAsync(string outputFile, bool generateManifest, bool onlySelected, bool generateBinary, bool exportGltf, Form callerForm)
         {
             var gameConversionManger = Loader.Global.ConversionManager;
             gameConversionManger.CoordSystem = Autodesk.Max.IGameConversionManager.CoordSystem.D3d;
@@ -342,10 +340,15 @@ namespace Max2Babylon
             }
 
             ReportProgressChanged(100);
+
+            // Export glTF
+            if (exportGltf)
+            {
+                ExportGltf(babylonScene, outputFile, generateBinary);
+            }
+
             watch.Stop();
             RaiseMessage(string.Format("Exportation done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue);
         }
-
-
     }
 }

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

@@ -48,6 +48,7 @@
             this.chkHidden = new System.Windows.Forms.CheckBox();
             this.butExportAndRun = new System.Windows.Forms.Button();
             this.butClose = new System.Windows.Forms.Button();
+            this.chkGltf = new System.Windows.Forms.CheckBox();
             ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
             this.groupBox1.SuspendLayout();
             this.SuspendLayout();
@@ -57,7 +58,7 @@
             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(211, 153);
+            this.butExport.Location = new System.Drawing.Point(210, 166);
             this.butExport.Name = "butExport";
             this.butExport.Size = new System.Drawing.Size(197, 27);
             this.butExport.TabIndex = 0;
@@ -116,9 +117,9 @@
             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, 186);
+            this.treeView.Location = new System.Drawing.Point(12, 199);
             this.treeView.Name = "treeView";
-            this.treeView.Size = new System.Drawing.Size(799, 324);
+            this.treeView.Size = new System.Drawing.Size(799, 311);
             this.treeView.TabIndex = 6;
             // 
             // butCancel
@@ -192,7 +193,7 @@
             this.groupBox1.Controls.Add(this.label2);
             this.groupBox1.Location = new System.Drawing.Point(12, 6);
             this.groupBox1.Name = "groupBox1";
-            this.groupBox1.Size = new System.Drawing.Size(493, 136);
+            this.groupBox1.Size = new System.Drawing.Size(493, 154);
             this.groupBox1.TabIndex = 13;
             this.groupBox1.TabStop = false;
             // 
@@ -245,7 +246,7 @@
             this.butExportAndRun.Anchor = System.Windows.Forms.AnchorStyles.Top;
             this.butExportAndRun.Enabled = false;
             this.butExportAndRun.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butExportAndRun.Location = new System.Drawing.Point(414, 153);
+            this.butExportAndRun.Location = new System.Drawing.Point(413, 166);
             this.butExportAndRun.Name = "butExportAndRun";
             this.butExportAndRun.Size = new System.Drawing.Size(197, 27);
             this.butExportAndRun.TabIndex = 14;
@@ -265,11 +266,24 @@
             this.butClose.UseVisualStyleBackColor = true;
             this.butClose.Click += new System.EventHandler(this.butClose_Click);
             // 
+            // chkGltf
+            // 
+            this.chkGltf.AutoSize = true;
+            this.chkGltf.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkGltf.Location = new System.Drawing.Point(30, 133);
+            this.chkGltf.Name = "chkGltf";
+            this.chkGltf.Size = new System.Drawing.Size(135, 17);
+            this.chkGltf.TabIndex = 17;
+            this.chkGltf.Text = "Generate glTF file";
+            this.chkGltf.UseVisualStyleBackColor = true;
+            this.chkGltf.CheckedChanged += new System.EventHandler(this.chkGltf_CheckedChanged);
+            // 
             // ExporterForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ClientSize = new System.Drawing.Size(823, 551);
+            this.Controls.Add(this.chkGltf);
             this.Controls.Add(this.butExportAndRun);
             this.Controls.Add(this.groupBox1);
             this.Controls.Add(this.pictureBox2);
@@ -316,5 +330,6 @@
         private System.Windows.Forms.CheckBox chkOnlySelected;
         private System.Windows.Forms.Button butClose;
         private System.Windows.Forms.CheckBox chkBinary;
+        private System.Windows.Forms.CheckBox chkGltf;
     }
 }

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

@@ -31,7 +31,8 @@ namespace Max2Babylon
             Tools.PrepareCheckBox(chkHidden, Loader.Core.RootNode, "babylonjs_exporthidden");
             Tools.PrepareCheckBox(chkAutoSave, Loader.Core.RootNode, "babylonjs_autosave", 1);
             Tools.PrepareCheckBox(chkOnlySelected, Loader.Core.RootNode, "babylonjs_onlySelected");
-            Tools.PrepareCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary"); 
+            Tools.PrepareCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary");
+            Tools.PrepareCheckBox(chkGltf, Loader.Core.RootNode, "babylonjs_exportGltf");
         }
 
         private void butBrowse_Click(object sender, EventArgs e)
@@ -55,6 +56,7 @@ namespace Max2Babylon
             Tools.UpdateCheckBox(chkAutoSave, Loader.Core.RootNode, "babylonjs_autosave");
             Tools.UpdateCheckBox(chkOnlySelected, Loader.Core.RootNode, "babylonjs_onlySelected");
             Tools.UpdateCheckBox(chkBinary, Loader.Core.RootNode, "babylonjs_binary");
+            Tools.UpdateCheckBox(chkGltf, Loader.Core.RootNode, "babylonjs_exportGltf");
 
             Loader.Core.RootNode.SetLocalData(txtFilename.Text);
 
@@ -121,7 +123,7 @@ namespace Max2Babylon
                 exporter.AutoSave3dsMaxFile = chkAutoSave.Checked;
                 exporter.ExportHiddenObjects = chkHidden.Checked;
                 exporter.CopyTexturesToOutput = chkCopyTextures.Checked;
-                await exporter.ExportAsync(txtFilename.Text, chkManifest.Checked, chkOnlySelected.Checked, chkBinary.Checked, this);
+                await exporter.ExportAsync(txtFilename.Text, chkManifest.Checked, chkOnlySelected.Checked, chkBinary.Checked, chkGltf.Checked, this);
             }
             catch (OperationCanceledException)
             {
@@ -225,5 +227,10 @@ namespace Max2Babylon
         {
             Close();
         }
+
+        private void chkGltf_CheckedChanged(object sender, EventArgs e)
+        {
+
+        }
     }
 }

+ 192 - 193
Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx

@@ -123,199 +123,198 @@
   <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
   <data name="pictureBox2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
     <value>
-        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
-        QmCC
+        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
 </value>
   </data>
 </root>