Bläddra i källkod

Getting things ready for action builder
More SIMD goodness

David Catuhe 10 år sedan
förälder
incheckning
e6e9df65ff
50 ändrade filer med 3935 tillägg och 857 borttagningar
  1. 29 2
      Babylon/Math/babylon.math.js
  2. 35 53
      Babylon/Math/babylon.math.ts
  3. 16 1
      Exporters/3ds Max/BabylonExport.Entities/BabylonAbstractMesh.cs
  4. 39 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonActions.cs
  5. 2 2
      Exporters/3ds Max/BabylonExport.Entities/BabylonAnimation.cs
  6. 1 2
      Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs
  7. 6 19
      Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj
  8. 19 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonIAnimatable.cs
  9. 1 1
      Exporters/3ds Max/BabylonExport.Entities/BabylonLight.cs
  10. 3 16
      Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs
  11. 44 22
      Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs
  12. 12 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonShadowGenerator.cs
  13. 86 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonSound.cs
  14. 36 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonVector3.cs
  15. 79 13
      Exporters/3ds Max/Max2Babylon/2015/Max2Babylon2015.csproj
  16. 1 2
      Exporters/3ds Max/Max2Babylon/2015/packages.config
  17. 0 1
      Exporters/3ds Max/Max2Babylon/BabylonExportActionItem.cs
  18. 24 2
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/action.js
  19. 174 30
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/actionkinds.js
  20. 141 0
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/contextmenu.js
  21. 152 0
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/fonts.css
  22. 200 58
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/index.css
  23. 147 20
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/index.html
  24. 127 33
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/list.js
  25. 307 0
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/parametersManager.js
  26. 216 0
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/utils.js
  27. 517 178
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/viewer.js
  28. 87 0
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/viewsertoolbar.js
  29. 95 0
      Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/BabylonActionsBuilderActionItem.cs
  30. 4 4
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Animation.cs
  31. 44 0
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs
  32. 22 1
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.ShadowGenerator.cs
  33. 2 2
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Skeleton.cs
  34. 14 16
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Texture.cs
  35. 25 3
      Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.cs
  36. 7 1
      Exporters/3ds Max/Max2Babylon/Forms/ActionsBuilderForm.Designer.cs
  37. 81 4
      Exporters/3ds Max/Max2Babylon/Forms/ActionsBuilderForm.cs
  38. 193 305
      Exporters/3ds Max/Max2Babylon/Forms/ExporterForm.resx
  39. 176 4
      Exporters/3ds Max/Max2Babylon/Forms/LightPropertiesForm.Designer.cs
  40. 15 0
      Exporters/3ds Max/Max2Babylon/Forms/LightPropertiesForm.cs
  41. 503 49
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.Designer.cs
  42. 47 0
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.cs
  43. 3 0
      Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.resx
  44. 122 3
      Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.Designer.cs
  45. 23 1
      Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.cs
  46. 3 0
      Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.resx
  47. 5 5
      Exporters/3ds Max/Max2Babylon/GlobalUtility.cs
  48. 19 0
      Exporters/3ds Max/Max2Babylon/Tools/Tools.cs
  49. 29 2
      babylon.2.1-alpha.debug.js
  50. 2 2
      babylon.2.1-alpha.js

+ 29 - 2
Babylon/Math/babylon.math.js

@@ -1897,8 +1897,8 @@ var BABYLON;
             var tmp23 = SIMD.float32x4.shuffle(f, zero, 0, 1, 4, 5);
             var a0 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
             var a1 = SIMD.float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7);
-            var tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7);
-            var tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7);
+            tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7);
+            tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7);
             var a2 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
             var a3 = SIMD.float32x4(0.0, 0.0, 0.0, 1.0);
             // cc.kmMat4Translation(translate, -pEye.x, -pEye.y, -pEye.z);
@@ -2741,6 +2741,33 @@ var BABYLON;
         Matrix.LookAtLHToRef = Matrix.LookAtLHToRefSIMD;
         Vector3.TransformCoordinatesToRef = Vector3.TransformCoordinatesToRefSIMD;
         Vector3.TransformCoordinatesFromFloatsToRef = Vector3.TransformCoordinatesFromFloatsToRefSIMD;
+        Object.defineProperty(BABYLON.Vector3.prototype, "x", {
+            get: function () {
+                return this._data[0];
+            },
+            set: function (value) {
+                if (!this._data) {
+                    this._data = new Float32Array(3);
+                }
+                this._data[0] = value;
+            }
+        });
+        Object.defineProperty(BABYLON.Vector3.prototype, "y", {
+            get: function () {
+                return this._data[1];
+            },
+            set: function (value) {
+                this._data[1] = value;
+            }
+        });
+        Object.defineProperty(BABYLON.Vector3.prototype, "z", {
+            get: function () {
+                return this._data[2];
+            },
+            set: function (value) {
+                this._data[2] = value;
+            }
+        });
     }
 })(BABYLON || (BABYLON = {}));
 //# sourceMappingURL=babylon.math.js.map

+ 35 - 53
Babylon/Math/babylon.math.ts

@@ -786,11 +786,7 @@
             var m1 = SIMD.float32x4.load(transformation.m, 4);
             var m2 = SIMD.float32x4.load(transformation.m, 8);
             var m3 = SIMD.float32x4.load(transformation.m, 12);
-
-            var r = SIMD.float32x4.add(
-                SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(v, 0, 0, 0, 0), m0),
-                    SIMD.float32x4.mul(SIMD.float32x4.swizzle(v, 1, 1, 1, 1), m1)),
-                SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(v, 2, 2, 2, 2), m2), m3));
+            var r = SIMD.float32x4.add(SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(v, 0, 0, 0, 0), m0), SIMD.float32x4.mul(SIMD.float32x4.swizzle(v, 1, 1, 1, 1), m1)), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(v, 2, 2, 2, 2), m2), m3));
             r = SIMD.float32x4.div(r, SIMD.float32x4.swizzle(r, 3, 3, 3, 3));
             SIMD.float32x4.storeXYZ(result, 0, r);
         }
@@ -803,11 +799,7 @@
             var m1 = SIMD.float32x4.load(transformation.m, 4);
             var m2 = SIMD.float32x4.load(transformation.m, 8);
             var m3 = SIMD.float32x4.load(transformation.m, 12);
-
-            var r = SIMD.float32x4.add(
-                SIMD.float32x4.add(SIMD.float32x4.mul(v0, m0),
-                    SIMD.float32x4.mul(v1, m1)),
-                SIMD.float32x4.add(SIMD.float32x4.mul(v2, m2), m3));
+            var r = SIMD.float32x4.add(SIMD.float32x4.add(SIMD.float32x4.mul(v0, m0), SIMD.float32x4.mul(v1, m1)), SIMD.float32x4.add(SIMD.float32x4.mul(v2, m2), m3));
             r = SIMD.float32x4.div(r, SIMD.float32x4.swizzle(r, 3, 3, 3, 3));
             SIMD.float32x4.storeXYZ(result, 0, r);
         }
@@ -2387,18 +2379,14 @@
             tmp = SIMD.float32x4.mul(up, up);
             tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3)));
             up = SIMD.float32x4.mul(up, SIMD.float32x4.reciprocalSqrt(tmp));
-
             // cc.kmVec3Cross(s, f, up);
-            var s = SIMD.float32x4.sub(SIMD.float32x4.mul(SIMD.float32x4.swizzle(f, 1, 2, 0, 3), SIMD.float32x4.swizzle(up, 2, 0, 1, 3)),
-                SIMD.float32x4.mul(SIMD.float32x4.swizzle(f, 2, 0, 1, 3), SIMD.float32x4.swizzle(up, 1, 2, 0, 3)));
+            var s = SIMD.float32x4.sub(SIMD.float32x4.mul(SIMD.float32x4.swizzle(f, 1, 2, 0, 3), SIMD.float32x4.swizzle(up, 2, 0, 1, 3)), SIMD.float32x4.mul(SIMD.float32x4.swizzle(f, 2, 0, 1, 3), SIMD.float32x4.swizzle(up, 1, 2, 0, 3)));
             // cc.kmVec3Normalize(s, s);
             tmp = SIMD.float32x4.mul(s, s);
             tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3)));
             s = SIMD.float32x4.mul(s, SIMD.float32x4.reciprocalSqrt(tmp));
-
             // cc.kmVec3Cross(u, s, f);
-            var u = SIMD.float32x4.sub(SIMD.float32x4.mul(SIMD.float32x4.swizzle(s, 1, 2, 0, 3), SIMD.float32x4.swizzle(f, 2, 0, 1, 3)),
-                SIMD.float32x4.mul(SIMD.float32x4.swizzle(s, 2, 0, 1, 3), SIMD.float32x4.swizzle(f, 1, 2, 0, 3)));
+            var u = SIMD.float32x4.sub(SIMD.float32x4.mul(SIMD.float32x4.swizzle(s, 1, 2, 0, 3), SIMD.float32x4.swizzle(f, 2, 0, 1, 3)), SIMD.float32x4.mul(SIMD.float32x4.swizzle(s, 2, 0, 1, 3), SIMD.float32x4.swizzle(f, 1, 2, 0, 3)));
             // cc.kmVec3Normalize(s, s);
             tmp = SIMD.float32x4.mul(s, s);
             tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3)));
@@ -2414,58 +2402,28 @@
             //pOut.mat[2] = -f.x;
             //pOut.mat[6] = -f.y;
             //pOut.mat[10] = -f.z;
+
             var zero = SIMD.float32x4.splat(0.0);
             s = SIMD.float32x4.neg(s);
             var tmp01 = SIMD.float32x4.shuffle(s, u, 0, 1, 4, 5);
             var tmp23 = SIMD.float32x4.shuffle(f, zero, 0, 1, 4, 5);
             var a0 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
             var a1 = SIMD.float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7);
-
-            var tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7);
-            var tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7);
+            tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7);
+            tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7);
             var a2 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
             var a3 = SIMD.float32x4(0.0, 0.0, 0.0, 1.0);
-
             // cc.kmMat4Translation(translate, -pEye.x, -pEye.y, -pEye.z);
             var b0 = SIMD.float32x4(1.0, 0.0, 0.0, 0.0);
             var b1 = SIMD.float32x4(0.0, 1.0, 0.0, 0.0);
             var b2 = SIMD.float32x4(0.0, 0.0, 1.0, 0.0);
             var b3 = SIMD.float32x4.neg(eye);
             b3 = SIMD.float32x4.withW(b3, 1.0);
-
             // cc.kmMat4Multiply(pOut, pOut, translate);
-            SIMD.float32x4.store(out, 0, SIMD.float32x4.add(
-                SIMD.float32x4.mul(
-                    SIMD.float32x4.swizzle(b0, 0, 0, 0, 0), a0),
-                SIMD.float32x4.add(
-                    SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 1, 1, 1, 1), a1),
-                    SIMD.float32x4.add(
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 2, 2, 2, 2), a2),
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 3, 3, 3, 3), a3)))));
-            SIMD.float32x4.store(out, 4, SIMD.float32x4.add(
-                SIMD.float32x4.mul(
-                    SIMD.float32x4.swizzle(b1, 0, 0, 0, 0), a0),
-                SIMD.float32x4.add(
-                    SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 1, 1, 1, 1), a1),
-                    SIMD.float32x4.add(
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 2, 2, 2, 2), a2),
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 3, 3, 3, 3), a3)))));
-            SIMD.float32x4.store(out, 8, SIMD.float32x4.add(
-                SIMD.float32x4.mul(
-                    SIMD.float32x4.swizzle(b2, 0, 0, 0, 0), a0),
-                SIMD.float32x4.add(
-                    SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 1, 1, 1, 1), a1),
-                    SIMD.float32x4.add(
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 2, 2, 2, 2), a2),
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 3, 3, 3, 3), a3)))));
-            SIMD.float32x4.store(out, 12, SIMD.float32x4.add(
-                SIMD.float32x4.mul(
-                    SIMD.float32x4.swizzle(b3, 0, 0, 0, 0), a0),
-                SIMD.float32x4.add(
-                    SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 1, 1, 1, 1), a1),
-                    SIMD.float32x4.add(
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 2, 2, 2, 2), a2),
-                        SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 3, 3, 3, 3), a3)))));
+            SIMD.float32x4.store(out, 0, SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 0, 0, 0, 0), a0), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 1, 1, 1, 1), a1), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 2, 2, 2, 2), a2), SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 3, 3, 3, 3), a3)))));
+            SIMD.float32x4.store(out, 4, SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 0, 0, 0, 0), a0), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 1, 1, 1, 1), a1), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 2, 2, 2, 2), a2), SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 3, 3, 3, 3), a3)))));
+            SIMD.float32x4.store(out, 8, SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 0, 0, 0, 0), a0), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 1, 1, 1, 1), a1), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 2, 2, 2, 2), a2), SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 3, 3, 3, 3), a3)))));
+            SIMD.float32x4.store(out, 12, SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 0, 0, 0, 0), a0), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 1, 1, 1, 1), a1), SIMD.float32x4.add(SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 2, 2, 2, 2), a2), SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 3, 3, 3, 3), a3)))));
         }
 
 
@@ -3464,5 +3422,29 @@
         Matrix.LookAtLHToRef = <any>Matrix.LookAtLHToRefSIMD;
         Vector3.TransformCoordinatesToRef = <any>Vector3.TransformCoordinatesToRefSIMD;
         Vector3.TransformCoordinatesFromFloatsToRef = <any>Vector3.TransformCoordinatesFromFloatsToRefSIMD;
+
+        Object.defineProperty(BABYLON.Vector3.prototype, "x", {
+            get: function () { return this._data[0]; },
+            set: function (value: number) {
+                if (!this._data) {
+                    this._data = new Float32Array(3);
+                }
+                this._data[0] = value;
+            }
+        });
+
+        Object.defineProperty(BABYLON.Vector3.prototype, "y", {
+            get: function () { return this._data[1]; },
+            set: function (value: number) {
+                this._data[1] = value;
+            }
+        });
+
+        Object.defineProperty(BABYLON.Vector3.prototype, "z", {
+            get: function () { return this._data[2]; },
+            set: function (value: number) {
+                this._data[2] = value;
+            }
+        });
     }
 }

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

@@ -3,7 +3,7 @@
 namespace BabylonExport.Entities
 {
     [DataContract]
-    public class BabylonAbstractMesh
+    public class BabylonAbstractMesh: BabylonIAnimatable
     {
         [DataMember]
         public string name { get; set; }
@@ -21,8 +21,23 @@ namespace BabylonExport.Entities
         public float[] rotationQuaternion { get; set; }
 
         [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; }
+
         public BabylonAbstractMesh()
         {
             position = new[] { 0f, 0f, 0f };

+ 39 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonActions.cs

@@ -0,0 +1,39 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonActionsProperties
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public string value { get; set; }
+
+        [DataMember]
+        public string targetType { get; set; }
+    }
+
+    [DataContract]
+    public class BabylonActions
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public int type { get; set; }
+
+        [DataMember]
+        public bool detached { get; set; }
+
+        [DataMember]
+        public BabylonActions[] children { get; set; }
+
+        [DataMember]
+        public BabylonActions[] combine { get; set; }
+
+        [DataMember]
+        public BabylonActionsProperties[] properties { get; set; }
+    }
+}

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

@@ -12,10 +12,10 @@ namespace BabylonExport.Entities
         public string property { get; set; }
 
         [DataMember]
-        public DataType dataType { get; set; }
+        public int dataType { get; set; }
 
         [DataMember]
-        public LoopBehavior loopBehavior { get; set; }
+        public int loopBehavior { get; set; }
 
         [DataMember]
         public int framePerSecond { get; set; }

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

@@ -3,7 +3,7 @@
 namespace BabylonExport.Entities
 {
     [DataContract]
-    public class BabylonCamera
+    public class BabylonCamera : BabylonIAnimatable
     {
         [DataMember]
         public string name { get; set; }
@@ -68,7 +68,6 @@ namespace BabylonExport.Entities
         [DataMember]
         public BabylonAnimation[] animations { get; set; }
 
-
         public BabylonCamera()
         {
             position = new[] { 0f, 0f, 0f };

+ 6 - 19
Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj

@@ -9,14 +9,13 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>BabylonExport.Entities</RootNamespace>
     <AssemblyName>BabylonExport.Entities</AssemblyName>
-    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <SccProjectName>SAK</SccProjectName>
     <SccLocalPath>SAK</SccLocalPath>
     <SccAuxPath>SAK</SccAuxPath>
     <SccProvider>SAK</SccProvider>
-    <TargetFrameworkProfile />
-    <NuGetPackageImportStamp>a3421fc4</NuGetPackageImportStamp>
+    <TargetFrameworkProfile>Client</TargetFrameworkProfile>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -36,26 +35,22 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="SharpDX">
-      <HintPath>$(SharpDXPackageBinDir)\SharpDX.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Drawing" />
     <Reference Include="System.Runtime.Serialization" />
-    <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
-    <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="BabylonActions.cs" />
     <Compile Include="BabylonAnimation.cs" />
     <Compile Include="BabylonAnimationKey.cs" />
     <Compile Include="BabylonBone.cs" />
     <Compile Include="BabylonCamera.cs" />
     <Compile Include="BabylonFresnelParameters.cs" />
+    <Compile Include="BabylonIAnimatable.cs" />
     <Compile Include="BabylonLensFlare.cs" />
     <Compile Include="BabylonLensFlareSystem.cs" />
     <Compile Include="BabylonLight.cs" />
@@ -67,21 +62,13 @@
     <Compile Include="BabylonScene.cs" />
     <Compile Include="BabylonShadowGenerator.cs" />
     <Compile Include="BabylonSkeleton.cs" />
+    <Compile Include="BabylonSound.cs" />
     <Compile Include="BabylonSubMesh.cs" />
     <Compile Include="BabylonTexture.cs" />
+    <Compile Include="BabylonVector3.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
-  <ItemGroup>
-    <None Include="packages.config" />
-  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Import Project="..\packages\SharpDX.2.6.3\build\SharpDX.targets" Condition="Exists('..\packages\SharpDX.2.6.3\build\SharpDX.targets')" />
-  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
-    <PropertyGroup>
-      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
-    </PropertyGroup>
-    <Error Condition="!Exists('..\packages\SharpDX.2.6.3\build\SharpDX.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\SharpDX.2.6.3\build\SharpDX.targets'))" />
-  </Target>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 19 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonIAnimatable.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BabylonExport.Entities
+{
+    public interface BabylonIAnimatable
+    {
+        BabylonAnimation[] animations { get; set; }
+        bool autoAnimate { get; set; }
+
+        int autoAnimateFrom { get; set; }
+
+        int autoAnimateTo { get; set; }
+
+        bool autoAnimateLoop { get; set; }
+    }
+}

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

@@ -3,7 +3,7 @@
 namespace BabylonExport.Entities
 {
     [DataContract]
-    public class BabylonLight
+    public class BabylonLight : BabylonIAnimatable
     {
         [DataMember]
         public string name { get; set; }

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

@@ -5,7 +5,6 @@ namespace BabylonExport.Entities
     [DataContract]
     public class BabylonMesh : BabylonAbstractMesh
     {
-
         [DataMember]
         public string id { get; set; }
 
@@ -58,11 +57,11 @@ namespace BabylonExport.Entities
         public bool checkCollisions { get; set; }
 
         [DataMember]
-        public bool receiveShadows { get; set; }
-
+        public bool receiveShadows { get; set; }    
+    
         [DataMember]
         public bool infiniteDistance { get; set; }
-
+        
         [DataMember]
         public int billboardMode { get; set; }
 
@@ -79,18 +78,6 @@ namespace BabylonExport.Entities
         public int skeletonId { get; set; }
 
         [DataMember]
-        public bool autoAnimate { get; set; }
-
-        [DataMember]
-        public int autoAnimateFrom { get; set; }
-
-        [DataMember]
-        public int autoAnimateTo { get; set; }
-
-        [DataMember]
-        public bool autoAnimateLoop { get; set; }
-
-        [DataMember]
         public bool showBoundingBox { get; set; }
 
         [DataMember]

+ 44 - 22
Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Runtime.Serialization;
-using SharpDX;
 
 namespace BabylonExport.Entities
 {
@@ -49,6 +48,9 @@ namespace BabylonExport.Entities
         public BabylonMesh[] meshes { get; set; }
 
         [DataMember]
+        public BabylonSound[] sounds { get; set; }
+
+        [DataMember]
         public BabylonMaterial[] materials { get; set; }
 
         [DataMember]
@@ -65,13 +67,17 @@ namespace BabylonExport.Entities
 
         [DataMember]
         public BabylonSkeleton[] skeletons { get; set; }
-        
-        public Vector3 MaxVector { get; set; }
-        public Vector3 MinVector { get; set; }
+
+        [DataMember]
+        public BabylonActions actions { get; set; }
+
+        public BabylonVector3 MaxVector { get; set; }
+        public BabylonVector3 MinVector { get; set; }
 
         public string OutputPath { get; private set; }
 
         public List<BabylonMesh> MeshesList { get; private set; }
+        public List<BabylonSound> SoundsList { get; private set; }
         public List<BabylonCamera> CamerasList { get; private set; }
         public List<BabylonLight> LightsList { get; private set; }
         public List<BabylonMaterial> MaterialsList { get; private set; }
@@ -92,17 +98,22 @@ namespace BabylonExport.Entities
             MultiMaterialsList = new List<BabylonMultiMaterial>();
             ShadowGeneratorsList = new List<BabylonShadowGenerator>();
             SkeletonsList = new List<BabylonSkeleton>();
+            SoundsList = new List<BabylonSound>();
 
             // Default values
             autoClear = true;
             clearColor = new[] { 0.2f, 0.2f, 0.3f };
-            ambientColor = new[] {0f, 0f, 0f };
-            gravity = new[] {0f, 0f, -0.9f};
+            ambientColor = new[] { 0f, 0f, 0f };
+            gravity = new[] { 0f, 0f, -0.9f };
+
+            MaxVector = new BabylonVector3 { X = float.MinValue, Y = float.MinValue, Z = float.MinValue };
+            MinVector = new BabylonVector3 { X = float.MaxValue, Y = float.MaxValue, Z = float.MaxValue };
         }
 
         public void Prepare(bool generateDefaultLight = true)
         {
             meshes = MeshesList.ToArray();
+            sounds = SoundsList.ToArray();
 
             materials = MaterialsList.ToArray();
             multiMaterials = MultiMaterialsList.ToArray();
@@ -111,15 +122,15 @@ namespace BabylonExport.Entities
 
             if (CamerasList.Count == 0)
             {
-                var camera = new BabylonCamera {name = "Default camera", id = Guid.NewGuid().ToString()};
+                var camera = new BabylonCamera { name = "Default camera", id = Guid.NewGuid().ToString() };
 
                 var distanceVector = MaxVector - MinVector;
-                var midPoint = MinVector +distanceVector / 2;
+                var midPoint = MinVector + distanceVector / 2;
                 camera.target = midPoint.ToArray();
                 camera.position = (midPoint + distanceVector).ToArray();
 
                 var distance = distanceVector.Length();
-                camera.speed =  distance/ 50.0f;
+                camera.speed = distance / 50.0f;
                 camera.maxZ = distance * 4f;
 
                 camera.minZ = distance < 100.0f ? 0.1f : 1.0f;
@@ -129,18 +140,19 @@ namespace BabylonExport.Entities
 
             if (LightsList.Count == 0 && generateDefaultLight)
             {
-                var light = new BabylonLight {name = "Default light", id = Guid.NewGuid().ToString()};
-
-                var midPoint = MinVector + (MaxVector - MinVector) / 2;
-                light.type = 0;
-                light.position = (midPoint + (MaxVector - MinVector)).ToArray();
-
-                light.diffuse = new Vector3(1, 1, 1).ToArray();
-                light.specular = new Vector3(1, 1, 1).ToArray();
+                var light = new BabylonLight
+                {
+                    name = "Default light",
+                    id = Guid.NewGuid().ToString(),
+                    type = 3,
+                    groundColor = new float[] {0, 0, 0},
+                    direction = new[] {0, 1.0f, 0},
+                    intensity = 1
+                };
 
                 LightsList.Add(light);
             }
-            
+
             cameras = CamerasList.ToArray();
             lights = LightsList.ToArray();
 
@@ -150,14 +162,24 @@ namespace BabylonExport.Entities
             }
         }
 
-        public void AddTexture(string diffuseTexture)
+        public void AddTexture(string texture)
         {
-            if (exportedTextures.Contains(diffuseTexture))
+            if (exportedTextures.Contains(texture))
                 return;
 
-            exportedTextures.Add(diffuseTexture);
+            exportedTextures.Add(texture);
+
+            File.Copy(texture, Path.Combine(OutputPath, Path.GetFileName(texture)), true);
+        }
+
+        public bool AddTextureCube(string textureName)
+        {
+            if (exportedTextures.Contains(textureName))
+                return false;
+
+            exportedTextures.Add(textureName);
 
-            File.Copy(diffuseTexture, Path.Combine(OutputPath, Path.GetFileName(diffuseTexture)), true);
+            return true;
         }
     }
 }

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

@@ -9,6 +9,9 @@ namespace BabylonExport.Entities
         public int mapSize { get; set; }
 
         [DataMember]
+        public float bias { get; set; }
+
+        [DataMember]
         public string lightId { get; set; }
 
         [DataMember]
@@ -18,6 +21,15 @@ namespace BabylonExport.Entities
         public bool usePoissonSampling { get; set; }
 
         [DataMember]
+        public bool useBlurVarianceShadowMap { get; set; }
+
+        [DataMember]
+        public float blurScale { get; set; }
+
+        [DataMember]
+        public float blurBoxOffset { get; set; }
+
+        [DataMember]
         public string[] renderList { get; set; }
 
     }

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

@@ -0,0 +1,86 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonSound
+    {
+        [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public float volume { get; set; }
+
+        [DataMember]
+        public float playbackRate { get; set; }
+
+        [DataMember]
+        public bool autoplay { get; set; }
+
+        [DataMember]
+        public bool loop { get; set; }
+
+        [DataMember]
+        public int soundTrackId { get; set; }
+
+        [DataMember]
+        public bool spatialSound { get; set; }
+
+        [DataMember]
+        public float[] position { get; set; }
+
+        [DataMember]
+        public float refDistance { get; set; }
+
+        [DataMember]
+        public float rolloffFactor { get; set; }
+
+        [DataMember]
+        public float maxDistance { get; set; }
+
+        [DataMember]
+        public string distanceModel { get; set; }
+
+        [DataMember]
+        public string panningModel { get; set; }
+
+        [DataMember]
+        public bool isDirectional { get; set; }
+
+        [DataMember]
+        public float coneInnerAngle { get; set; }
+
+        [DataMember]
+        public float coneOuterAngle { get; set; }
+
+        [DataMember]
+        public float coneOuterGain { get; set; }
+
+        [DataMember]
+        public string connectedMeshId { get; set; }
+
+        [DataMember]
+        public float[] localDirectionToMesh { get; set; }
+
+        public BabylonSound()
+        {
+            volume = 1;
+            playbackRate = 1;
+            autoplay = false;
+            loop = false;
+            soundTrackId = -1;
+            spatialSound = false;
+            position = new[] { 0f, 0f, 0f };
+            refDistance = 1;
+            rolloffFactor = 1;
+            maxDistance = 100;
+            distanceModel = "linear";
+            panningModel = "equalpower";
+            isDirectional = false;
+            coneInnerAngle = 360;
+            coneOuterAngle = 360;
+            coneOuterGain = 0;
+            localDirectionToMesh = new[] { 1f, 0f, 0f };
+        }
+    }
+}

+ 36 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonVector3.cs

@@ -0,0 +1,36 @@
+using System;
+
+namespace BabylonExport.Entities
+{
+    public class BabylonVector3
+    {
+        public float X { get; set; }
+        public float Y { get; set; }
+        public float Z { get; set; }
+
+        public float[] ToArray()
+        {
+            return new [] {X, Y, Z};
+        }
+
+        public float Length()
+        {
+            return (float)Math.Sqrt(X * X + Y * Y + Z * Z);
+        }
+
+        public static BabylonVector3 operator +(BabylonVector3 a, BabylonVector3 b)
+        {
+            return new BabylonVector3 {X = a.X + b.X, Y = a.Y + b.Y, Z = a.Z + b.Z};
+        }
+
+        public static BabylonVector3 operator -(BabylonVector3 a, BabylonVector3 b)
+        {
+            return new BabylonVector3 { X = a.X - b.X, Y = a.Y - b.Y, Z = a.Z - b.Z };
+        }
+
+        public static BabylonVector3 operator /(BabylonVector3 a, float b)
+        {
+            return new BabylonVector3 { X = a.X / b, Y = a.Y / b, Z = a.Z / b };
+        }
+    }
+}

+ 79 - 13
Exporters/3ds Max/Max2Babylon/2015/Max2Babylon2015.csproj

@@ -17,7 +17,7 @@
     <SccAuxPath>SAK</SccAuxPath>
     <SccProvider>SAK</SccProvider>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
-    <NuGetPackageImportStamp>847376a2</NuGetPackageImportStamp>
+    <RestorePackages>true</RestorePackages>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -38,25 +38,19 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <Prefer32Bit>false</Prefer32Bit>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="Autodesk.Max, Version=17.0.630.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>Refs\2015\Autodesk.Max.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="BabylonFileConverter, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\Refs\BabylonFileConverter.dll</HintPath>
+      <HintPath>..\..\..\..\..\Repos\Babylon.js\Exporters\3ds Max\Max2Babylon\2015\Refs\Autodesk.Max.dll</HintPath>
     </Reference>
-    <Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+    <Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\..\packages\Newtonsoft.Json.6.0.6\lib\net45\Newtonsoft.Json.dll</HintPath>
+      <HintPath>..\..\packages\Newtonsoft.Json.6.0.7\lib\net45\Newtonsoft.Json.dll</HintPath>
     </Reference>
     <Reference Include="SharpDX, Version=2.4.2.0, Culture=neutral, PublicKeyToken=627a3d6d1956f55a, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>$(SharpDXPackageBinDir)\SharpDX.dll</HintPath>
+      <HintPath>..\..\BabylonExport.Core\Refs\SharpDX.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
@@ -82,6 +76,12 @@
     <Compile Include="..\Descriptor.cs">
       <Link>Descriptor.cs</Link>
     </Compile>
+    <Compile Include="..\Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs">
+      <Link>Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\ActionBuilder\BabylonExporter.Action.cs">
+      <Link>Exporter\ActionBuilder\BabylonExporter.Action.cs</Link>
+    </Compile>
     <Compile Include="..\Exporter\BabylonExporter.Animation.cs">
       <Link>Exporter\BabylonExporter.Animation.cs</Link>
     </Compile>
@@ -112,6 +112,14 @@
     <Compile Include="..\Exporter\GlobalVertex.cs">
       <Link>Exporter\GlobalVertex.cs</Link>
     </Compile>
+    <Compile Include="..\Forms\ActionsBuilderForm.cs">
+      <Link>Forms\ActionsBuilderForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ActionsBuilderForm.designer.cs">
+      <Link>Forms\ActionsBuilderForm.designer.cs</Link>
+      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
+    </Compile>
     <Compile Include="..\Forms\CameraPropertiesForm.cs">
       <Link>Forms\CameraPropertiesForm.cs</Link>
       <SubType>Form</SubType>
@@ -186,9 +194,49 @@
     </Compile>
   </ItemGroup>
   <ItemGroup>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\action.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\action.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\actionkinds.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\actionkinds.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\contextmenu.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\contextmenu.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\fonts.css">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\fonts.css</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\index.css">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\index.css</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\index.html">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\index.html</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\list.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\list.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\parametersManager.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\parametersManager.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\raphael.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\raphael.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\utils.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\utils.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\viewer.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\viewer.js</Link>
+    </Content>
+    <Content Include="..\Exporter\ActionBuilder\ActionsBuilder\viewsertoolbar.js">
+      <Link>Exporter\ActionBuilder\ActionsBuilder\viewsertoolbar.js</Link>
+    </Content>
     <Content Include="Refs\Autodesk.Max.dll" />
   </ItemGroup>
   <ItemGroup>
+    <EmbeddedResource Include="..\Forms\ActionsBuilderForm.resx">
+      <Link>Forms\ActionsBuilderForm.resx</Link>
+      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="..\Forms\CameraPropertiesForm.resx">
       <Link>Forms\CameraPropertiesForm.resx</Link>
       <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
@@ -219,22 +267,40 @@
     </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\Babylon.csproj">
+      <Project>{ba85b096-ddb8-4324-bb47-5f4ec2c5db6a}</Project>
+      <Name>Babylon</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\BabylonExport.Entities\BabylonExport.Entities.csproj">
       <Project>{6150965a-658c-4263-89ad-4f980eb0675d}</Project>
       <Name>BabylonExport.Entities</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\BabylonFileConverter\BabylonFileConverter.csproj">
+      <Project>{a6b76356-1d1c-4c82-8199-a6406da85a95}</Project>
+      <Name>BabylonFileConverter</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Import Project="..\..\packages\SharpDX.2.6.3\build\SharpDX.targets" Condition="Exists('..\..\packages\SharpDX.2.6.3\build\SharpDX.targets')" />
+  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>
       <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
     </PropertyGroup>
-    <Error Condition="!Exists('..\..\packages\SharpDX.2.6.3\build\SharpDX.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\SharpDX.2.6.3\build\SharpDX.targets'))" />
+    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
   </Target>
+  <PropertyGroup>
+    <PostBuildEvent>mkdir "$(OutDir)BabylonActionsBuilder"
+mkdir "$(OutDir)BabylonActionsBuilder\fonts\SinkinSans"
+copy "$(SolutionDir)Max2Babylon\Exporter\ActionBuilder\ActionsBuilder\" "$(OutDir)BabylonActionsBuilder\"
+copy "$(SolutionDir)Max2Babylon\Exporter\ActionBuilder\ActionsBuilder\fonts\SinkinSans" "$(OutDir)BabylonActionsBuilder\fonts\SinkinSans"
+copy "$(SolutionDir)babylon.max.js" "$(OutDir)BabylonActionsBuilder\babylon.max.js"</PostBuildEvent>
+  </PropertyGroup>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 1 - 2
Exporters/3ds Max/Max2Babylon/2015/packages.config

@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net45" />
-  <package id="SharpDX" version="2.6.3" targetFramework="net45" />
+  <package id="Newtonsoft.Json" version="6.0.7" targetFramework="net45" />
 </packages>

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

@@ -20,7 +20,6 @@ namespace Max2Babylon
 
         public void Close()
         {
-
             if (form == null)
             {
                 return;

+ 24 - 2
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/action.js

@@ -7,13 +7,21 @@ var AB;
 
     var Node = (function () {
         function Node() {
+            // Members
             this.rect = null;
             this.text = null;
             this.line = null;
 
             this.action = null;
+
+            this.detached = false;
+            this.minimized = false;
         }
 
+        // Get node's object attribute
+        // element: The element to get the attribute
+        // attribute: the attribute name "text, "width", etc.
+        // value: optional, if not reading mode but writing mode
         Node.prototype.attr = function(element, attribute, value) {
             if (value)
                 element.attr(attribute, value);
@@ -21,6 +29,9 @@ var AB;
                 return element.attr(attribute);
         }
 
+        // Returns the point at (x, y) is inside the node
+        // x: the x position of the point
+        // y: the y position of the point
         Node.prototype.isPointInside = function (x, y) {
             return this.rect.isPointInside(x, y) || this.text.isPointInside(x, y);
         }
@@ -35,12 +46,20 @@ var AB;
             this.children = new Array();
             this.node = node;
 
-            this.name = '';
+            // Action
+            this.name = "";
             this.type = AB.ActionsBuilder.Type.OBJECT;
             this.propertiesResults = new Array();
             this.properties = new Array();
+
+            // Extra
+            this.combine = false;
+            this.combineArray = new Array();
+            this.hub = null;
         }
 
+        // Adds a child to the action
+        // object: the child
         Action.prototype.addChild = function (object) {
             if (object == null)
                 return false;
@@ -51,10 +70,12 @@ var AB;
             return true;
         }
 
+        // Removes a child from the action
+        // object: the child to remove
         Action.prototype.removeChild = function (object) {
             var indice = this.children.indexOf(object);
 
-            if (indice != 1) {
+            if (indice != -1) {
                 this.children.splice(indice, 1);
                 return true;
             }
@@ -62,6 +83,7 @@ var AB;
             return false;
         }
 
+        // Clears all the children of the action
         Action.prototype.clearChildren = function () {
             this.children = new Array();
         }

+ 174 - 30
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/actionkinds.js

@@ -1,17 +1,18 @@
 /// <reference path="raphael.js" />
 /// <reference path="viewer.js" />
 /// <reference path="action.js" />
+/// <reference path="babylon.max.js" />
 
 var AB;
 (function (AB) {
 
     var directActionTemplate = function (parameters, secondArgument) {
         var template = [
-            { text: 'target', value: 'Object name?' }
+            { text: "target", value: "Object name?", targetType: "SceneProperties" }
         ];
 
         if (secondArgument)
-            template.push({text: secondArgument, value: 'value...'});
+            template.push({text: secondArgument, value: "value..."});
 
         if (parameters)
             template.push.apply(template, parameters);
@@ -20,13 +21,39 @@ var AB;
     }
 
     var directActionWidthPropertyPath = function (parameters) {
-        return directActionTemplate(parameters, 'propertyPath');
+        return directActionTemplate(parameters, "propertyPath");
     }
 
     var ActionsBuilder = (function () {
         function ActionsBuilder()
         { }
 
+        var engine = new BABYLON.Engine(document.getElementById("renderCanvas"), false);
+        var scene = new BABYLON.Scene(engine);
+        var camera = new BABYLON.Camera("empty_camera", BABYLON.Vector3.Zero(), scene);
+        var light = new BABYLON.PointLight("empty_light", BABYLON.Vector3.Zero(), scene);
+        var mesh = new BABYLON.Mesh("empty_mesh", scene);
+
+        ActionsBuilder.Engine = function () {
+            return engine;
+        }
+
+        ActionsBuilder.Scene = function () {
+            return scene;
+        }
+
+        ActionsBuilder.Camera = function () {
+            return camera;
+        }
+
+        ActionsBuilder.Light = function () {
+            return light;
+        }
+
+        ActionsBuilder.Mesh = function () {
+            return mesh;
+        }
+
         //
         // Types
         //
@@ -38,50 +65,167 @@ var AB;
         ActionsBuilder.Type.SCENE = 4;
 
         //
+        // Data types names
+        //
+        ActionsBuilder.DataTypesNames = ActionsBuilder.DataTypesNames || new Array();
+        ActionsBuilder.DataTypesNames.push({ name: "Scene", data: "SceneProperties" });
+        ActionsBuilder.DataTypesNames.push({ name: "Camera", data: "CameraProperties" });
+        ActionsBuilder.DataTypesNames.push({ name: "Light", data: "LightProperties" });
+        ActionsBuilder.DataTypesNames.push({ name: "Mesh", data: "MeshProperties" });
+
+        //
+        // Autorized data types
+        //
+        ActionsBuilder.Types = ActionsBuilder.Types || new Array();
+        ActionsBuilder.Types.push("Boolean");
+        ActionsBuilder.Types.push("Number");
+        ActionsBuilder.Types.push("Vector2");
+        ActionsBuilder.Types.push("Vector3");
+        ActionsBuilder.Types.push("String");
+
+        // Tests if the property type is in ActionsBuilder.Types
+        var testInstanceOf = function (object, propertyName) {
+            if (object == null || object.constructor == null)
+                return false;
+
+            if (propertyName.length > 0 && propertyName[0] == "_")
+                return false;
+
+            var name = object.constructor.toString().match(/function (\w*)/)[1];
+
+            for (var i = 0; i < ActionsBuilder.Types.length; i++) {
+                if (name == ActionsBuilder.Types[i])
+                    return true;
+            }
+
+            return false;
+        }
+
+        //
+        // Scene properties
+        //
+        ActionsBuilder.SceneProperties = ActionsBuilder.SceneProperties || new Array();
+        for (var thing in scene) {
+            if (testInstanceOf(scene[thing], thing)) {
+                ActionsBuilder.SceneProperties.push({ name: thing, type: typeof (scene[thing]) });
+            }
+        }
+
+        //
+        // Camera properties
+        //
+        ActionsBuilder.CameraProperties = ActionsBuilder.CameraProperties || new Array();
+        for (var thing in camera) {
+            if (testInstanceOf(camera[thing], thing)) {
+                ActionsBuilder.CameraProperties.push({ name: thing, type: typeof (camera[thing]) });
+            }
+        }
+
+        //
+        // Light properties
+        //
+        ActionsBuilder.LightProperties = ActionsBuilder.LightProperties || new Array();
+        for (var thing in light) {
+            if (testInstanceOf(light[thing], thing)) {
+                ActionsBuilder.LightProperties.push({ name: thing, type: typeof (light[thing]) });
+            }
+        }
+
+        //
+        // Mesh properties
+        //
+        ActionsBuilder.MeshProperties = ActionsBuilder.MeshProperties || new Array();
+        for (var thing in mesh) {
+            if (testInstanceOf(mesh[thing], thing)) {
+                ActionsBuilder.MeshProperties.push({ name: thing, type: typeof (mesh[thing]) });
+            }
+        }
+
+        //
+        // Scene properties (meshes, lights, etc)
+        //
+        ActionsBuilder.MeshesList = ActionsBuilder.MeshesList || new Array();
+        ActionsBuilder.LightsList = ActionsBuilder.LightsList || new Array();
+        ActionsBuilder.CamerasList = ActionsBuilder.CamerasList || new Array();
+        ActionsBuilder.SoundsList = ActionsBuilder.SoundsList || new Array();
+
+        //
         // Triggers
         //
         ActionsBuilder.Trigger = ActionsBuilder.Trigger || {};
-        ActionsBuilder.Trigger[0] = { name: 'NothingTrigger', properties: [] };
-        ActionsBuilder.Trigger[1] = { name: 'OnPickTrigger', properties: [] };
-        ActionsBuilder.Trigger[2] = { name: 'OnLeftPickTrigger', properties: [] };
-        ActionsBuilder.Trigger[3] = { name: 'OnRightPickTrigger', properties: [] };
-        ActionsBuilder.Trigger[4] = { name: 'OnCenterPickTrigger', properties: [] };
-        ActionsBuilder.Trigger[5] = { name: 'OnPointerOverTrigger', properties: [] };
-        ActionsBuilder.Trigger[6] = { name: 'OnPointerOutTrigger', properties: [] };
-        ActionsBuilder.Trigger[7] = { name: 'OnEveryFrameTrigger', properties: [] };
-        ActionsBuilder.Trigger[8] = { name: 'OnIntersectionEnterTrigger', properties: [{ text: 'parameter', value: 'Object name?' }] };
-        ActionsBuilder.Trigger[9] = { name: 'OnIntersectionExitTrigger', properties: [{ text: 'parameter', value: 'Object name?' }] };
-        ActionsBuilder.Trigger[10] = { name: 'OnKeyDownTrigger', properties: [{ text: 'parameter:', value: '' }] };
-        ActionsBuilder.Trigger[11] = { name: 'OnKeyUpTrigger', properties: [{ text: 'parameter:', value: '' }] };
+        ActionsBuilder.Trigger[0] = { name: "NothingTrigger", properties: [] };
+        ActionsBuilder.Trigger[1] = { name: "OnPickTrigger", properties: [], description: "When the user clicks" };
+        ActionsBuilder.Trigger[2] = { name: "OnLeftPickTrigger", properties: [], description: "When the user left clicks" };
+        ActionsBuilder.Trigger[3] = { name: "OnRightPickTrigger", properties: [], description: "When the user right clicks" };
+        ActionsBuilder.Trigger[4] = { name: "OnCenterPickTrigger", properties: [], description: "When the user center clicks (mouse wheel)" };
+        ActionsBuilder.Trigger[5] = { name: "OnPointerOverTrigger", properties: [], description: "When the user's mouse is on the object" };
+        ActionsBuilder.Trigger[6] = { name: "OnPointerOutTrigger", properties: [], description: "When the user's mouse is out of the object" };
+        ActionsBuilder.Trigger[7] = { name: "OnEveryFrameTrigger", properties: [], description: "Called each frame (only on scene)" };
+        ActionsBuilder.Trigger[8] = { name: "OnIntersectionEnterTrigger", properties: [{ text: "parameter", value: "Object name?" }], description: "When the object intersects an other object" };
+        ActionsBuilder.Trigger[9] = { name: "OnIntersectionExitTrigger", properties: [{ text: "parameter", value: "Object name?" }], descripton: "When the object exists intersection with an other object" };
+        ActionsBuilder.Trigger[10] = { name: "OnKeyDownTrigger", properties: [{ text: "parameter:", value: "" }], description: "When the user push a key" };
+        ActionsBuilder.Trigger[11] = { name: "OnKeyUpTrigger", properties: [{ text: "parameter:", value: "" }], description: "When the user pushed a key" };
         ActionsBuilder.Trigger.COUNT = 12;
 
         //
         // Actions (direct & interpolate)
         //
         ActionsBuilder.Action = ActionsBuilder.Action || {};
-        ActionsBuilder.Action[0] = { name: 'SwitchBooleanAction', properties: directActionWidthPropertyPath() };
-        ActionsBuilder.Action[1] = { name: 'SetStateAction', properties: directActionTemplate(null, 'value') };
-        ActionsBuilder.Action[2] = { name: 'SetValueAction', properties: directActionWidthPropertyPath([{ text: 'value', value: 'value?' }]) };
-        ActionsBuilder.Action[3] = { name: 'IncrementValueAction', properties: directActionWidthPropertyPath([{ text: 'value', value: 'value?' }]) };
-        ActionsBuilder.Action[4] = { name: 'PlayAnimationAction', properties: directActionTemplate([{ text: 'from', value: '0' }, { text: 'to', value: '150' }, { text: 'loop', value: 'false' }]) };
-        ActionsBuilder.Action[5] = { name: 'StopAnimationAction', properties: directActionTemplate() };
-        ActionsBuilder.Action[6] = { name: 'DoNothingAction', properties: [] };
-        ActionsBuilder.Action[7] = { name: 'CombineAction', properties: [] };
+        ActionsBuilder.Action[0] = { name: "SwitchBooleanAction", properties: directActionWidthPropertyPath(), description: "Switches a boolean value for a given parameter of the target object : true to false, or false to true" };
+        ActionsBuilder.Action[1] = { name: "SetStateAction", properties: directActionTemplate(null, "value"), description: "Sets a new state value of the target object (i.e \"off\" or \"on\")" };
+        ActionsBuilder.Action[2] = { name: "SetValueAction", properties: directActionWidthPropertyPath([{ text: "value", value: "value?" }]), description: "Sets a new value to the specified parameter of the object" };
+        ActionsBuilder.Action[3] = { name: "SetParentAction", properties: directActionTemplate([{ text: "parent", value: "object name?" }]), description: "Sets a new parent for the target object" };
+        ActionsBuilder.Action[4] = { name: "IncrementValueAction", properties: directActionWidthPropertyPath([{ text: "value", value: "value?" }]), description: "Increments the value of the given parameter for the target object. The value can be negative" };
+        ActionsBuilder.Action[5] = { name: "PlayAnimationAction", properties: directActionTemplate([{ text: "from", value: "0" }, { text: "to", value: "150" }, { text: "loop", value: "false" }]), description: "Plays the animation of the target object" };
+        ActionsBuilder.Action[6] = { name: "StopAnimationAction", properties: directActionTemplate(), description: "Stops the animation of the target object" };
+        ActionsBuilder.Action[7] = { name: "DoNothingAction", properties: [], description: "Does nothing, can be used to balance the actions graph" };
         ActionsBuilder.Action[8] = {
-            name: 'InterpolateValueAction', properties: directActionWidthPropertyPath([
-                { text: 'value', value: 'value' },
-                { text: 'duration', value: '1000' },
-                { text: 'stopOtherAnimations', value: 'false' }])
+            name: "InterpolateValueAction", properties: directActionWidthPropertyPath([
+                { text: "value", value: "value" },
+                { text: "duration", value: "1000" },
+                { text: "stopOtherAnimations", value: "false" }]),
+            description: "Creates an animation (key frames) that animates the target object by modifying the given parameter to the target value"
         };
-        ActionsBuilder.Action.COUNT = 9;
+        ActionsBuilder.Action[9] = { name: "PlaySoundAction", properties: [{ text: "sound", value: "path to sound..." }], description: "Plays a sound giving it's path (name)" };
+        ActionsBuilder.Action[10] = { name: "StopSoundAction", properties: [{ text: "sound", value: "path to sound..." }], description: "Stops a sound giving it's path (name)" };
+        ActionsBuilder.Action[11] = { name: "CombineAction", properties: [], description: "Special action that combines multiple actions" };
+        ActionsBuilder.Action.COUNT = 12;
 
         //
         // Flow Control
         //
+        ActionsBuilder.FlowActionOperators = ActionsBuilder.FlowActionOperators || new Array();
+        ActionsBuilder.FlowActionOperators.push("IsEqual");
+        ActionsBuilder.FlowActionOperators.push("IsDifferent");
+        ActionsBuilder.FlowActionOperators.push("IsGreater");
+        ActionsBuilder.FlowActionOperators.push("IsLesser");
+
         ActionsBuilder.FlowAction = ActionsBuilder.FlowAction || {};
-        ActionsBuilder.FlowAction[0] = { name: 'ValueCondition', properties: directActionWidthPropertyPath([{ text: 'value', value: 'value?' }, { text: 'operator', value: 'IsEqual' }]) };
-        ActionsBuilder.FlowAction[1] = { name: 'StateCondition', properties: directActionTemplate([{ text: 'value', value: 'value?' }]) };
+        ActionsBuilder.FlowAction[0] = { name: "ValueCondition", properties: directActionWidthPropertyPath([{ text: "value", value: "value?" }, { text: "operator", value: ActionsBuilder.FlowActionOperators[0] }]), description: "A condition" };
+        ActionsBuilder.FlowAction[1] = { name: "StateCondition", properties: directActionTemplate([{ text: "value", value: "value?" }]), description: "A condition, true if the target object state equals the given state" };
         ActionsBuilder.FlowAction.COUNT = 2;
+        ActionsBuilder.FlowAction.Hub = { name: "Hub", properties: [], description: "" };
+
+        //
+        // Utils
+        //
+        ActionsBuilder.GetDescriptionFromActionName = function (name) {
+            for (var i = 0; i < ActionsBuilder.Trigger.COUNT; i++) {
+                if (ActionsBuilder.Trigger[i].name == name)
+                    return ActionsBuilder.Trigger[i].description;
+            }
+
+            for (var i = 0; i < ActionsBuilder.Action.COUNT; i++) {
+                if (ActionsBuilder.Action[i].name == name)
+                    return ActionsBuilder.Action[i].description;
+            }
+
+            for (var i = 0; i < ActionsBuilder.FlowAction.COUNT; i++) {
+                if (ActionsBuilder.FlowAction[i].name == name)
+                    return ActionsBuilder.FlowAction[i].description;
+            }
+
+        };
 
         return ActionsBuilder;
     })();

+ 141 - 0
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/contextmenu.js

@@ -0,0 +1,141 @@
+/// <reference path="raphael.js" />
+/// <reference path="actionKinds.js" />
+/// <reference path="viewer.js" />
+
+var AB;
+(function (AB) {
+
+    var ContextMenu = (function () {
+
+        function ContextMenu(viewer) {
+            var scope = this;
+
+            // Members
+            this.viewer = viewer;
+
+            this.showing = false;
+            this.savedColor = Raphael.rgb(255, 255, 255);
+            this.overColor = Raphael.rgb(140, 200, 230);
+
+            // Context menu elements
+            this.elements = [
+                { text: "Reduce", rect: null, action: "onReduce" },
+                { text: "Delete", rect: null, action: "onRemoveNode" },
+                { text: "Delete branch", rect: null, action: "onRemoveBranch" },
+                { text: "Connect / Disconnect", rect: null, action: "onDetachNode" },
+                { text: "Copy", rect: null, action: "onCopyStructure" },
+                { text: "Paste", rect: null, action: "onPasteStructure" },
+                // Add other elements here
+                { text: "", rect: null, action: null } // Color separator (top)
+            ];
+
+            // Finish
+            this.attachControl(viewer.graph.canvas);
+        }
+
+        // Attaches controls to the viewer
+        // element: the viewer element (canvas or div or etc.)
+        ContextMenu.prototype.attachControl = function (element) {
+            var scope = this;
+
+            var onClick = function (event) {
+                var x = scope.viewer.mousex;
+                var y = scope.viewer.mousey;
+
+                // Remove all context menu nodes, and run action if selected
+                if (scope.showing) {
+                    for (var i = 0; i < scope.elements.length; i++) {
+                        var el = scope.elements[i];
+
+                        if (el.action && el.rect.isPointInside(x, y))
+                            scope.viewer.utils[el.action]();
+
+                        scope.viewer._removeNode(el.rect);
+                    }
+                }
+                scope.showing = false;
+            };
+
+            var onMouseMove = function (event) {
+                // Override context menu's node color if mouse is inside
+                if (scope.showing) {
+                    for (var i = 0; i < scope.elements.length; i++) {
+                        var el = scope.elements[i];
+
+                        if (el.text == "")
+                            continue;
+
+                        var x = scope.viewer.mousex;
+                        var y = scope.viewer.mousey;
+
+                        if (el.rect.isPointInside(x, y))
+                            el.rect.attr(el.rect.rect, "fill", scope.overColor);
+                        else
+                            el.rect.attr(el.rect.rect, "fill", scope.savedColor);
+                    }
+                }
+            };
+
+            var onRightClick = function (event) {
+                var x = scope.viewer.mousex;
+                var y = scope.viewer.mousey;
+
+                if (y + (AB.Graph.NODE_HEIGHT * scope.elements.length) > scope.viewer.element.offsetHeight + scope.viewer.element.scrollTop)
+                    y = (AB.Graph.NODE_HEIGHT * scope.elements.length);
+                if (x + AB.Graph.NODE_WIDTH > scope.viewer.element.offsetWidth + scope.viewer.element.scrollLeft)
+                    x -= AB.Graph.NODE_WIDTH;
+
+                scope.viewer.onClick(event);
+
+                // Set selected node
+                var result = scope.viewer.traverseGraph(null, x, y);
+                if (result.hit) {
+                    scope.viewer.selectedNode = result.element;
+                }
+
+                if (!scope.showing) {
+                    if (scope.viewer.selectedNode == null)
+                        return;
+
+                    // Create elements
+                    var yOffset = 10;
+
+                    for (var i = 0; i < scope.elements.length - 1; i++) {
+                        var el = scope.elements[i];
+
+                        el.rect = scope.viewer._createNode(el.text, Raphael.rgb(216, 216, 216), true);
+                        scope.viewer._setNodePosition(el.rect, x, y + yOffset);
+
+                        el.rect.text.attr("x", x + 5);
+
+                        yOffset += AB.Graph.NODE_HEIGHT;
+                    }
+
+                    // Color separator
+                    var separator = scope.elements[scope.elements.length - 1];
+                    separator.rect = scope.viewer._createNode("", scope.viewer.getNodeColor(scope.viewer.selectedNode), true);
+                    scope.viewer._setNodePosition(separator.rect, x, y);
+                    separator.rect.attr(separator.rect.rect, "height", 10);
+
+                    // Finish
+                    scope.showing = true;
+                }
+                else {
+                    onClick();
+                    onRightClick(event);
+                }
+
+                window.event.returnValue = false;
+            };
+
+            document.addEventListener("click", onClick);
+            document.addEventListener("mousemove", onMouseMove);
+            element.addEventListener("contextmenu", onRightClick);
+        }
+
+        return ContextMenu;
+    })();
+
+    AB.ContextMenu = ContextMenu;
+
+})(AB || (AB = { }));

+ 152 - 0
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/fonts.css

@@ -0,0 +1,152 @@
+@font-face {
+    font-family: "Sinkin Sans Thin";
+    src: url('fonts/SinkinSans/SinkinSans-100Thin.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-100Thin.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-100Thin.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-100Thin.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-100Thin.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Thin Italic";
+    src: url('fonts/SinkinSans/SinkinSans-100ThinItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-100ThinItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-100ThinItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-100ThinItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-100ThinItalic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans XLight";
+    src: url('fonts/SinkinSans/SinkinSans-200XLight.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-200XLight.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-200XLight.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-200XLight.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-200XLight.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans XLight Italic";
+    src: url('fonts/SinkinSans/SinkinSans-200XLightItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-200XLightItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-200XLightItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-200XLightItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-200XLightItalic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Light";
+    src: url('fonts/SinkinSans/SinkinSans-300Light.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-300Light.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-300Light.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-300Light.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-300Light.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Light Italic";
+    src: url('fonts/SinkinSans/SinkinSans-300LightItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-300LightItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-300LightItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-300LightItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-300LightItalic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Italic";
+    src: url('fonts/SinkinSans/SinkinSans-400Italic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-400Italic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-400Italic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-400Italic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-400Italic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Regular";
+    src: url('fonts/SinkinSans/SinkinSans-400Regular.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-400Regular.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-400Regular.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-400Regular.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-400Regular.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Medium";
+    src: url('fonts/SinkinSans/SinkinSans-500Medium.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-500Medium.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-500Medium.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-500Medium.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-500Medium.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Medium Italic";
+    src: url('fonts/SinkinSans/SinkinSans-500MediumItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-500MediumItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-500MediumItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-500MediumItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-500MediumItalic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Semi-Bold";
+    src: url('fonts/SinkinSans/SinkinSans-600SemiBold.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-600SemiBold.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-600SemiBold.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-600SemiBold.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-600SemiBold.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Semi-Bold Italic";
+    src: url('fonts/SinkinSans/SinkinSans-600SemiBoldItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-600SemiBoldItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-600SemiBoldItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-600SemiBoldItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-600SemiBoldItalic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Bold";
+    src: url('fonts/SinkinSans/SinkinSans-700Bold.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-700Bold.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-700Bold.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-700Bold.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-700Bold.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Bold Italic";
+    src: url('fonts/SinkinSans/SinkinSans-700BoldItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-700BoldItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-700BoldItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-700BoldItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-700BoldItalic.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans Black";
+    src: url('fonts/SinkinSans/SinkinSans-800Black.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-800Black.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-800Black.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-800Black.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-800Black.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans XBlack";
+    src: url('fonts/SinkinSans/SinkinSans-900XBlack.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-900XBlack.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-900XBlack.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-900XBlack.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-900XBlack.svg#segeoN') format('svg');
+}
+
+@font-face {
+    font-family: "Sinkin Sans XBlack Italic";
+    src: url('fonts/SinkinSans/SinkinSans-900XBlackItalic.ttf');
+    src: url('fonts/SinkinSans/SinkinSans-900XBlackItalic.eot?#iefix') format('embedded-opentype'),
+        url('fonts/SinkinSans/SinkinSans-900XBlackItalic.woff') format('woff'),
+        url('fonts/SinkinSans/SinkinSans-900XBlackItalic.ttf') format('truetype'),
+        url('fonts/SinkinSans/SinkinSans-900XBlackItalic.svg#segeoN') format('svg');
+}

+ 200 - 58
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/index.css

@@ -1,68 +1,210 @@
-
-html, body {
-    height: 100%;
-    width: 100%;
-
-    max-width: 99%;
-    max-height: 99%;
-
-    overflow: hidden;
+html, body {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  background-color: white;
+  cursor: default;
 }
 
-/*Context Menu*/
-.hide {
-    display: none;
-}
-.show {
-    z-index:1;
-    position: relative;
-    background-color:#C0C0C0;
-    border: 1px solid black;
-    padding: 2px;
-    display: block;
-    margin: 0;
-    list-style-type: none;
-    list-style: none;
-}
-.show li{ list-style: none; }
-.show a { border: 0 !important; text-decoration: none; }
-.show a:hover { text-decoration: underline !important; }
-
-/*Elements*/
-#List {
-    height: 100%;
-    width: 25%;
-
-    max-height: 100%;
-    max-width: 25%;
-
-    overflow-y: scroll;
-
-    float: left;
+ul {
+  padding-left: 1em;
+  margin-bottom: 0.5em;
+  margin-top: 0.5em;
+}
+ul li {
+  list-style-type: none;
+  display: inline-block;
+  margin-right: 3em;
+  cursor: pointer;
+}
+ul li:last-child {
+  margin-right: 0;
 }
 
-#Graph {
-    width: 50%;
-    height: 100%;
-
-    max-height: 100%;
-    max-width: 50%;
-    
-    overflow-x: scroll;
-    overflow-y: scroll;
-
-    float: right;
+svg {
+  left: 0 !important;
 }
 
-#Parameters {
-    width: 25%;
+.container {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.container a, .container p, .container li {
+  color: black;
+  font-family: Sinkin Sans Light;
+  font-size: 0.7em;
+}
+.container .formSend {
+  float: right;
+  width: 420px;
+  height: 55px;
+}
+.container .formSend button {
+  margin-right: 0.7em;
+  margin-bottom: 2em;
+  margin-top: 1.2em;
+  border: 0;
+  color: white;
+  width: 47%;
+  height: 50%;
+  border-radius: 1em;
+  moz-border-radius: 1em;
+  -webkit-border-radius: 1em;
+  -khtml-border-radius: 1em;
+  text-transform: uppercase;
+  font-family: "Sinkin Sans Semi-Bold";
+  outline: none;
+  cursor: pointer;
+}
+.container .formSend button.grey {
+  text-align: left;
+  padding-left: 1.5em;
+  background-color: #424242;
+}
+.container .formSend button.blue {
+  text-align: right;
+  padding-right: 1.5em;
+  background-color: #7facc1;
+}
+.container #Parameters, .container #List {
+  overflow: auto;
+  width: 25%;
+  /*height: 751px;*/
+  position: relative;
+  float: left;
+  margin-left: 1%;
+}
+.container #Parameters svg, .container #List svg {
+  width: 100%;
+}
+.container #Parameters {
+  position: relative;
+  margin-right: 1%;
+  background-color: #262626 !important;
+}
+.container #Parameters a {
+  margin-left: 3em;
+  color: white;
+}
+.container #Parameters .parametersClass {
+  border: 1px white solid;
+  margin: 0.9em;
+  padding-left: 0.3em;
+  padding-top: 0.5em;
+  padding-bottom: 0.5em;
+  border-radius: 2px;
+  moz-border-radius: 2px;
+  -webkit-border-radius: 2px;
+  -khtml-border-radius: 2px;
+  width: 88%;
+}
+.container #Parameters .parametersClass a {
+  border: 0;
+}
+.container #Parameters input.parametersClass, .container #Parameters select.parametersClass {
+  border: 0;
+  padding: 0;
+  height: 2em;
+  margin-left: 3em !important;
+  cursor: pointer;
+}
+.container #Parameters input.parametersClass {
+  width: 70%;
+  padding-left: 2em;
+  font-family: "Sinkin Sans Light";
+}
+.container #Parameters select.parametersClass {
+  width: 77%;
+  text-indent: 1em;
+  font-family: "Sinkin Sans Light";
+}
+.container #Parameters .parametersEditedNode {
+    width: 90%;
+    margin: 0.9em;
+    height: 25px;
+}
+.container #Parameters .parametersEditedNode a {
     height: 100%;
+    text-align: center;
+    font-family: "Sinkin Sans Light";
+}
+.container #Parameters .parametersHelp {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+  padding: 0;
+  background-color: black;
+}
+.container #Parameters .parametersHelp a {
+  padding-left: 0.2em;
+  padding-right: 0.2em;
+  padding-top: 0.5em;
+  padding-bottom: 2em;
+  display: block;
+  width: auto;
+  text-align: left;
+  border: 0;
+  color: #828282;
+  font-style: italic;
+  max-height: 95%;
+  height: 95%;
+}
+.container .GraphContainer {
+  width: 46%;
+  /*height: 751px;*/
+  height: 100%;
+  max-height: 100%;
+  position: relative;
+  float: left;
+  margin-left: 1%;
+}
+.container #Graph {
+  background: -ms-linear-gradient(top, #262626 0%, #7d7e7d 100%);
+  background: linear-gradient(top, #262626 0%, #7d7e7d 100%);
+  background: -webkit-linear-gradient(top, #262626 0%, #7d7e7d 100%);
+  position: relative;
+  overflow: scroll;
+  height: 96%;
+  width: 100%;
+  position: relative;
+}
 
-    max-height: 100%;
-    max-width: 25%;
-
-    overflow-y: scroll;
-    overflow-x: scroll;
+.container #Graph svg {
+  overflow: scroll;
+  height: 100%;
+  display: inline-block;
+}
 
-    float: right;
+.container #Toolbar {
+  margin: 0;
+  padding: 0;
+  background-color: black;
+  padding-top: 1.1%;
+  padding-bottom: 1.1%;
+}
+.container #Toolbar ul {
+  padding: 0;
+  margin: 0;
+  position: relative;
+}
+.container #Toolbar ul li {
+  color: white;
+  margin-right: 4em;
+}
+.container #Toolbar ul li:first-child {
+  margin-left: 3em;
+}
+.container #Toolbar ul li:last-child {
+  margin-right: 0;
+}
+.container #Toolbar ul li.border {
+  padding: 0.2em;
+  border: 1px solid white;
+}
+.container #List {
+  background-color: #3a3a3a;
+  max-height: 95%;
+  height: 95%;
 }

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 147 - 20
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/index.html


+ 127 - 33
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/list.js

@@ -5,6 +5,9 @@
 var AB;
 (function (AB) {
 
+    //
+    // List element
+    //
     var ListElement = (function () {
         function ListElement() {
             // Members
@@ -34,8 +37,13 @@ var AB;
         //
         function List(graph) {
             // Members
-            this.element = document.getElementById('List');
-            this.list = Raphael('List', (25 * screen.width) / 100, screen.height);
+            this.telement = document.getElementById("ListTriggers");
+            this.aelement = document.getElementById("ListActions");
+            this.faelement = document.getElementById("ListFlowActions");
+
+            this.tlist = Raphael("ListTriggers", (25 * screen.width) / 100, 400);
+            this.alist = Raphael("ListActions", (25 * screen.width) / 100, 400);
+            this.falist = Raphael("ListFlowActions", (25 * screen.width) / 100, 400);
             this.graph = graph;
 
             this.listElements = new Array();
@@ -44,76 +52,131 @@ var AB;
             var scope = this;
             window.onresize = function () {
                 for (var i = 0; i < scope.listElements.length; i++) {
-                    scope.listElements[i].attr(scope.listElements[i].rect, 'width', scope.element.getBoundingClientRect().width - 20);
+                    scope.listElements[i].attr(scope.listElements[i].rect, "width", scope.telement.getBoundingClientRect().width - 20);
                 }
             }
         }
 
+        // Clears the list of elements
         List.prototype.clearList = function () {
             for (var i = 0; i < this.listElements.length; i++) {
                 this._removeListElement(this.listElements[i]);
             }
         }
 
+        // Creates the list of elements automatically
         List.prototype.createListElements = function () {
             var excludedTriggers = [7, 10, 11]; // If objectType === Scene
             var yOffset = 10;
+            var textColor = Raphael.rgb(61, 72, 76);
+            var whiteTitle = Raphael.rgb(255, 255, 255);
 
             // Create Triggers
-            var t = this._createListElement('Triggers', AB.ActionsBuilder.Type.OBJECT, null, yOffset);
-            t.attr(t.rect, 'fill', Raphael.rgb(100, 149, 237));
-            yOffset += 15;
+            var t = this._createListElement("TRIGGERS", AB.ActionsBuilder.Type.OBJECT, null, yOffset, false, whiteTitle, this.tlist);
+            t.attr(t.text, "x", 15);
+            t.attr(t.rect, "fill", Raphael.rgb(41, 129, 255));
+            t.text.attr("font-family", "Sinkin Sans Medium");
+            t.text.attr("font-size", "11");
+            yOffset += List.ELEMENT_HEIGHT;
 
             for (var i = 0; i < AB.ActionsBuilder.Trigger.COUNT; i++) {
                 if (excludedTriggers.indexOf(i) != -1 && this.objectType != AB.ActionsBuilder.Type.SCENE)
                     continue;
 
                 var e = this._createListElement(AB.ActionsBuilder.Trigger[i].name, AB.ActionsBuilder.Type.TRIGGER,
-                                                AB.ActionsBuilder.Trigger[i].properties, yOffset, true);
+                                                AB.ActionsBuilder.Trigger[i].properties, yOffset, true, textColor,
+                                                this.tlist);
+                e.attr(e.rect, "fill", Raphael.rgb(133, 154, 185));
 
-                yOffset += 15;
+                yOffset += List.ELEMENT_HEIGHT;
             }
 
+            yOffset += List.ELEMENT_HEIGHT;
+            this.tlist.canvas.style.height = this.telement.style.height = yOffset + "px";
+            this._createCollapseAnimation(this.tlist, this.telement, yOffset, t);
+
             // Create Actions
-            var a = this._createListElement('Actions', AB.ActionsBuilder.Type.OBJECT, null, yOffset);
-            a.attr(a.rect, 'fill', Raphael.rgb(240, 230, 140));
-            yOffset += 15;
+            yOffset = 10;
+
+            var a = this._createListElement("ACTIONS", AB.ActionsBuilder.Type.OBJECT, null, yOffset, false, textColor, this.alist);
+            a.attr(a.text, "x", 15);
+            a.attr(a.rect, "fill", Raphael.rgb(255, 220, 42));
+            a.text.attr("font-family", "Sinkin Sans Medium");
+            a.text.attr("font-size", "11");
+            yOffset += List.ELEMENT_HEIGHT;
 
             for (var i = 0; i < AB.ActionsBuilder.Action.COUNT; i++) {
                 var e = this._createListElement(AB.ActionsBuilder.Action[i].name, AB.ActionsBuilder.Type.ACTION,
-                                                AB.ActionsBuilder.Action[i].properties, yOffset, true);
+                                                AB.ActionsBuilder.Action[i].properties, yOffset, true, textColor,
+                                                this.alist);
+                e.attr(e.rect, "fill", Raphael.rgb(182, 185, 132));
 
-                yOffset += 15;
+                yOffset += List.ELEMENT_HEIGHT;
             }
 
+            yOffset += List.ELEMENT_HEIGHT;
+            this.alist.canvas.style.height = this.aelement.style.height = yOffset + "px";
+            this._createCollapseAnimation(this.alist, this.aelement, yOffset, a);
+
             // Create flow control
-            var f = this._createListElement('Flow Control', AB.ActionsBuilder.Type.OBJECT, null, yOffset);
-            f.attr(f.rect, 'fill', Raphael.rgb(205, 92, 92));
-            yOffset += 15;
+            yOffset = 10;
+
+            var f = this._createListElement("FLOW CONTROL", AB.ActionsBuilder.Type.OBJECT, null, yOffset, false, whiteTitle, this.falist);
+            f.attr(f.text, "x", 15);
+            f.attr(f.rect, "fill", Raphael.rgb(255, 41, 53));
+            f.text.attr("font-family", "Sinkin Sans Medium");
+            f.text.attr("font-size", "11");
+            yOffset += List.ELEMENT_HEIGHT;
 
             for (var i = 0; i < AB.ActionsBuilder.FlowAction.COUNT; i++) {
                 var e = this._createListElement(AB.ActionsBuilder.FlowAction[i].name, AB.ActionsBuilder.Type.FLOW_CONTROL,
-                                                AB.ActionsBuilder.FlowAction[i].properties, yOffset, true);
+                                                AB.ActionsBuilder.FlowAction[i].properties, yOffset, true, textColor,
+                                                this.falist);
+                e.attr(e.rect, "fill", Raphael.rgb(185, 132, 140));
 
-                yOffset += 15;
+                yOffset += List.ELEMENT_HEIGHT;
             }
+
+            yOffset += List.ELEMENT_HEIGHT;
+            this.falist.canvas.style.height = this.faelement.style.height = yOffset + "px";
+            this._createCollapseAnimation(this.falist, this.faelement, yOffset, f);
+        }
+
+        // Sets the color theme of the list (background)
+        List.prototype.setColorTheme = function (color) {
+            this.tlist.canvas.style.backgroundColor = color;
+            this.alist.canvas.style.backgroundColor = color;
+            this.falist.canvas.style.backgroundColor = color;
         }
 
         //
         // Private functions
         //
-        List.prototype._createListElement = function (name, type, properties, yOffset, drag) {
+
+        // Creates a list element
+        // name: the element's name
+        // type: the elements type (TRIGGER, ACTION, FLOW_CONTROL)
+        // properties: array of properties
+        // yOffset: the y position of the element
+        // drag: boolean, if the element should be animated
+        // textColor: optional, the text color
+        // list: the raphaeljs object
+        List.prototype._createListElement = function (name, type, properties, yOffset, drag, textColor, list) {
             var e = new ListElement();
 
-            e.rect = this.list.rect(0, yOffset, this.element.getBoundingClientRect().width - 20, 15);
-            e.text = this.list.text(e.rect.attr('x') + 20, yOffset + e.rect.attr('height') / 2, name);
+            e.rect = list.rect(10, yOffset, this.telement.getBoundingClientRect().width - 30, List.ELEMENT_HEIGHT);
+            e.text = list.text(e.rect.attr("x") + 20, yOffset + e.rect.attr("height") / 2, name);
             e.name = name;
             e.type = type;
             e.properties = properties;
 
-            e.rect.attr('fill', Raphael.rgb(200, 200, 200));
-            e.text.attr('font-size', '12');
-            e.text.attr('text-anchor', 'start');
+            e.rect.attr("fill", Raphael.rgb(64, 64, 64));
+            e.rect.attr("stroke", Raphael.rgb(58, 58, 58));
+            e.text.attr("font-size", "12");
+            e.text.attr("text-anchor", "start");
+            e.text.attr("font-family", "Sinkin Sans Light");
+            if (textColor)
+                e.text.attr("fill", textColor);
 
             if (drag) {
                 this._createListElementAnimation(e);
@@ -123,11 +186,34 @@ var AB;
             return e;
         }
 
+        // Removes a list element
+        // element: the list element to remove
         List.prototype._removeListElement = function (element) {
             element.rect.remove();
             element.text.remove();
         }
 
+        // Creates the collapse / expand animation
+        // element: the element to animated
+        // htmlElement: the html element container
+        // expandedHeight: the height when list is expanded
+        // onElement: the element that receives the click element
+        List.prototype._createCollapseAnimation = function (element, htmlElement, expandedHeight, onElement) {
+            var onClick = function (event) {               
+                var height = htmlElement.style.height;
+                if (height == expandedHeight + "px") {
+                    htmlElement.style.height = element.canvas.style.height = 35 + "px";
+                }
+                else {
+                    htmlElement.style.height = element.canvas.style.height = expandedHeight + "px";
+                }
+            };
+
+            onElement.rect.click(onClick);
+        }
+
+        // Creates the animation of list elements
+        // element: the element to animate
         List.prototype._createListElementAnimation = function (element) {
             var scope = this;
             var mousex, mousey;
@@ -139,24 +225,24 @@ var AB;
 
             var onStart = function (x, y, event) {
                 element.rect.animate({
-                    x: -20,
+                    x: -10,
                     opacity: 0.25
-                }, 500, '>');
+                }, 500, ">");
                 element.text.animate({
-                    x: 0,
+                    x: 10,
                     opacity: 0.25
-                }, 500, '>');
+                }, 500, ">");
             };
 
             var onEnd = function (event) {
                 element.rect.animate({
-                    x: 0,
+                    x: 10,
                     opacity: 1.0
-                }, 500, '<');
+                }, 500, "<");
                 element.text.animate({
-                    x: 20,
+                    x: 30,
                     opacity: 1.0
-                }, 500, '<');
+                }, 500, "<");
 
                 var x = mousex - scope.graph.graph.canvas.getBoundingClientRect().left;
                 var y = mousey - scope.graph.graph.canvas.getBoundingClientRect().top;
@@ -169,7 +255,13 @@ var AB;
                     if (element.type == AB.ActionsBuilder.Type.ACTION && dragResult.element == scope.graph.root.action)
                         return;
 
-                    if (element.type == AB.ActionsBuilder.Type.FLOW_CONTROL && dragResult.element == scope.graph.root.action)
+                    if (element.type == AB.ActionsBuilder.Type.FLOW_CONTROL && (dragResult.element == scope.graph.root.action || (dragResult.element.type == AB.ActionsBuilder.Type.FLOW_CONTROL && dragResult.element.parent.hub == null)))
+                        return;
+
+                    if (element.type == AB.ActionsBuilder.Type.FLOW_CONTROL && dragResult.element.combine)
+                        return;
+
+                    if (!dragResult.element.combine && dragResult.element.children.length > 0 && dragResult.element.type != AB.ActionsBuilder.Type.TRIGGER && dragResult.element != scope.graph.root.action)
                         return;
 
                     scope.graph.addNode(dragResult.element, element);
@@ -181,6 +273,8 @@ var AB;
             element.text.drag(onMove, onStart, onEnd);
         }
 
+        List.ELEMENT_HEIGHT = 25;
+
         return List;
     })();
 

+ 307 - 0
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/parametersManager.js

@@ -0,0 +1,307 @@
+/// <reference path="raphael.js" />
+/// <reference path="actionKinds.js" />
+/// <reference path="viewer.js" />
+
+var AB;
+(function (AB) {
+
+    var ParametersManager = (function () {
+
+        function ParametersManager(viewer) {
+            // Members
+            this.viewer = viewer;
+            this.parametersElement = document.getElementById("Parameters");
+        }
+
+        // Clears the parameters view (right side)
+        ParametersManager.prototype.clearParameters = function () {
+            if (this.divHelpText) {
+                this.divHelpText.parentNode.removeChild(this.divHelpText);
+                this.divHelpText = null;
+            }
+
+            while (this.parametersElement.childNodes.length)
+                this.parametersElement.removeChild(this.parametersElement.firstChild);
+        }
+
+        // Creates the help section
+        ParametersManager.prototype.createHelpSection = function (element) {
+            var divHelpText = document.createElement("div");
+            divHelpText.className = "parametersHelp";
+            this.parametersElement.appendChild(divHelpText);
+
+            var helpText = document.createElement("a");
+            helpText.innerHTML = AB.ActionsBuilder.GetDescriptionFromActionName(element.action.name);
+            helpText.className = "parametersClass";
+            divHelpText.appendChild(helpText);
+        }
+
+        // Creates parameters for the given element (node / action)
+        ParametersManager.prototype.createParameters = function (element) {
+            // Events
+            var onInputChange = function (input, propertyID) {
+                return function () {
+                    var value = input.value;
+                    element.action.propertiesResults[propertyID] = value;
+                };
+            };
+
+            var onTargetTypeChanged = function (targetTypeSelect, propertyPathSelect, targetNameSelect, propertyID) {
+                return function () {
+                    var selected = targetTypeSelect.selectedIndex;
+                    var value = targetTypeSelect.options[selected].value;
+
+                    if (targetNameSelect != null && value != "SceneProperties") {
+                        var array = null;
+                        // Keep target name value
+                        var nameValue = targetNameSelect.value;
+
+                        targetNameSelect.length = 0;
+
+                        if (value == "MeshProperties") array = AB.ActionsBuilder.MeshesList;
+                        else if (value == "LightProperties") array = AB.ActionsBuilder.LightsList;
+                        else if (value == "CameraProperties") array = AB.ActionsBuilder.CamerasList;
+
+                        for (var i = 0; i < array.length; i++) {
+                            var option = document.createElement("option");
+                            option.value = array[i];
+                            option.innerHTML = array[i];
+                            targetNameSelect.add(option);
+                        }
+
+                        targetNameSelect.value = nameValue;
+                    }
+
+                    if (propertyPathSelect != null) {
+                        // Get array of properties for selected object type (scene, camera, light, mesh, ...)
+                        var array = AB.ActionsBuilder[targetTypeSelect.options[selected].value];
+                        propertyPathSelect.length = 0;
+
+                        for (var j = 0; j < array.length; j++) {
+                            var option = document.createElement("option");
+                            option.value = array[j].name;
+                            option.innerHTML = array[j].name;
+                            propertyPathSelect.add(option);
+                        }
+                    }
+                    element.action.properties[propertyPathSelect != null ? propertyID - 1 : propertyID].targetType = targetTypeSelect.options[selected].value;
+                };
+            };
+
+            var onTargetNameChanged = function (targetNameSelect, propertyID) {
+                return function () {
+                    element.action.propertiesResults[propertyID] = targetNameSelect.options[targetNameSelect.selectedIndex].value;
+                }
+            }
+
+            var onPropertyPathChanged = function (propertyPathSelect, additionalInput, propertyID) {
+                return function () {
+                    additionalInput.value = "";
+                    element.action.propertiesResults[propertyID] = propertyPathSelect.options[propertyPathSelect.selectedIndex].value;
+                };
+            };
+
+            var onAdditionalPropertyPathChanged = function (propertyPathSelect, additionalInput, propertyID) {
+                return function () {
+                    var propertyPath = propertyPathSelect.options[propertyPathSelect.selectedIndex].value;
+                    if (additionalInput.value != "")
+                        propertyPath += "." + additionalInput.value;
+                    element.action.propertiesResults[propertyID] = propertyPath;
+                };
+            };
+
+            var onConditionOperatorChanged = function (conditionOperatorSelect, propertyID) {
+                return function () {
+                    element.action.propertiesResults[propertyID] = conditionOperatorSelect.options[conditionOperatorSelect.selectedIndex].value;
+                };
+            };
+
+            // Special Elements
+            var targetTypeSelect = null;
+            var targetNameSelect = null;
+            var propertyPathSelect = null;
+            var soundNameSelect = null;
+
+            // Clear view (div)
+            this.clearParameters();
+
+            // Get properties
+            var p = element.action.properties;
+            var pr = element.action.propertiesResults;
+
+            // Create edition presentation
+            var divEditedNode = document.createElement("div");
+            divEditedNode.className = "parametersEditedNode";
+            divEditedNode.style.backgroundColor = this.viewer.getSelectedNodeColor(element.action, this.viewer.isParentDetached(element.action));
+            divEditedNode.style.border = "2px solid white";
+
+            var textEditedNode = document.createElement("a");
+            textEditedNode.className = "parametersEditedNode";
+            textEditedNode.innerHTML = element.action.name;
+            textEditedNode.style.verticalAlign = "middle";
+            textEditedNode.style.textAlign = "center";
+            if (element.action.type == AB.ActionsBuilder.Type.ACTION)
+                textEditedNode.style.color = "black";
+            else
+                textEditedNode.style.color = "white";
+            textEditedNode.style.width = textEditedNode.style.height = "100%";
+            divEditedNode.appendChild(textEditedNode);
+
+            this.parametersElement.appendChild(divEditedNode);
+
+            if (p.length == 0) {
+                this.createHelpSection(element);
+                return;
+            }
+
+            // Create parameters
+            for (var i = 0; i < p.length; i++) {
+                // Create separator
+                var separator = document.createElement("hr");
+                separator.noShade = true;
+                separator.style.width = "90%";
+
+                this.parametersElement.appendChild(separator);
+
+                // Parameter text
+                var propertyText = document.createElement("a");
+                propertyText.text = p[i].text;
+
+                this.parametersElement.appendChild(propertyText);
+
+                // If target, add the input element + combo box with target type
+                if (p[i].text == "sound") {
+                    soundNameSelect = document.createElement("select");
+                    soundNameSelect.className = "parametersClass";
+
+                    for (var j = 0; j < AB.ActionsBuilder.SoundsList.length; j++) {
+                        var name = AB.ActionsBuilder.SoundsList[j];
+                        var option = document.createElement("option");
+                        option.value = option.innerHTML = name;
+                        option.className = "parametersClass";
+                        soundNameSelect.add(option);
+                    }
+
+                    soundNameSelect.value = pr[i];
+                    this.parametersElement.appendChild(document.createElement("br"));
+                    this.parametersElement.appendChild(soundNameSelect);
+
+                    soundNameSelect.onchange = onInputChange(soundNameSelect, i);
+
+                    continue;
+                }
+                else if (p[i].text == "target"
+                    || (element.action.type == AB.ActionsBuilder.Type.TRIGGER && p[i].text == "parameter")
+                    || p[i].text == "parent")
+                {
+                    targetTypeSelect = document.createElement("select");
+                    targetTypeSelect.className = "parametersClass";
+
+                    for (var j = 0; j < AB.ActionsBuilder.DataTypesNames.length; j++) {
+                        var data = AB.ActionsBuilder.DataTypesNames[j];
+                        var option = document.createElement("option");
+                        option.value = data.data;
+                        option.innerHTML = data.name;
+                        option.className = "parametersClass";
+                        targetTypeSelect.add(option);
+                    }
+
+                    targetTypeSelect.value = p[i].targetType;
+
+                    this.parametersElement.appendChild(document.createElement("br"));
+                    this.parametersElement.appendChild(targetTypeSelect);
+
+                    // List names
+                    targetNameSelect = document.createElement("select");
+                    targetNameSelect.className = "parametersClass";
+
+                    this.parametersElement.appendChild(document.createElement("br"));
+                    this.parametersElement.appendChild(targetNameSelect);
+
+                    onTargetTypeChanged(targetTypeSelect, null, targetNameSelect, i)();
+
+                    targetNameSelect.value = pr[i];
+
+                    targetTypeSelect.onchange = onTargetTypeChanged(targetTypeSelect, null, targetNameSelect, i);
+                    targetNameSelect.onchange = onTargetNameChanged(targetNameSelect, i);
+
+                    continue;
+                }
+                // If propertyPath, add the combox box to select the property and input element for additional property
+                else if (p[i].text == "propertyPath") {
+                    propertyPathSelect = document.createElement("select");
+                    propertyPathSelect.className = "parametersClass";
+
+                    this.parametersElement.appendChild(document.createElement("br"));
+                    this.parametersElement.appendChild(propertyPathSelect);
+
+                    // Special input, then continue after its creation
+                    var additionalInput = document.createElement("input");
+                    additionalInput.setAttribute("value", "");
+                    additionalInput.className = "parametersClass";
+
+                    this.parametersElement.appendChild(document.createElement("br"));
+                    this.parametersElement.appendChild(additionalInput);
+
+                    // If propertyPath exists, then target exists
+                    targetTypeSelect.onchange = onTargetTypeChanged(targetTypeSelect, propertyPathSelect, targetNameSelect, i);
+                    propertyPathSelect.onchange = onPropertyPathChanged(propertyPathSelect, additionalInput, i);
+                    additionalInput.onkeyup = onAdditionalPropertyPathChanged(propertyPathSelect, additionalInput, i);
+
+                    // Fill propertyPath combo box
+                    onTargetTypeChanged(targetTypeSelect, propertyPathSelect, targetNameSelect, i)();
+
+                    // Set selected property
+                    var propertyName = pr[i].split(".");
+                    propertyPathSelect.value = propertyName[0];
+
+                    var additionPropertyName = "";
+                    for (var j = 1; j < propertyName.length; j++)
+                        additionPropertyName += propertyName[j];
+
+                    additionalInput.setAttribute("value", additionPropertyName);
+
+                    // Finish
+                    continue;
+                }
+                    // Value condition, add combo box for operator type
+                else if (p[i].text == "operator") {
+                    var conditionOperatorSelect = document.createElement("select");
+                    conditionOperatorSelect.className = "parametersClass";
+
+                    for (var j = 0; j < AB.ActionsBuilder.FlowActionOperators.length; j++) {
+                        var option = document.createElement("option");
+                        option.value = AB.ActionsBuilder.FlowActionOperators[j];
+                        option.innerHTML = AB.ActionsBuilder.FlowActionOperators[j];
+                        conditionOperatorSelect.add(option);
+                    }
+
+                    conditionOperatorSelect.value = pr[i];
+                    conditionOperatorSelect.onchange = onConditionOperatorChanged(conditionOperatorSelect, i);
+
+                    this.parametersElement.appendChild(document.createElement("br"));
+                    this.parametersElement.appendChild(conditionOperatorSelect);
+
+                    continue;
+                }
+
+                var propertyInput = document.createElement("input");
+                propertyInput.setAttribute("value", pr[i]);
+                propertyInput.onkeyup = onInputChange(propertyInput, i);
+                propertyInput.className = "parametersClass";
+
+                //this.parametersElement.appendChild(document.createElement("br"));
+                this.parametersElement.appendChild(propertyInput);
+            }
+
+            // Create help text (bottom)
+            this.createHelpSection(element);
+        }
+
+        return ParametersManager;
+
+    })();
+
+    AB.ParametersManager = ParametersManager;
+
+})(AB || (AB = {}));

+ 216 - 0
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/utils.js

@@ -0,0 +1,216 @@
+/// <reference path="raphael.js" />
+/// <reference path="viewer.js" />
+
+var AB;
+(function (AB) {
+
+    var Utils = (function () {
+        function Utils(viewer) {
+            // Members
+            this.viewer = viewer;
+        }
+
+        // Removes the selected node
+        Utils.prototype.onRemoveNode = function () {
+            if (!this.viewer.selectedNode)
+                return;
+
+            var node = this.viewer.selectedNode;
+
+            if (node.type == AB.ActionsBuilder.Type.FLOW_CONTROL && node.parent.combine)
+                this.viewer.selectedNode = node.parent;
+
+            node.parent.removeChild(node);
+            this.viewer._removeAction(node, false);
+
+            if (node.combine) {
+                // Remove hub
+                var hub = node.hub;
+
+                node.parent.removeChild(hub.action);
+                this.viewer._removeAction(hub.action, false);
+
+                if (hub.action.children.length > 0)
+                    node.hub.action.children[0].parent = node.parent;
+            }
+
+            this.viewer.update();
+            this.viewer.parametersManager.clearParameters();
+
+            this.viewer.selectedNode = null;
+        }
+
+        // Removes the selected branch (starting from the selected node)
+        Utils.prototype.onRemoveBranch = function () {
+            if (!this.viewer.selectedNode)
+                return;
+
+            var node = this.viewer.selectedNode;
+
+            if (node.type == AB.ActionsBuilder.Type.FLOW_CONTROL && node.parent.combine)
+                this.selectedNode = node.parent;
+
+            node.parent.removeChild(this.selectedNode);
+            this.viewer._removeAction(node, true);
+
+            this.viewer.update();
+            this.viewer.parametersManager.clearParameters();
+
+            this.viewer.selectedNode = null;
+        }
+
+        // Detaches the selected node
+        // forceDetach: forces the Detach Color (green) for children of the selected node
+        // forceAttach: same as forceDetach but for the original node color
+        Utils.prototype.onDetachNode = function (forceDetach, forceAttach) {
+            if (!this.viewer.selectedNode)
+                return;
+
+            var node = this.viewer.selectedNode;
+
+            if (forceDetach == null) forceDetach = false;
+            if (forceAttach == null) forceAttach = false;
+
+            var detached = node.node.detached;
+            if (forceAttach)
+                detached = false;
+            else if (forceDetach)
+                detached = true;
+            else
+                detached = !detached;
+
+            node.node.detached = detached;
+
+            var scope = this;
+            var resetColor = function (root, color) {
+                var col = color == null ? scope.viewer.getNodeColor(root, scope.viewer.isParentDetached(root)) : color;
+                root.node.attr(root.node.rect, "fill", col);
+
+                if (root.node.line)
+                    root.node.attr(root.node.line, "stroke", col);
+
+                if (root.combine) {
+                    for (var i = 0; i < root.combineArray.length; i++) {
+                        resetColor(root.combineArray[i], color);
+                    }
+                }
+
+                for (var i = 0; i < root.children.length; i++) {
+                    resetColor(root.children[i], color);
+                }
+            };
+
+            this.viewer._setLine(node);
+            resetColor(node, !detached ? null : this.viewer.getNodeColor(node));
+        }
+
+        // Disconnects all triggers
+        Utils.prototype.onDetachAll = function (forceDetach, forceAttach) {
+            var scope = this;
+
+            var detach = function (root) {
+                scope.viewer.selectedNode = root;
+                scope.onDetachNode(forceDetach, forceAttach);
+            };
+
+            for (var i = 0; i < this.viewer.root.action.children.length; i++)
+                detach(this.viewer.root.action.children[i]);
+        }
+
+        // Copies the selected node structure (except root node) to the
+        // clipboard
+        Utils.prototype.onCopyStructure = function () {
+            if (!this.viewer.selectedNode)
+                return;
+
+            var structure = this.viewer.createJSON(this.viewer.selectedNode);
+            var asText = JSON.stringify(structure);
+
+            window.clipboardData.setData("text", asText);
+        }
+
+        // Pastes the previously copied structure (onCopyStructure) to
+        // the selected node
+        Utils.prototype.onPasteStructure = function () {
+            if (!this.viewer.selectedNode)
+                return;
+
+            var asText = window.clipboardData.getData("text");
+            var isJson = asText.length > 0 && asText[0] == "{" && asText[asText.length - 1] == "}";
+            var structure = JSON.parse(asText);
+            var node = this.viewer.selectedNode;
+
+            if (structure.type == AB.ActionsBuilder.Type.TRIGGER && node.node != this.viewer.root) {
+                alert("You can't paste a trigger if the selected node isn't the root object");
+                return;
+            }
+
+            if (structure.type != AB.ActionsBuilder.Type.TRIGGER && node.node == this.viewer.root) {
+                alert("You can't paste an action or condition if the selected node is the root object");
+                return;
+            }
+
+            this.viewer.loadFromJSON(structure, node);
+            this.viewer.update();
+        }
+
+        // Reduces the select node's width
+        Utils.prototype.onReduce = function (forceExpand, forceReduce) {
+            if (!this.viewer.selectedNode)
+                return;
+
+            if (forceExpand == null) forceExpand = false;
+            if (forceReduce == null) forceReduce = false;
+
+            var node = this.viewer.selectedNode.node;
+            var width = node.attr(node.rect, "width");
+
+            if ((width >= Graph.NODE_WIDTH && !forceExpand) || forceReduce) {
+                node.text.hide();
+                node.rect.stop();
+            }
+            else {
+                node.text.show();
+                node.attr(node.rect, "opacity", 1.0);
+            }
+
+            node.attr(node.rect, "width", (width >= Graph.NODE_WIDTH * this.viewer.zoom && !forceExpand) || forceReduce ? Graph.NODE_MINIMIZED_WIDTH * this.viewer.zoom : Graph.NODE_WIDTH * this.viewer.zoom);
+            node.minimized = !node.minimized;
+
+            this.viewer.update();
+        }
+
+        // Reduces all graph's nodes
+        Utils.prototype.onReduceAll = function (forceExpand) {
+            if (forceExpand == null)
+                forceExpand = false;
+
+            var scope = this;
+
+            var reduce = function (root) {
+                scope.viewer.selectedNode = root;
+                scope.onReduce(forceExpand, forceExpand ? false : true);
+
+                if (root.combine) {
+                    for (var i = 0; i < root.combineArray.length; i++)
+                        reduce(root.combineArray[i]);
+                }
+
+                for (var i = 0; i < root.children.length; i++)
+                    reduce(root.children[i]);
+
+            };
+
+            for (var i = 0; i < this.viewer.root.action.children.length; i++)
+                reduce(this.viewer.root.action.children[i]);
+
+            this.viewer.selectedNode = null;
+        }
+
+        return Utils;
+
+    })();
+
+    AB.Utils = Utils;
+
+})(AB || (AB = {}));

+ 517 - 178
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/viewer.js

@@ -1,5 +1,9 @@
 /// <reference path="raphael.js" />
 /// <reference path="action.js" />
+/// <reference path="contextmenu.js" />
+/// <reference path="viewertoolbar.js" />
+/// <reference path="parametersManager.js" />
+/// <reference path="utils.js" />
 
 var AB;
 (function (AB) {
@@ -13,145 +17,358 @@ var AB;
             var scope = this;
 
             // Members
-            this.element = document.getElementById('Graph');
-            this.graph = Raphael('Graph', (50 * screen.width) / 100, screen.height);
-            this.root = this._createNode('Object name', Raphael.rgb(200, 200, 200), true);
-            this.parametersElement = document.getElementById('Parameters');
+            this.element = document.getElementById("Graph");
+            this.graph = Raphael("Graph", (50 * screen.width) / 100, screen.height);
+            this.root = this._createNode("Object name", Raphael.rgb(255, 255, 255), true);
+
+            this.utils = new AB.Utils(this);
+            this.contextMenu = new AB.ContextMenu(this);
+            this.toolbar = new AB.ToolBar(this);
+            this.parametersManager = new AB.ParametersManager(this);
 
             this.mousex = 0;
             this.mousey = 0;
-            this.objectName = '';
+            this.objectName = "";
+            this.editing = false;
+            this.zoom = 1.0;
 
-            // Context menu (to remove a node)
-            this.contextMenu = null;
             this.selectedNode = null;
 
-            this.graph.canvas.addEventListener('contextmenu', function (event) {
-                var result = scope.traverseGraph(null, scope.mousex, scope.mousey);
-                if (result.hit && result.element != scope.root.action) {
-                    scope.selectedNode = result.element;
-                    scope.contextMenu = scope._createNode('Remove', Raphael.rgb(255, 255, 255), true);
-                    scope._setNodePosition(scope.contextMenu, scope.mousex, scope.mousey);
-                }
+            this.element.onmouseover = function () {
+                scope.editing = false;
+            };
+            this.element.onmouseout = function () {
+                scope.editing = true;
+            };
 
-                window.event.returnValue = false;
+            document.addEventListener("click", function (event) {
+                if (!scope.contextMenu.showing) {
+                    scope.onClick(event);
+                }
             });
-            document.onclick = function (event) {
-                if (scope.contextMenu) {
-                    if (scope.contextMenu.isPointInside(scope.mousex, scope.mousey)) {
-                        scope.selectedNode.parent.removeChild(scope.selectedNode);
-                        scope._removeAction(scope.selectedNode, true);
-                        scope.update();
-                    }
-
-                    scope.selectedNode = null;
-                    scope._removeNode(scope.contextMenu);
+            document.ondblclick = function (event) {
+                var result = scope.traverseGraph(null, scope.mousex, scope.mousey);
+                if (result.hit && result.element.node != scope.root) {
+                    scope.onReduce();
                 }
-
-                scope.contextMenu = null;
             };
             document.onmousemove = function (event) {
                 scope.mousex = event.clientX - scope.graph.canvas.getBoundingClientRect().left;
                 scope.mousey = event.clientY - scope.graph.canvas.getBoundingClientRect().top;
-
-                if (!scope.contextMenu)
-                    return;
-
-                if (scope.contextMenu.isPointInside(scope.mousex, scope.mousey))
-                    scope.contextMenu.attr(scope.contextMenu.rect, 'fill', Raphael.rgb(140, 200, 230));
-                else
-                    scope.contextMenu.attr(scope.contextMenu.rect, 'fill', Raphael.rgb(255, 255, 255));
             };
 
             // Set properties
             this.element.scrollLeft = (this.element.getBoundingClientRect().width / 2) + (Graph.NODE_WIDTH / 2);
         }
 
+        // Handles the click event. Called by this and this.contextmenu
+        // event: the event object
+        Graph.prototype.onClick = function (event) {
+            var result = this.traverseGraph(null, this.mousex, this.mousey, true);
+
+            if (!this.editing) {
+                if (this.selectedNode != null) {
+                    var detached = this.isParentDetached(this.selectedNode);
+                    this.selectedNode.node.attr(this.selectedNode.node.rect, "fill", this.getNodeColor(this.selectedNode, detached));
+                    this.selectedNode = null;
+                    if (!result.hit)
+                        this.parametersManager.clearParameters();
+                }
+
+                if (result.hit && result.element.node != this.root) {
+                    this.selectedNode = result.element;
+                    this.selectedNode.node.attr(this.selectedNode.node.rect, "fill", this.getSelectedNodeColor(this.selectedNode, detached));
+                }
+
+                if (!result.hit)
+                    this.selectedNode = null;
+            }
+        }
+
+        // Returns the node's color for a given type
+        // action : the action to test
+        // returns the node color
+        Graph.prototype.getSelectedNodeColor = function (action, detached) {
+            // Detached
+            if (action.detached || (action.node && action.node.detached) || detached)
+                return Raphael.rgb(96, 122, 14);
+
+            // Get proper color
+            var color = Raphael.rgb(255, 255, 255); // Default color for root
+            switch (action.type) {
+                case AB.ActionsBuilder.Type.TRIGGER: color = Raphael.rgb(41, 129, 255); break;
+                case AB.ActionsBuilder.Type.ACTION: color = Raphael.rgb(255, 220, 42); break;
+                case AB.ActionsBuilder.Type.FLOW_CONTROL: color = Raphael.rgb(255, 41, 53); break;
+            }
+
+            return color;
+        }
+
+        Graph.prototype.getNodeColor = function (action, detached) {
+            if (action.detached || (action.node && action.node.detached) || detached)
+                return Raphael.rgb(96, 122, 14);
+
+            // Get proper color
+            var color = Raphael.rgb(255, 255, 255); // Default color for root
+            switch (action.type) {
+                case AB.ActionsBuilder.Type.TRIGGER: color = Raphael.rgb(133, 154, 185); break;
+                case AB.ActionsBuilder.Type.ACTION: color = Raphael.rgb(182, 185, 132); break;
+                case AB.ActionsBuilder.Type.FLOW_CONTROL: color = Raphael.rgb(185, 132, 140); break;
+            }
+
+            return color;
+        }
+
+        Graph.prototype.isParentDetached = function (node) {
+            var parent = node.parent;
+            while (parent != null) {
+                if (parent.node.detached)
+                    return true;
+                parent = parent.parent;
+            }
+
+            return false;
+        }
+
+        // Adds a node to the graph
+        // parent : the parent of the added node
+        // listElement : the values
         Graph.prototype.addNode = function (parent, listElement) {
-            var color = Raphael.rgb(200, 200, 200);
-            switch (listElement.type) {
-                case AB.ActionsBuilder.Type.TRIGGER: color = Raphael.rgb(100, 149, 237); break;
-                case AB.ActionsBuilder.Type.ACTION: color = Raphael.rgb(240, 230, 140); break;
-                case AB.ActionsBuilder.Type.FLOW_CONTROL: color = Raphael.rgb(205, 92, 92); break;
+            var color = this.getNodeColor(listElement);
+
+            var n = this._createNode(listElement.name, color, parent.combine ? true : false);
+            if (listElement.name == "CombineAction") {
+                n.action.combine = true;
+
+                var hubElement = AB.ActionsBuilder.FlowAction.Hub;
+                var hubType = AB.ActionsBuilder.Type.FLOW_CONTROL;
+                n.action.hub = this._createNode(hubElement.name, this.getNodeColor({ type: hubType }), false);
+                n.action.hub.action.type = hubType;
+                n.action.addChild(n.action.hub.action);
+                this._createNodeAnimation(n.action.hub);
             }
 
-            var n = this._createNode(listElement.name, color, false);
             n.action.name = listElement.name;
             n.action.type = listElement.type;
+            n.detached = listElement.detached || false;
             n.action.properties = listElement.properties;
 
             for (var i = 0; i < listElement.properties.length; i++)
                 n.action.propertiesResults.push(listElement.properties[i].value);
 
-            if (parent)
+            if (parent && !parent.combine) {
                 parent.addChild(n.action);
+            }
+            else if (parent.combine) {
+                // Create hub
+                parent.combineArray.push(n.action);
+                n.action.parent = parent;
+                parent.node.attr(parent.node.text, "text", "");
+            }
 
             this._createNodeAnimation(n);
-        }
 
-        Graph.prototype.update = function (node, yOffset, childID, rootChildID) {
-            if (!yOffset)
-                yOffset = 10;
-            else
-                yOffset += Graph.VERTICAL_OFFSET;
+            return n;
+        }
 
-            if (!node) node = this.root;
+        // Updates the graph
+        Graph.prototype.update = function () {
+            var scope = this;
+            var rootChildID = 0;
 
-            if (node == this.root) {
-                this._setNodePosition(node, (this.graph.width / 2) - (Graph.NODE_WIDTH / 2), yOffset);
-            }
-            else {
-                var length = node.action.parent.children.length;
-                var parentx = node.action.parent.node.attr(node.action.parent.node.rect, 'x');
-                var totalLength = Graph.NODE_WIDTH * length;
-                var offset = ( Graph.NODE_WIDTH * (length - childID - 1) );
-                var posx = parentx;
-                posx += offset - ((Graph.NODE_WIDTH / 2) * (length - 1));
-
-                this._setNodePosition(node, posx, yOffset);
-                this._setLine(node.action);
+            var setNodeSize = function(node) {
+                if (node.minimized) {
+                    node.attr(node.rect, "width", Graph.NODE_MINIMIZED_WIDTH * scope.zoom);
+                    node.attr(node.rect, "height", Graph.NODE_HEIGHT * scope.zoom);
+                }
+                else {
+                    node.attr(node.rect, "width", Graph.NODE_WIDTH * scope.zoom);
+                    node.attr(node.rect, "height", Graph.NODE_HEIGHT * scope.zoom);
+                }
+                node.attr(node.text, "font-size", 11 * scope.zoom);
             }
 
-            for (var i = 0; i < node.action.children.length; i++) {
-                if (node == this.root)
-                    rootChildID = i;
+            var onUpdate = function (action, yOffset) {
+                var node = action.node;
+                var parent = action.parent ? action.parent.node : null;
+
+                // Set size of node according to zoom
+                if (action.combine) {
+                    var length = 0;
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        var n = action.combineArray[i].node;
+
+                        setNodeSize(n);
+
+                        length += n.attr(n.rect, "width");
+                    }
+                }
+                setNodeSize(node);
+
+                // Set position
+                if (parent) {
+                    scope._setNodePosition(node, parent.attr(parent.rect, "x"), yOffset);
+                    scope._setLine(action);
+                }
+
+                // Calculate total width
+                var totalWidth = 0;
+                for (var i = 0; i < action.children.length; i++) {
+                    var n = action.children[i].node;
+                    totalWidth += n.attr(n.rect, "width");
+                }
+
+                var nodeWidth = node.attr(node.rect, "width");
+                var nodex = node.attr(node.rect, "x");
+
+                var startingXPos = nodex + (nodeWidth / 2) - (totalWidth / 2);
 
-                this.update(node.action.children[i].node, yOffset, i, rootChildID);
+                // Recursively set position of children
+                for (var i = 0; i < action.children.length; i++) {
 
-                var n = node.action.children[i].node;
-                if (n.action.children.length > 1) {
-                    for (var j = rootChildID; j >= 0; j--) {
-                        if (node.action.children.length < 2 || n.action.children.length < 2)
-                            continue;
+                    var n = action.children[i].node;
+                    var newx = startingXPos;
+                    var newy = yOffset + Graph.VERTICAL_OFFSET * scope.zoom;
 
-                        var rx = this.root.attr(this.root.rect, 'x');
-                        var x = this.root.action.children[j].node.attr(this.root.action.children[j].node.rect, 'x');
-                        var y = this.root.action.children[j].node.attr(this.root.action.children[j].node.rect, 'y');
+                    onUpdate(n.action, newy);
 
-                        x -= ((Graph.NODE_WIDTH / 2) * (node.action.children.length - 1)) * (x > rx ? -1 : 1);
+                    scope._setNodePosition(n, newx, newy);
+                    scope._setLine(n.action);
+                    startingXPos += n.attr(n.rect, "width");
+                }
+            };
 
-                        this._setNodePosition(this.root.action.children[j].node, x, y);
-                        this._setLine(this.root.action.children[j]);
+            var onPosition = function (action, maxWidth) {
 
+                var total = 0;
+                var start = action.combine && action.combineArray.length > 1 ? 0 : 1;
+
+                for (var i = start; i < action.children.length; i++) {
+                    var n = action.children[i].node;
+
+                    if (action.combine) {
+                        for (var j = 1; j < action.combineArray.length; j++) {
+                            var cn = action.combineArray[j].node;
+                            total += cn.attr(cn.rect, "width");
+                        }
                     }
+                    else
+                        total += n.attr(n.rect, "width");
+                }
+
+                if (total > maxWidth) {
+                    maxWidth = total;
+                }
+
+                for (var i = 0; i < action.children.length; i++) {
+                    maxWidth = onPosition(action.children[i], maxWidth);
                 }
-                
+
+                return maxWidth;
+
+            };
+
+            // Set root node position / scale and recursively set position of its children
+            this._setNodePosition(this.root, (this.graph.width / 2) - (Graph.NODE_WIDTH / 2) * this.zoom, 10);
+            this.root.attr(this.root.rect, "width", Graph.NODE_WIDTH * scope.zoom);
+            this.root.attr(this.root.rect, "height", Graph.NODE_HEIGHT * scope.zoom);
+
+            onUpdate(this.root.action, 10 * this.zoom);
+
+            // Get total widths
+            var widths = new Array();
+            /*
+            object:
+                {
+                    triggerWidth: number
+                    childrenWidths: new Array<number>()
+                }
+            */
+
+            for (var i = 0; i < scope.root.action.children.length; i++) {
+                var a = scope.root.action.children[i];
+
+                var obj = {
+                    triggerWidth: onPosition(a, 0),
+                    childrenWidths: new Array()
+                };
+
+                for (var j = 0; j < a.children.length; j++) {
+                    var cw = onPosition(a.children[j], 0);
+                    obj.childrenWidths.push(cw);
+                    obj.triggerWidth += cw;
+                }
+
+                widths.push(obj);
+            }
+
+            // Set new position of children
+            var rx = scope.root.attr(scope.root.rect, "x");
+            var rwidth = 0;
+            var cwidth = 0;
+
+            for (var i = 0; i < scope.root.action.children.length; i++) {
+                var a = scope.root.action.children[i];
+                var tx = a.node.attr(a.node.rect, "x");
+
+                for (var j = 0; j < a.children.length; j++) {
+                    var n = a.children[j].node;
+                    var x = n.attr(n.rect, "x");
+                    var y = n.attr(n.rect, "y");
+                    var inverse = x >= tx ? 1 : -1;
+
+                    scope._setNodePosition(n, x + (scope.root.action.children.length > 1 ? widths[i].childrenWidths[j] / 1.8 : 0) * inverse, y);
+                    scope._setLine(n.action);
+
+                    scope._resizeCanvas(n);
+                    cwidth += widths[i].childrenWidths[j] / 2;
+                }
+
+                if (scope.root.action.children.length > 1) {
+                    var n = a.node;
+                    var x = n.attr(n.rect, "x");
+                    var y = n.attr(n.rect, "y");
+                    var inverse = x >= rx && i == 0 && !n.minimized ? 1 : -1;
+
+                    scope._setNodePosition(n, x + rwidth + (i > 1 ? widths[i - 1].triggerWidth / 1.8 : 0) + (widths[i].triggerWidth / 1.8) * inverse, y);
+                    scope._setLine(n.action);
+
+                    scope._resizeCanvas(n);
+                }
+
+                rwidth += widths[i].triggerWidth / 2;
             }
         }
 
-        Graph.prototype.createJSON = function (root, graph) {
+        // Creates the JSON according to the graph
+        // root: the root object to start with
+        Graph.prototype.createJSON = function (root) {
             if (!root) root = this.root.action;
-            if (!graph) graph = {};
 
             var action = {};
             action.type = root.type;
             action.name = root.name;
+            action.detached = root.node.detached;
             action.children = new Array();
+            action.combine = new Array();
 
             action.properties = new Array();
-            for (var i = 0; i < root.properties.length; i++)
+            for (var i = 0; i < root.properties.length; i++) {
                 action.properties[i] = { name: root.properties[i].text, value: root.propertiesResults[i] };
+                if (root.properties[i].targetType != null)
+                    action.properties[i].targetType = root.properties[i].targetType;
+            }
+
+            if (root.combine) {
+                for (var i = 0; i < root.combineArray.length; i++) {
+                    var combinedAction = root.combineArray[i];
+                    action.combine.push(this.createJSON(combinedAction, action));
+                }
+            }
             
+            if (root.combine)
+                root = root.children[0]; // The hub
+
             for (var i = 0; i < root.children.length; i++) {
                 action.children.push(this.createJSON(root.children[i], action));
             }
@@ -159,45 +376,80 @@ var AB;
             return action;
         }
 
-        Graph.prototype.loadFromJSON = function (graph) {
+        // Loads a graph from a JSON
+        // Graph: the root object's graph
+        // startNode: the start node to where begin the load
+        Graph.prototype.loadFromJSON = function (graph, startNode) {
             var scope = this;
 
-            for (var i = 0; i < this.root.action.children.length; i++)
-                this._removeAction(this.root.action.children[i], true);
-
-            this.root.action.clearChildren();
+            // If startNode is null, means it replaces all the graph
+            // If not, it comes from a copy/paste
+            if (startNode == null) {
+                for (var i = 0; i < this.root.action.children.length; i++)
+                    this._removeAction(this.root.action.children[i], true);
 
-            graph = JSON.parse(graph);
-            console.log(graph);
+                this.root.action.clearChildren();
+            }
 
-            var load = function (root, parent) {
+            var load = function (root, parent, detached, combine) {
                 if (!parent) parent = scope.root.action;
                 if (!root) root = graph;
+                if (!detached) detached = false;
+                if (!combine) combine = false;
 
-                if (root.type != AB.ActionsBuilder.Type.OBJECT) { // Means it is the root (the edited object)
+                var n = null; // Not going to be created
+
+                if (root.type != AB.ActionsBuilder.Type.OBJECT) { // Means it is not the root (the edited object)
                     var e = {};
                     e.type = root.type;
                     e.name = root.name;
+                    e.detached = root.detached;
+                    e.combine = root.combine.length > 0;
                     e.properties = new Array();
+                    e.combineArray = new Array();
 
-                    for (var i = 0; i < root.properties.length; i++)
+                    for (var i = 0; i < root.properties.length; i++) {
                         e.properties.push({ text: root.properties[i].name, value: root.properties[i].value });
+                        if (root.properties[i].targetType != null) {
+                            e.properties[e.properties.length - 1].targetType = root.properties[i].targetType;
+                        }
+                    }
 
-                    var n = scope.addNode(parent, e);
-                    parent = parent.children[parent.children.length - 1];
+                    n = scope.addNode(parent, e);
+                    if (detached)
+                        n.attr(n.rect, "fill", scope.getNodeColor(n.action));
+                    else
+                        detached = n.detached;
+
+                    // If combine array length > 0, it is a combine action
+                    for (var i = 0; i < root.combine.length; i++) {
+                        load(root.combine[i], n.action, detached, true);
+                    }
+
+                    if (!combine)
+                        parent = parent.children[parent.children.length - 1];
+                    else
+                        n.action.parent = null;
                 }
 
                 for (var i = 0; i < root.children.length; i++) {
-                    load(root.children[i], parent);
+                    load(root.children[i], n && n.action.combine ? n.action.hub.action : parent, root.detached, false);
                 }
             }
 
-            load();
+            load(graph, startNode);
             this.update();
         }
 
-        Graph.prototype.traverseGraph = function (start, x, y) {
+        // Traverse the graph and returns if hit a node
+        // start: the start node to start traverse
+        // x: the mouse's x position
+        // y: the mouse's y position
+        // traverseCombine: if check the combine nodes
+        Graph.prototype.traverseGraph = function (start, x, y, traverseCombine) {
             if (!start) start = this.root.action;
+            if (traverseCombine == null) traverseCombine = false;
+
             var result = {
                 hit: true,
                 element: start
@@ -211,10 +463,21 @@ var AB;
                 if (start.children[i].node.isPointInside(x, y)) {
                     result.hit = true;
                     result.element = start.children[i];
+
+                    if (start.children[i].combine && traverseCombine) {
+                        var a = start.children[i];
+                        for (var j = 0; j < a.combineArray.length; j++) {
+                            if (a.combineArray[j].node.isPointInside(x, y)) {
+                                result.element = a.combineArray[j];
+                                break;
+                            }
+                        }
+                    }
+
                     return result;
                 }
 
-                result = this.traverseGraph(start.children[i], x, y);
+                result = this.traverseGraph(start.children[i], x, y, traverseCombine);
                 if (result.hit)
                     return result;
 
@@ -228,43 +491,120 @@ var AB;
         //
         // Private functions
         //
+
+        // Sets the given node's line
+        // If commented, the line isn't setted by hidden
         Graph.prototype._setLine = function (element) {
-            var linex = element.node.attr(element.node.rect, 'x') + Graph.NODE_WIDTH / 2;
-            var liney = element.node.attr(element.node.rect, 'y');
-            var linex2 = element.parent.node.attr(element.parent.node.rect, 'x') + Graph.NODE_WIDTH / 2;
-            var liney2 = element.parent.node.attr(element.parent.node.rect, 'y') + Graph.NODE_HEIGHT;
+            var n = element.node;
+            var nodeWidth = n.attr(n.rect, "width");
+            var nodeHeight = n.attr(n.rect, "height");
+            var nodex = n.attr(n.rect, "x");
+            var nodey = n.attr(n.rect, "y");
+
+            if (n.detached) {
+                n.attr(n.line, "path", ["M", nodex, nodey, "L", nodex, nodey]);
+                return;
+            }
+
+            var linex = n.attr(n.rect, "x") + nodeWidth / 2;
+            var liney = n.attr(n.rect, "y");
+
+            var p = element.parent.node;
+            var parentWidth = p.attr(p.rect, "width");
+            var parentHeight = p.attr(p.rect, "height");
+            var parentx = p.attr(p.rect, "x");
+            var parenty = p.attr(p.rect, "y");
+
+            var liney2 = liney - (liney - parenty - parentHeight) / 2;
+            var linex3 = parentx + parentWidth / 2;
+            var liney4 = parenty + parentHeight;
+
+            n.attr(n.line, "path", ["M", linex, liney, "L", linex, liney2, "L", linex3, liney2, "L", linex3, liney4]);
+            n.attr(n.line, "stroke", this.getSelectedNodeColor(element, element.node.detached));
 
-            element.node.attr(element.node.line, 'path', 'M' + linex + ' ' + liney + 'L' + linex2 + ' ' + liney2);
         }
 
+        // Sets the given node's position
+        // Applies changements on its children
         Graph.prototype._setNodePosition = function (node, x, y) {
-            var offsetx = node.attr(node.rect, 'x') - x;
+            var offsetx = node.attr(node.rect, "x") - x;
+
+            node.attr(node.rect, "x", x);
+            node.attr(node.rect, "y", y);
 
-            node.attr(node.rect, 'x', x);
-            node.attr(node.rect, 'y', y);
+            var bbox = node.text.getBBox();
+            var textWidth = 0;
+            if (bbox)
+                textWidth = node.text.getBBox().width;
 
-            node.attr(node.text, 'x', x + 5);
-            node.attr(node.text, 'y', y + Graph.NODE_HEIGHT / 2);
+            node.attr(node.text, "x", x + node.attr(node.rect, "width") / 2 - textWidth / 2);
+            node.attr(node.text, "y", y + node.attr(node.rect, "height") / 2);
+
+            // Set combine nodes positions
+            if (node.action.combine) {
+                var length = 0;
+                for (var i = 0; i < node.action.combineArray.length; i++) {
+                    var a = node.action.combineArray[i];
+                    var n = a.node;
+
+                    n.attr(n.rect, "x", node.attr(node.rect, "x") + length);
+                    n.attr(n.rect, "y", node.attr(node.rect, "y"));
+
+                    textWidth = n.text.getBBox().width;
+                    n.attr(n.text, "x", n.attr(n.rect, "x") + n.attr(n.rect, "width") / 2 - textWidth / 2);
+                    n.attr(n.text, "y", y + Graph.NODE_HEIGHT / 2);
+
+                    length += n.attr(n.rect, "width");
+                }
+
+                node.attr(node.rect, "width", length);
+            }
 
             for (var i = 0; i < node.action.children.length; i++) {
-                this._setNodePosition(node.action.children[i].node, node.action.children[i].node.attr(node.action.children[i].node.rect, 'x') - offsetx, y + Graph.VERTICAL_OFFSET);
+                this._setNodePosition(node.action.children[i].node, node.action.children[i].node.attr(node.action.children[i].node.rect, "x") - offsetx, y + Graph.VERTICAL_OFFSET);
                 this._setLine(node.action.children[i]);
             }
         }
 
-        Graph.prototype._removeNode = function (element) {
-            element.rect.remove();
-            element.text.remove();
-            if (element.line)
-                element.line.remove();
+        // Resizes the canvas if the node is outside the current canvas size
+        Graph.prototype._resizeCanvas = function (node) {
+            var x = node.attr(node.rect, "x");
+            var y = node.attr(node.rect, "y");
+
+            if (x > 0 + Graph.NODE_WIDTH && x < this.graph.width - Graph.NODE_WIDTH && y < this.graph.height)
+                    return;
+
+            this.graph.setSize(this.graph.width + 500, this.graph.height + 500);
+            this._setNodePosition(this.root, (this.graph.width / 2) - (Graph.NODE_WIDTH / 2), 10);
+
+            this.element.scrollLeft = (this.graph.width / 2) - this.element.getBoundingClientRect().width / 2;
+        }
+
+        // Removes a node
+        // node : the node to remove
+        Graph.prototype._removeNode = function (node) {
+            node.rect.remove();
+            node.text.remove();
+            if (node.line)
+                node.line.remove();
         }
 
+        // Remove an action from the graph
+        // action : the action to remove
+        // removeChildren : if true, it deletes the branch
         Graph.prototype._removeAction = function (action, removeChildren) {
             if (!removeChildren)
                 removeChildren = false;
 
             this._removeNode(action.node);
 
+            if (action.combine) {
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this._removeNode(action.combineArray[i].node);
+                }
+                action.combineArray = new Array();
+            }
+
             if (removeChildren) {
                 for (var i = 0; i < action.children.length; i++)
                     this._removeAction(action.children[i], removeChildren);
@@ -272,57 +612,39 @@ var AB;
                 action.clearChildren();
             }
             else {
-                for (var i = 0; i < action.children.length; i++)
+                for (var i = 0; i < action.children.length; i++) {
+                    action.parent.addChild(action.children[i]);
                     action.children[i].parent = action.parent;
+                }
             }
         }
 
+        // Creates a node
+        // text : the text/name of the node
+        // color : the node's color
+        // noLine : if the node has parent then draw a line, or not
         Graph.prototype._createNode = function (text, color, noLine) {
             var n = new AB.Node();
-            n.rect = this.graph.rect(0, 0, Graph.NODE_WIDTH, Graph.NODE_HEIGHT, 5);
+            n.rect = this.graph.rect(20, 20, Graph.NODE_WIDTH, Graph.NODE_HEIGHT, 0);
             n.text = this.graph.text(0, 0, text);
-            if (!noLine)
-                n.line = this.graph.path('M10 10L90 90');
-            n.action = new AB.Action(n);
-
-            n.rect.attr('fill', color);
-            n.text.attr('font-size', 12);
-            n.text.attr('text-anchor', 'start');
-
-            return n;
-        }
 
-        Graph.prototype._createParameters = function (element) {
-            var onChange = function (input, propertyID) {
-                return function () {
-                    var value = input.value;
-                    element.action.propertiesResults[propertyID] = value;
-                }
+            if (!noLine) {
+                n.line = this.graph.path("M10 10L90 90");
+                n.line.attr("stroke", color);
             }
 
-            while (this.parametersElement.childNodes.length)
-                this.parametersElement.removeChild(this.parametersElement.firstChild);
+            n.action = new AB.Action(n);
 
-            var p = element.action.properties;
-            var pr = element.action.propertiesResults;
+            n.rect.attr("fill", color);
+            n.text.attr("font-size", 11);
+            n.text.attr("text-anchor", "start");
+            n.text.attr("font-family", "Sinkin Sans Light");
 
-            if (p.length == 0)
-                return;
-
-            for (var i = 0; i < p.length; i++) {
-                var el = document.createElement('input');
-                el.setAttribute('value', pr[i]);
-                el.onchange = onChange(el, i);
-
-                var text = document.createElement('a');
-                text.text = p[i].text;
-                this.parametersElement.appendChild(document.createElement('br'));
-                this.parametersElement.appendChild(text);
-                this.parametersElement.appendChild(document.createElement('br'));
-                this.parametersElement.appendChild(el);
-            }
+            return n;
         }
 
+        // Creates the animations for a node
+        // element: the node
         Graph.prototype._createNodeAnimation = function (element) {
             var scope = this;
             var mousex, mousey;
@@ -336,62 +658,78 @@ var AB;
             };
 
             var onStart = function (x, y, event) {
+                if (element.minimized)
+                    return;
+
                 mousex = x;
                 mousey = y;
 
                 if (finished) {
-                    elementx = element.attr(element.rect, 'x');
-                    elementy = element.attr(element.rect, 'y');
+                    elementx = element.attr(element.rect, "x");
+                    elementy = element.attr(element.rect, "y");
                 }
                 finished = false;
 
-                element.rect.animate({
-                    x: element.attr(element.rect, 'x') - 10,
-                    y: element.attr(element.rect, 'y') - 5,
-                    width: Graph.NODE_WIDTH + 20,
-                    height: Graph.NODE_HEIGHT + 10,
-                    opacity: 0.25
-                }, 500, '>');
+                if (!element.minimized) {
+                    element.rect.animate({
+                        x: element.attr(element.rect, "x") - 10,
+                        y: element.attr(element.rect, "y") - 5,
+                        width: element.minimized ? Graph.NODE_MINIMIZED_WIDTH + 20 : Graph.NODE_WIDTH + 20,
+                        height: Graph.NODE_HEIGHT + 10,
+                        opacity: 0.25
+                    }, 500, ">");
+                }
             };
 
             var onEnd = function (event) {
-                element.rect.animate({
-                    x: elementx,
-                    y: elementy,
-                    width: Graph.NODE_WIDTH,
-                    height: Graph.NODE_HEIGHT,
-                    opacity: 1.0
-                }, 500, '>', function () { finished = true; });
 
+                if (!element.minimized) {
+                    element.rect.animate({
+                        x: elementx,
+                        y: elementy,
+                        width: element.minimized ? Graph.NODE_MINIMIZED_WIDTH : Graph.NODE_WIDTH,
+                        height: Graph.NODE_HEIGHT,
+                        opacity: 1.0
+                    }, 500, ">", function () { finished = true; });
+                }
                 var x = mousex - scope.graph.canvas.getBoundingClientRect().left;
                 var y = mousey - scope.graph.canvas.getBoundingClientRect().top;
-                var dragResult = scope.traverseGraph(null, x, y);
-
-                var json = JSON.stringify(scope.createJSON());
-                console.log(json);
+                var dragResult = scope.traverseGraph(null, x, y, true);
 
                 if (dragResult.hit && dragResult.element == element.action || !dragResult.hit) {
-                    scope._createParameters(element);
+                    scope.parametersManager.createParameters(element);
                 }
                 else {
+                    if (dragResult.element.children.length > 0 && dragResult.element.type != AB.ActionsBuilder.Type.TRIGGER)
+                        return;
+
                     if (element.action.type == AB.ActionsBuilder.Type.TRIGGER && dragResult.element != scope.root.action)
                         return;
 
                     if (element.action.type == AB.ActionsBuilder.Type.ACTION && dragResult.element == scope.root.action)
                         return;
 
-                    if (element.action.type == AB.ActionsBuilder.Type.FLOW_CONTROL && dragResult.element == scope.root.action)
+                    if (element.action.type == AB.ActionsBuilder.Type.FLOW_CONTROL && (dragResult.element == scope.root.action || dragResult.element.type == AB.ActionsBuilder.Type.FLOW_CONTROL))
+                        return;
+
+                    if (element.action.parent && element.action.parent.combine) // Musn't move hubs
+                        return;
+
+                    if (element.action == dragResult.element.parent)
                         return;
 
                     // Reset node
                     element.rect.stop(element.rect.animation);
-                    element.attr(element.rect, 'opacity', 1.0);
-                    element.attr(element.rect, 'width', Graph.NODE_WIDTH);
-                    element.attr(element.rect, 'height', Graph.NODE_HEIGHT);
+                    element.attr(element.rect, "opacity", 1.0);
+                    element.attr(element.rect, "width", Graph.NODE_WIDTH);
+                    element.attr(element.rect, "height", Graph.NODE_HEIGHT);
 
-                    element.action.parent.removeChild(element.action);
-                    dragResult.element.addChild(element.action);
-                    scope.update();
+                    if (element.action.parent) {
+                        element.action.parent.removeChild(element.action);
+                        dragResult.element.addChild(element.action);
+                        scope.update();
+                        scope._createNodeAnimation(element);
+                    }
                 }
             };
 
@@ -399,6 +737,7 @@ var AB;
             element.text.drag(onMove, onStart, onEnd);
         }
 
+        Graph.NODE_MINIMIZED_WIDTH = 50;
         Graph.NODE_WIDTH = 150;
         Graph.NODE_HEIGHT = 25;
         Graph.VERTICAL_OFFSET = 70;

+ 87 - 0
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/ActionsBuilder/viewsertoolbar.js

@@ -0,0 +1,87 @@
+/// <reference path="raphael.js" />
+/// <reference path="actionKinds.js" />
+/// <reference path="viewer.js" />
+
+var AB;
+(function (AB) {
+
+    var ToolBar = (function () {
+        function ToolBar(viewer) {
+            // Members
+            this.element = document.getElementById("ToolbarList");
+            this.viewer = viewer;
+
+            // Configure
+            this._attachControl();
+        }
+
+        //
+        // Private methods
+        //
+        // Attaches controls to each button
+        ToolBar.prototype._attachControl = function () {
+            var scope = this;
+            var events = new Array();
+
+            // Create event handlers and add them to the events array
+            var onReduce = function () {
+                scope.viewer.utils.onReduceAll(false);
+            };
+            events.push(onReduce);
+
+            var onExpand = function () {
+                scope.viewer.utils.onReduceAll(true);
+            };
+            events.push(onExpand);
+
+            var onDisconnect = function () {
+                scope.viewer.utils.onDetachAll(true, false);
+            };
+            events.push(onDisconnect);
+
+            var onReconnect = function () {
+                scope.viewer.utils.onDetachAll(false, true);
+            };
+            events.push(onReconnect);
+
+            var onDeZoom = function () {
+                if (scope.viewer.zoom > 0.0)
+                    scope.viewer.zoom -= 0.1;
+
+                scope.viewer.update();
+            };
+            events.push(onDeZoom);
+
+            var onZoom = function () {
+                if (scope.viewer.zoom < 1.0)
+                    scope.viewer.zoom += 0.1;
+
+                scope.viewer.update();
+            };
+            events.push(onZoom);
+
+            // Add events
+            var onEvent = function (id, element) {
+                return function (event) {
+                    element.style.backgroundColor = "rgb(155, 155, 155)";
+                    events[id](event);
+                    setTimeout(function () {
+                        element.style.backgroundColor = "rgb(0, 0, 0)";
+                    }, 100);
+                };
+            };
+
+            var list = this.element.getElementsByTagName("li");
+            for (var i = 0; i < events.length; i++) {
+                list[i].addEventListener("click", onEvent(i, list[i]));
+            }
+        }
+
+        ToolBar.VIEW_HEIGHT = 40;
+
+        return ToolBar;
+    })();
+
+    AB.ToolBar = ToolBar;
+
+})(AB || (AB = {}));

+ 95 - 0
Exporters/3ds Max/Max2Babylon/Exporter/ActionBuilder/BabylonActionsBuilderActionItem.cs

@@ -0,0 +1,95 @@
+using Autodesk.Max;
+using System.Windows.Forms;
+using ActionItem = Autodesk.Max.Plugins.ActionItem;
+
+namespace Max2Babylon
+{
+    public class BabylonActionsBuilderActionItem : ActionItem
+    {
+        private ActionsBuilderForm _form = null;
+
+        public override bool ExecuteAction()
+        {
+            if (Loader.Core.SelNodeCount > 1)
+            {
+                Loader.Core.PushPrompt("Actions Builder only supports one Node");
+            }
+            else
+            {
+                IINode node = null;
+                SClass_ID type;
+
+                if (Loader.Core.SelNodeCount == 0)
+                    type = SClass_ID.Scene;
+                else
+                {
+                    node = Loader.Core.GetSelNode(0);
+                    type = node.ObjectRef.Eval(0).Obj.SuperClassID;
+                }
+
+                if (type == SClass_ID.Geomobject || type == SClass_ID.Scene)
+                {
+                    if (_form == null)
+                        _form = new ActionsBuilderForm(this);
+
+                    _form.WindowState = FormWindowState.Maximized;
+
+                    _form.ShowDialog();
+                    //Application.Run(_form);
+
+                }
+            }
+
+            return true;
+        }
+
+        public void Close()
+        {
+            if (_form == null)
+                return;
+
+            _form.Dispose();
+            _form = null;
+        }
+
+        public override int Id_
+        {
+            get { return 2; }
+        }
+
+        public override string ButtonText
+        {
+            get { return "Babylon Actions Builder"; }
+        }
+
+        public override string MenuText
+        {
+            get { return "Babylon Actions Builder"; }
+        }
+
+        public override string DescriptionText
+        {
+            get { return "UI graph to build custom actions on selected object"; }
+        }
+
+        public override string CategoryText
+        {
+            get { return "Babylon"; }
+        }
+
+        public override bool IsChecked_
+        {
+            get { return false; }
+        }
+
+        public override bool IsItemVisible
+        {
+            get { return true; }
+        }
+
+        public override bool IsEnabled_
+        {
+            get { return true; }
+        }
+    }
+}

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

@@ -154,11 +154,11 @@ namespace Max2Babylon
 
             var babylonAnimation = new BabylonAnimation
             {
-                dataType = dataType,
+                dataType = (int)dataType,
                 name = property + " animation",
                 keys = keys.ToArray(),
                 framePerSecond = Loader.Global.FrameRate,
-                loopBehavior = loopBehavior,
+                loopBehavior = (int)loopBehavior,
                 property = property
             };
 
@@ -283,11 +283,11 @@ namespace Max2Babylon
 
                     var babylonAnimation = new BabylonAnimation
                     {
-                        dataType = dataType,
+                        dataType = (int)dataType,
                         name = property + " animation",
                         keys = keys.ToArray(),
                         framePerSecond = Loader.Global.FrameRate,
-                        loopBehavior = BabylonAnimation.LoopBehavior.Cycle,
+                        loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle,
                         property = property
                     };
 

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

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using Autodesk.Max;
 using BabylonExport.Entities;
@@ -112,6 +113,49 @@ namespace Max2Babylon
                 babylonMesh.parentId = GetParentID(meshNode.NodeParent, babylonScene, scene);
             }
 
+            // Sounds
+            var soundName = meshNode.MaxNode.GetStringProperty("babylonjs_sound_filename", "");
+            if (!string.IsNullOrEmpty(soundName))
+            {
+                var filename = Path.GetFileName(soundName);
+
+                var meshSound = new BabylonSound
+                {
+                    name = filename,
+                    autoplay = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1),
+                    loop = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1),
+                    volume = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f),
+                    playbackRate = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f),
+                    connectedMeshId = babylonMesh.id,
+                    isDirectional = false,
+                    spatialSound = false,
+                    distanceModel = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"),
+                    maxDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f),
+                    rolloffFactor = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f),
+                    refDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f),
+                };
+
+                var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional", 0);
+                
+                if (isDirectional)
+                {
+                    meshSound.isDirectional = true;
+                    meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f);
+                    meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f);
+                    meshSound.coneOuterGain = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f);
+                }
+
+                babylonScene.SoundsList.Add(meshSound);
+
+                try
+                {
+                    File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true);
+                }
+                catch
+                {
+                }
+            }
+
             // Misc.
             babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1;
             babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable");

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

@@ -16,8 +16,29 @@ namespace Max2Babylon
             babylonShadowGenerator.lightId = lightNode.GetGuid().ToString();
 
             babylonShadowGenerator.mapSize = maxLight.GetMapSize(0, Tools.Forever);
-            babylonShadowGenerator.usePoissonSampling = maxLight.AbsMapBias >= 1;
 
+            babylonShadowGenerator.bias = lightNode.GetFloatProperty("babylonjs_shadows_bias", 0.00005f);
+
+            var shadowsType = lightNode.GetStringProperty("babylonjs_shadows_type", "Blurred Variance");
+
+            switch (shadowsType)
+            {
+                case "Hard shadows":
+                    break;
+                case "Poisson Sampling":
+                    babylonShadowGenerator.usePoissonSampling = true;
+                    break;
+                case "Variance":
+                    babylonShadowGenerator.useVarianceShadowMap = true;
+                    break;
+                case"Blurred Variance":
+                    babylonShadowGenerator.useBlurVarianceShadowMap = true;
+                    babylonShadowGenerator.blurScale = lightNode.GetFloatProperty("babylonjs_shadows_blurScale", 2);
+                    babylonShadowGenerator.blurBoxOffset = lightNode.GetFloatProperty("babylonjs_shadows_blurBoxOffset", 1);
+                    break;
+            }
+
+            
             var list = new List<string>();
 
             var inclusion = maxLight.ExclList.TestFlag(1); //NT_INCLUDE 

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

@@ -83,8 +83,8 @@ namespace Max2Babylon
                 {
                     name = gameBone.Name + "Animation",
                     property = "_matrix",
-                    dataType = BabylonAnimation.DataType.Matrix,
-                    loopBehavior = BabylonAnimation.LoopBehavior.Cycle,
+                    dataType = (int)BabylonAnimation.DataType.Matrix,
+                    loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle,
                     framePerSecond = Loader.Global.FrameRate
                 };
 

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

@@ -27,7 +27,7 @@ namespace Max2Babylon
                 int height = intArray[3];
                 int mipmapsCount = intArray[7];
 
-                if ((width >> (mipmapsCount -1)) > 1)
+                if ((width >> (mipmapsCount - 1)) > 1)
                 {
                     var expected = 1;
                     var currentSize = Math.Max(width, height);
@@ -124,7 +124,7 @@ namespace Max2Babylon
             if (forceAlpha)
             {
                 babylonTexture.hasAlpha = true;
-                babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2) || (texture.AlphaSource == 3);                
+                babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2) || (texture.AlphaSource == 3);
             }
             else
             {
@@ -190,7 +190,7 @@ namespace Max2Babylon
             {
                 babylonTexture.wrapV = 2;
             }
-            
+
             babylonTexture.name = Path.GetFileName(texture.MapName);
 
             // Animations
@@ -208,21 +208,19 @@ namespace Max2Babylon
             // Copy texture to output
             try
             {
-               
-                   
-                    if (File.Exists(absolutePath))
-                    {
-                        babylonTexture.isCube = IsTextureCube(absolutePath);
-                        if (CopyTexturesToOutput)
-                        {
-                            File.Copy(absolutePath, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true);
-                        }
-                    }
-                    else
+                if (File.Exists(absolutePath))
+                {
+                    babylonTexture.isCube = IsTextureCube(absolutePath);
+                    if (CopyTexturesToOutput)
                     {
-                        RaiseWarning(string.Format("Texture {0} not found.", babylonTexture.name), 2);
+                        File.Copy(absolutePath, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true);
                     }
-                
+                }
+                else
+                {
+                    RaiseWarning(string.Format("Texture {0} not found.", babylonTexture.name), 2);
+                }
+
             }
             catch
             {

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

@@ -21,8 +21,6 @@ namespace Max2Babylon
         public event Action<string, Color, int, bool> OnMessage;
         public event Action<string, int> OnError;
 
-        readonly List<string> alreadyExportedTextures = new List<string>();
-
         public bool AutoSave3dsMaxFile { get; set; }
         public bool ExportHiddenObjects { get; set; }
         public bool IsCancelled { get; set; }
@@ -95,7 +93,6 @@ namespace Max2Babylon
             ReportProgressChanged(0);
             var babylonScene = new BabylonScene(Path.GetDirectoryName(outputFile));
             var rawScene = Loader.Core.RootNode;
-            alreadyExportedTextures.Clear();
 
             if (!Directory.Exists(babylonScene.OutputPath))
             {
@@ -128,6 +125,31 @@ namespace Max2Babylon
             babylonScene.gravity = rawScene.GetVector3Property("babylonjs_gravity");
             ExportQuaternionsInsteadOfEulers = rawScene.GetBoolProperty("babylonjs_exportquaternions", 1);
 
+            // Sounds
+            var soundName = rawScene.GetStringProperty("babylonjs_sound_filename", "");
+
+            if (!string.IsNullOrEmpty(soundName))
+            {
+                var filename = Path.GetFileName(soundName);
+
+                var globalSound = new BabylonSound
+                {
+                    autoplay = rawScene.GetBoolProperty("babylonjs_sound_autoplay", 1),
+                    loop = rawScene.GetBoolProperty("babylonjs_sound_loop", 1),
+                    name = filename
+                };
+
+                babylonScene.SoundsList.Add(globalSound);
+
+                try
+                {
+                    File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true);
+                }
+                catch
+                {
+                }
+            }
+
             // Cameras
             BabylonCamera mainCamera = null;
             ICameraObject mainCameraNode = null;

+ 7 - 1
Exporters/3ds Max/Max2Babylon/Forms/ActionsBuilderForm.Designer.cs

@@ -35,6 +35,7 @@
             // 
             // ActionsBuilderWebView
             // 
+            this.ActionsBuilderWebView.AccessibleRole = System.Windows.Forms.AccessibleRole.Window;
             this.ActionsBuilderWebView.AllowNavigation = false;
             this.ActionsBuilderWebView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
             | System.Windows.Forms.AnchorStyles.Left) 
@@ -57,6 +58,7 @@
             this.butCancel.TabIndex = 1;
             this.butCancel.Text = "Cancel";
             this.butCancel.UseVisualStyleBackColor = true;
+            this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
             // 
             // butOK
             // 
@@ -79,7 +81,11 @@
             this.Controls.Add(this.butCancel);
             this.Controls.Add(this.ActionsBuilderWebView);
             this.Name = "ActionsBuilderForm";
-            this.Text = "Actions Builder";
+            this.Text = "Babylon.js Actions Builder";
+            this.TopMost = true;
+            this.Activated += new System.EventHandler(this.ActionsBuilderForm_Activated);
+            this.Deactivate += new System.EventHandler(this.ActionsBuilderForm_Deactivate);
+            this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ActionsBuilderForm_FormClosed);
             this.Load += new System.EventHandler(this.ActionsBuilderForm_Load);
             this.ResumeLayout(false);
 

+ 81 - 4
Exporters/3ds Max/Max2Babylon/Forms/ActionsBuilderForm.cs

@@ -1,12 +1,16 @@
 using System;
+using System.IO;
 using System.Collections.Generic;
 using System.Windows.Forms;
 using Autodesk.Max;
+using System.Runtime.InteropServices;
 
 namespace Max2Babylon
 {
+
     public partial class ActionsBuilderForm : Form
     {
+        private readonly BabylonActionsBuilderActionItem _babylonActionsBuilderAction;
         private IINode _node = null;
 
         private HtmlDocument _document;
@@ -15,11 +19,14 @@ namespace Max2Babylon
         private string _jsonResult = "";
         private bool isRootNode;
 
-        public ActionsBuilderForm()
+        public ActionsBuilderForm(BabylonActionsBuilderActionItem babylonActionsBuilderAction)
         {
             InitializeComponent();
-        }
 
+            // Finish
+            _babylonActionsBuilderAction = babylonActionsBuilderAction;
+        }
+         
         private void ActionsBuilderForm_Load(object sender, EventArgs e)
         {
             if (Loader.Core.SelNodeCount > 0)
@@ -34,12 +41,46 @@ namespace Max2Babylon
             }
             _objectName = _node.Name;
 
-            string currentDirectory = System.IO.Directory.GetCurrentDirectory();
-            ActionsBuilderWebView.Url = new Uri(string.Format("file:///{0}/bin/assemblies/BabylonActionsBuilder/index.html", currentDirectory), System.UriKind.Absolute);
+            // Set url (webview)
+            string assemblyPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
+            ActionsBuilderWebView.Url = new Uri(string.Format("{0}/BabylonActionsBuilder/index.html", assemblyPath), System.UriKind.Absolute);
+        }
+
+        private void ActionsBuilderForm_FormClosed(object sender, FormClosedEventArgs e)
+        {
+            _babylonActionsBuilderAction.Close();
+        }
+
+        private void fillObjectsList(ITab<IIGameNode> list, string scriptName)
+        {
+            object[] names = new object[list.Count];
+            for (int i = 0; i < list.Count; i++)
+            {
+                var indexer = new IntPtr(i);
+                var node = list[indexer];
+                names[i] = node.MaxNode.Name;
+            }
+            _document.InvokeScript(scriptName, names);
+        }
+
+        private void fillSoundsList(ITab<IIGameNode> list, string scriptName)
+        {
+            object[] names = new object[list.Count];
+            for (int i = 0; i < list.Count; i++)
+            {
+                var indexer = new IntPtr(i);
+                var node = list[indexer].MaxNode;
+                string soundFile = "";
+
+                soundFile = Tools.GetStringProperty(node, "babylonjs_sound_filename", soundFile);
+                names[i] = Path.GetFileName(soundFile);
+            }
+            _document.InvokeScript(scriptName, names);
         }
 
         private void ActionsBuilderWebView_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
         {
+            // Set common properties (name, is scene or object, etc.)
             _document = ActionsBuilderWebView.Document;
             _document.GetElementById("ActionsBuilderObjectName").SetAttribute("value", _objectName);
 
@@ -55,6 +96,24 @@ namespace Max2Babylon
                 _document.GetElementById("ActionsBuilderJSON").SetAttribute("value", _jsonResult);
                 _document.InvokeScript("updateGraphFromJSON");
             }
+
+            // Set lists of meshes, lights, cameras etc.
+            var gameScene = Loader.Global.IGameInterface;
+            gameScene.InitialiseIGame(false);
+
+            var meshes = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Mesh);
+            fillObjectsList(meshes, "setMeshesNames");
+
+            var lights = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Light);
+            fillObjectsList(lights, "setLightsNames");
+
+            var cameras = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Camera);
+            fillObjectsList(cameras, "setCamerasNames");
+
+            fillSoundsList(meshes, "setSoundsNames");
+
+            // Need to subclass this, then allow 3ds Max usage 
+            //Win32.SubClass(this.ActionsBuilderWebView.Handle);
         }
 
         private void butOK_Click(object sender, EventArgs e)
@@ -63,6 +122,18 @@ namespace Max2Babylon
             _jsonResult = _document.GetElementById("ActionsBuilderJSON").GetAttribute("value");
 
             setProperty();
+
+            _babylonActionsBuilderAction.Close();
+        }
+
+        private void ActionsBuilderForm_Activated(object sender, EventArgs e)
+        {
+            Loader.Global.DisableAccelerators();
+        }
+
+        private void ActionsBuilderForm_Deactivate(object sender, EventArgs e)
+        {
+            Loader.Global.EnableAccelerators();
         }
 
         private void setProperty()
@@ -80,5 +151,11 @@ namespace Max2Babylon
 
             return true;
         }
+
+        private void butCancel_Click(object sender, EventArgs e)
+        {
+            _babylonActionsBuilderAction.Close();
+        }
     }
+
 }

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

@@ -123,311 +123,199 @@
   <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>
-        /9j/4AAQSkZJRgABAQEAAAAAAAD/7gAOQWRvYmUAZAAAAAAB/+EAnkV4aWYAAE1NACoAAAAIAAMBMQAC
-        AAAAHgAAADIBMgACAAAAGgAAAFCHaQAEAAAAAQAAAGoAAAAAQWRvYmUgUGhvdG9zaG9wIENTNiAoV2lu
-        ZG93cykAMjAxNC0wNi0xOVQwOTowNzoyOCswMjowMAAAA5AAAAcAAAAEMDIyMKACAAQAAAABAAABLKAD
-        AAQAAAABAAAAggAAAAAAAP/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
-        AQEBAQICAgICAgICAgICAwMDAwMDAwMDA//bAEMBAQEBAQEBAgEBAgICAQICAwMDAwMDAwMDAwMDAwMD
-        AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA//AABEIAIIBLAMBEQACEQEDEQH/xAAfAAAA
-        BgIDAQAAAAAAAAAAAAAHCAYFBAkDCgIBAAv/xAC1EAACAQMEAQMDAgMDAwIGCXUBAgMEEQUSBiEHEyIA
-        CDEUQTIjFQlRQhZhJDMXUnGBGGKRJUOhsfAmNHIKGcHRNSfhUzaC8ZKiRFRzRUY3R2MoVVZXGrLC0uLy
-        ZIN0k4Rlo7PD0+MpOGbzdSo5OkhJSlhZWmdoaWp2d3h5eoWGh4iJipSVlpeYmZqkpaanqKmqtLW2t7i5
-        usTFxsfIycrU1dbX2Nna5OXm5+jp6vT19vf4+fr/xAAfAQAABgMBAQEAAAAAAAAAAAAGBQQDBwIIAQkA
-        Cgv/xAC1EQACAQMCBAQDBQQEBAYGBW0BAgMRBCESBTEGACITQVEHMmEUcQhCgSORFVKhYhYzCbEkwdFD
-        cvAX4YI0JZJTGGNE8aKyJjUZVDZFZCcKc4OTRnTC0uLyVWV1VjeEhaOzw9Pj8ykalKS0xNTk9JWltcXV
-        5fUoR1dmOHaGlqa2xtbm9md3h5ent8fX5/dIWGh4iJiouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqq
-        usra6vr/2gAMAwEAAhEDEQA/ANxbxuGazMPU1xqN7XPv329epnrIsb3/AFP/ALFz/wAV49+69/g6ziNy
-        f1P/AMlH/er+/de4dZlhe/6mP+uT/wAQffq9e6zCFv8AVP8A7cj/AHs+/Z631nWFrfVv9uf+K+/U6159
-        Zlhf+rf7c/Xj/G/v32de4dZlgf8Aq3+3J/4k+/DjTrx4dZlgYflyP+DEf8T799nXusywtxYtz/Qn37HD
-        r3WUQv8A6pv+Sj/xv37rfyHWZYG/Bb/bnn/effj1qvWQQMR9T/tzf/ex78OvdZhA/HL/AO3Pv1OvY4jr
-        KIHtwW/2BP8AxBt79UVp1rP5dZBTt/qn/wCSif8AiffsjHW+uYp3/wBqP+xPH+8+/de6yCnccam/25vf
-        /X49+691kFO5/tH/AG5N/wDX9+p177OuYpmHN2/2Bb37/B17z65Cne31bn/aj791vHXMUzEfVv8Abn/e
-        /fuHWq9chTN/tX+3Pv3Xuu/tW/xt/rm//E+/db67FKf8f8effutV65CkP+P+tf8AH9feuB68D119q1/z
-        /t/duvdd/an/AB9169xPz699qfzq/wB9/vfvfXuvfan/AB/2/vX2da4ceujSn/av9vb3vPW8+fXX2p/x
-        /wCSj78OvdcftWH5J/2J/wCIPv1QeHXuuP27f7UP9j/xv3vr3n1x+3Y/lv8Akoj3rr3XA07/AIL/AOPq
-        P/FffuvdcDTt/Vv9iffuvdcftmt9T+r+p/p/X6e/Vx17oODDd2/4M3+9+/dar1kWC/4P+P8AT37rfAV6
-        kLD/AIe/Vx16vn1IWDj6D/ff4+/de/w9ZlgA/H+8+/deBr1mWC/4Fv6/8j9+691IWAfj/ev949+Pz611
-        nWn/AMOP99/sPfv8PXusywAf8Utc/wC9D36nXusy0/8Arf7f/ivPv1et46zinH9P9v8A8i9+691lFPf6
-        Dj/bf8j9+HXh1mFNfm304/r7317h1nFOPwP9e3/E8e9V618z1kFN/tv9h73jrf2dZhTfnSPej16mfn1k
-        FN/Uf77/AHn37rfD7OsopjwLe/fPrXWQU3+H++/3n377OvY610W/4U4fy9kyJxbdd/K5apch/DSf9HPW
-        PjFSKhqXl/8ATSD4xMLXt+Qfpe3v8PXutiPDVVPm8Vi8vSpNHBlsbQ5SnhqVWOpjpq+miqoVqIkklVJl
-        SUBgGYBrgE/X37r32dOgpR/qbf77/H34fLr3XMUn9QP94/4179w63x65il/w/wBtce/V61ToDPkz3zsr
-        4p9Ddm/IjsXG7my2yOqNunc+5Mds2gxuT3PU45a6jx7Jh6DMZfAY2pqVlrlYrNWU66Ax1XAB917qpz45
-        f8KC/g/8oO8es/j/ANe7H+SWP3r2ruWn2ttyu3XsPYGO25TZKqglniky9bi+1s3kKajPhZS0VJOwb+zp
-        9Xv3Xur2BS/0A4+v++t791rrsUv+A/33+wB9+p1ulevfaf1H0/p7917PXX2l7cf77/E+/de6oY7/AP8A
-        hQz8Hfjf3Z2l0Lv7YfyYq96dR7xy+yNy1e2thdd5Db1XlcNJ46iowtbkO28VXVWOmNjE81LTyEMNSLzb
-        3XsdW9/HTu3Znye6N6w+QPXtFuDH7J7Z2rR7v23RbroaLG7jpcbWyTRRw5igxuTzNBS1qPA2pYaqoj+l
-        nPv1fTr1MdDP9re9x/vv9v79SnXgOuBpf8P99/vHvw69gceHXX2n1sDwCT9TwPqf8PfuHXuJ6xtS/wCH
-        +w9+69jy6xmmH9AP99/re/fPPXs9dfa+j6D9X+w+n+v79Xr1DSvn0E/ju7cf2m4/p6v9b6e/Hr3WVY/8
-        P9t78OvdSVT6cf7D6e9daz+XWcRjj/invYr1vqQsXNh/xv8A4n377evDrOsX4/r9fp795de6zrEL8j/e
-        vfv8PXh8upCRfn8/j/effuvfLrOsX9R/jf8AHv3l149Z0iHH1t/vv6+/f4etnPWcRf7EfgfT37rXWZYu
-        B/vvz7914+vWdYRb/jX/ABr37r3HrOsI+h/5H718+vcOsqw8gAH/AHw/1vfuPXuIr1mEH4tz+fe+vZ4e
-        XWVYB9f6f61vfvt69w6zCAccW/xt9f8ADjj37z691lEH+w/2Hv3Xh18eWs/4/Gp/8O2X/wB3h9+691an
-        /ML+VXya6p/mBd+jrX5DdzbGG3N17cGDpNs9lbqxlFiYo9j7Xljho8ZT5NMfDShpG/a8RjIYgggn37r3
-        WyZ/It/nUbt+XWdk+J3yxymNru9qHB5DOdadnQ0VDhU7UweCgarzuF3Jj6JabGU2+sFjlaqWWkihhyFF
-        FK5iSWF2m917qs3+bJ/woB7y392tvHor4Sb2q+pemtjZbKbVzPa+3Y6Ydg9qZvGVMlBlq7A5yoiqW2hs
-        qlrIHjoXoRHkK1QZ3nSN0gT3XqdUfbH/AJj3zz693Uu8dp/MHv2DcMdauQqZMp2VuDdGPyFSsyzn+NYD
-        ctZl8HmIJJVBeKqppY3HBUjj37r3Wynh/wCbrV/zE/5Qfz96r7mo8Hg/k91R0OM1mpsFGmOwnaGyJNz7
-        cx/9+cNhmkZsTlcZkpI6fLUURenikqIJoSsc3ii917rXg/lVPJH/ADBPjFJC8kU0e9MjJFLC8kc0Use0
-        twPHJFJF+7HLG6gqy+oEXHPv3XukL1n/ADBfm30lvVt3dafKfurEZagysrtBkN+53dGByCUtW3joc3tr
-        c1Zl8FmKAKgUw1EDrb6WNiPde62kN0fzxe0Pkf8Ayh+8O4+ttzU3SXzO6E3f1PgOxv7o0lHLQzYPee66
-        fG4/fG0qDccGYiTCbohgqaWeBxPJQVULpr0vEze690Xv+Rn/ADM/nd8pP5gezOpO+vkRursjrrIdd9mZ
-        qu2xlMPtOjo58lhMEtRiquSowu3cfWq1HUPqUeVULEXubD37r3Vd3yl/nK/zN9ifJP5F7M2p8s97YXbO
-        z+8e2ts7bxEG39hyQYnB4HfmexmIxsDVe05ahoaChpY4k8jM+lRqJPv3Xuq6vnVn8zuz5cd67p3FXy5X
-        cG5d00O4M7k51iSbI5jM7YwWRyddMkEcUCS1dbUPIwRVUFuABx7917r6O/8AJ2hLfyv/AITN/Xo/A/i/
-        0rsmPfutHqyc0459Jv8A7x78et9EB/mW/N3bf8vb4nb5+QOVxUG5N0RT0Wz+rtoVEskFLunsncaVK7fx
-        +RnhInp8JQRUk9fkHQiT7KklWM+Rk9+qet/Lr5xvfH8yX5w/Ine1dvrsv5Mdpw1tXkZK3Hbe2dvHObF2
-        XtvyPqhoNtbY25kKCgoaalRVRGcTVMgQGWWR7sfda6tt/k9fzs/kX1R391p0D8mOzdwdv9B9pbnw2wqf
-        Ndg5Gq3BvDq3PbgqY8TtrL4ndNa8uVrdsPl6inp62irJJ0ggk8sBjMZST3Xut/Mwf7Tb/XA/4j37rXHr
-        rwen/HV9Px9Pfqj8uvdAlp9TH/aj/vfvfl1vrOqj/Y/1P/Ece9dez1IVR/sf6+9/Z17gepKKOD9B/X6/
-        7x718vPr2K08+pCpb/Yng/8AG/ehx691nVP8Of8Ab+99e6kqvPA/3v6f7379Tr1adZ1X/Dn/AG/v329e
-        Ges6qPz/AK3/ABv37r329Z1QW9+/w9b/AMPUhUFvfvt60MdSFW/J/wB9/tx799nXvOnWZU/w4/23v3W/
-        t6zqg/w/3v8A3n37y61Tz6zKguCP9749+/w9e+XWcR/4f8U/2/v1R17rMqD6WP0H9D/tvr71x691mVBf
-        /jZ/5F73SnXusqxi4/5H7917r48W9cXktodjbvwmcop6TMbT35uDF5nHSo0VTTZLAbjq6XI0TxuA0c0V
-        TSOliAQRz7917rc7/mebr/lAdufyx9yd07Urvj9XfIbsTbW0Nz9dZbZdTthfkBX9u1VHgqetod2QY+Rt
-        5pTY6himgy1Nk1FJBBDpUCQQ+/de61RPguOz/wDZrepZOmzXjsWBt+VOD/hvl+5MFP1fvabNpaIF2hkw
-        aVCsDZSDYkA39+690WvZP92f707O/vx/Ehss7j25/fM40Mcx/dY5Wj/vIaAEhzkv4R5vHzq8tvz7917r
-        cy/m89V/ypN+fyx8nvX4V0fxtl310dQ9R7w2rlOlU2zHvij2LurdWH2LV03Y0uDVdw1S5aPLEzLni1aM
-        hBrNpBJf3XutVH4oNuiq3T3Ft7as9YtRun4tfIjF5Wion0tlMJjdhVO6q2jkTyRrMqNt1JgvJ1RAqC1g
-        fde6Vn8untTYHSvzh+L/AGd2pWxY3rPbfamBO+8pMJWpsZtbLpUYTK5GrEAaYUNHTZIyTsgLLCGYcj37
-        r3V7X/Chjbf8rjb3V3x3h+GydBUvdp3C0eRp/j3V7YqsdP0+m3a29Vvx9ny1GNkyRz/2X8PmrH/iEt6g
-        ksuoj3Xutffo6LcT9K/MqTGx1jbci6d2Ad0tEt6JGbvTrwbf+8JB0ynIa/Dp9X6/7Or37r3Vgf8AIG7i
-        6q6N/mTdc727k37tfrbZr9f9p4Rt1byylJhNv02Wye15f4bSVmXr2joqGSvenaOJpHQPIQgN2APuvdVm
-        fKrc2B3p8lPkfvHauSp8ztjdneXbm5NuZikDCkyuCze/M/ksTkqXWkbfbV1BUxypdR6WHHv3XulP8yf+
-        yme2f+1jt/8A94zbnv3Xut4T4rfOfNfHD+Wz/La6E6G6uf5A/MLvXoynqusOpBmYdu7c2/tLb9VkZ90d
-        sdsbqlDx7S6521TklpmHkrZx4YiCHdPde8uhJ677d/nI742llu5+pe1f5eHyswu28xXY/O9R9c4Xszae
-        DzGTxkrDK7Q6579y9VLtfcuWxpvTNWeI0S1q+KR1Ksvv3XuqiP58nzQ2j81P5fHxs33tjDbi683Ntn5S
-        7v697s6W3iY6TenVHbWzNmZeizO0N1Y4eN5pKCSaWSjq1RY56eYNZJC8ae690TT/AITl9TdYdr98/L2j
-        7P692d2JQ4T4ibjqcTQb127i9y4/HVWR3Jj6Ouq6Kjy0FTBS11RRJ4TMi+URMyBlDNf3XuqBMQzY/e+L
-        ahZqZ8fvGiaieJiHpmo8/EaV4nJLK0BiUqfqCB7917r7FRj/AN99Pfv8HXqmtOsegX/w/p/vj7917HQC
-        Xsx/1z9P9f8APv3XusyEXF/+I/p/tvfv8PXjjh1JX6/1/wB9/sffuvceHUhT/rf7a/v3Xq+nUhT/AIj6
-        /wBP+K+/de+3qQpH14P+2/4379177OpCkX/5H79n8+vcOpCn/b/1/p/t/wCvv329a6zo35+vH/FPfqDh
-        1vj1IU3/AD/vv949+68ccOPWdTxxx/xPv2D17qQCBb6W/p791456zKRf/X/H4H+8e/Hh17PUhfpYf737
-        91r/AA9Z1P8AW5t9P9b/AHr3rz+XW/l1mUj/AHw97631mU2sf99/xPv3Wvl1mBH1/wB9/wAT78K/l17r
-        OrA/0/H+x/3n344691pe/wA6r+Q33DvDuTe/y4+FW1Yd947sesm3R2t0rjKino92Y3etT6s3uvYdLVtT
-        0Wbxm4nX7qroBKlVDWvI0KyJIEj9x691rSYT+X1849wboTaWH+HvyJn3RNKkBx8vUe7cdKhkkEQeqrcj
-        i6OhpaYP9ZZZkiVQWLBRf37r3W5d/Iv/AJI+6fhtnpflf8qosanfmT25ktudfdZ4+ro8vQdVYHcVMlPn
-        8rn8tTGWiyO+c5ji1GYqZ3pqCikmTXLJM3i917qoL+bd/IL776V7Z3n3b8O+v853J8ft95vK7qm2Hsyl
-        /iu/+oMnlqiXI5TAJtuE/wAR3NsxayaRsdU0ST1NNCwp6iO8azTe691W90P1T2Z118I/5m8+/etN9bEi
-        qti/HXGxVG7dnZ/bUFVkqLv7BtV0MNRlsZSw1FbSrIC8avrX8/Qj37r3S/8A5CeIxef/AJpvx4wOcx9J
-        lsJncJ3Ng8xi6+JZ6PI4rNdQbyxWRo6mF/TJDUUdY6kH+vv3XujGfzI/+E/fyt+MfZG6d1/Gfr3cfyD+
-        OWbzVdkNoDY8Dbg7H2Fja2eSpptqbu2nABmMquIR/BBkqCKoiqYkUyrDKxT37r3VaHUP8sf59d1bnpto
-        9e/Ebuo19TWx0dTkNzbGy2xNs4qWRyhlze5d202Gw+OhhIJcvKWABspPHv3Xut0340fyA9l9T/y1fkT8
-        Wd67sxuV+Q/yn25hcjv/ALNx0Ek2B2lu7ZdQuf6z2xtlKmJauo2jtDcSF6qZkinyT1E8hVAYUj917rUS
-        3f8AyWv5m20ewcj1xW/E3f2ZqKGukpf74YNsPX9bVtDEdZz0W9ZclBh6XCCn/deSpaCSFQQ6Kyke/de6
-        rEy1BLjq3J4p5aWqnx9bW4ySbH1EdbQ1E9FUy0byUFXCTFWUk0sRMUqXWRCGXgj37r3VqXyi/l7fObev
-        fPYW7Nn/ABL773RtfcEu28hgtw4PrfcWQw+XoZNm7eWOsx9dBRtDU07shAZTYkH37r3VsWzerPkFsvq3
-        5C9HPhdz7T+Xe7P5LPQmJ6B2RkKOoxG/v9He0+zNxz/IzrzadK8EFfHuyswnkEmNg1Vs7SLxxqX3XuiI
-        /FP+cF81dn/EXHfyuehurNsbg3huytzHWHVe8cfTZ9e09oQb8ztTUV+Eods0YFDPnsbl8tVPT5KoMLY1
-        X1SRuYFYer5de6H/APmkfH/v75Md+fLJvjh1tvHurF7U7n+Oe2u3x1Rtyu3Nip/kLtT44/wbtXMePEUk
-        sEWQx+ReOjyTq1xWNpkvIT7917o0P/CeH4h/KTovvD5a5fub4/ds9X4vc3xXye3Nu1++NmZjb1HnM/Ju
-        ijnTDYybIUscdXkngBcRKwfSCeQOPde6oOp/5bX8wGPdsVW3w1+Rgpk3QlU03+i7cvj+3XMiYy6vs7FP
-        ENV/pb37r3X1V2P+P+w/J/23vVfTr2Oseoar/wC8X/w97690XnX62t9NTX/1tR9+8+vdZlcf1v8A65Nh
-        /sPfuvdSVkF/rfj/AH3Hv3XuGOpCuL/X/ivv3Xus6Pb/AH1/+J9+8+vdSVkH/Gv99x79nr2Os6uBxcW+
-        v+H+39++3r3l1IV7fTn37j16h49Z0k/r/tvp/Tn3o469jqQslx9eP634H+9X9++3r3WZZB/gf8Tz/vH0
-        928+vZr1nWQH/fce9H5dePWdZPpbgDi/+w/Hv3XvLrOsoP8Avre/DrxPWUS/4/7f37z631nWUfg/7G/H
-        /Ej37PWusok/xHH9Pr/txf37reOswlH5+n9ffvs61UdZVmH4I9+691mWYf4f8a/1x9ffj8uvU6yeb/Y/
-        73/vHvw619nWQTj6cD/ff7a/v2et56yCYW5/33+8W9+690Vr5ofE/r35xfHfevxr7QzW6dv7L3zUbeqc
-        plNlVeOoNw08m29wY7cdEKOpymNytEizVuNRZdUDFoywBBN/fh175eXVaPw4/kCfEn4U/IjYfyU6z7J7
-        2z29Ov0z6YjF7xz+0q3b1T/eLBZDb9Ya+mxm0MZWyeKjyLtHonS0gBNxx799nXuHV7nn/wB8OP8Ae/fv
-        n59epXrv7j/H/ez79T9vXuvGo5v/AE/339PfvkeHXuvmg/PD+bL/ADBs32X8qvi7kPlDvKLpnG94d3bG
-        TAY+m23hs5PsiDfe4cTBtLIbzxeIot3Vu3kw6Cl8ElYRJTftuWU29+690w/yiv5Vnbfz7712fn8vtLM7
-        c+LWwdyYrOdpdk5WhqsbidwUuHrIq9OvNkTTJA24Nw7helEFRJSloMZSu80zrJ4Ypvde6+mlB4KWCClp
-        40hp6eGKnp4Y1CxxQwoscUSKPSqRooAA+gHvfXseXVc/8yH41fH/ALk6xwfcfbPbtX8XN8/HDKLvPqr5
-        abfzVBtzcfTOZydTR4h1qclklOOy20dz1k9NSZDFVf7FaGVFKOQ3vXXuqNIN/vlcrn4Mz/Oe/lqddndN
-        PUY3eHyG6L+PHW+y/lXvLEVMSU9WaveBzJx+3s5VU2sff0zzSwu+qMAhdPuvdbFfwg6R+Ofx8+N2wNmf
-        F2qpM71XlaOTedN2CmXG5sz2xnN0MK/N9nbq3cf3t17k3bVnzz1jnTpCRRLHBFFEns/KnW+HSQ+UP8yj
-        4R/DbIU+E+Q/yE2VsXc9VTmph2ZAcpuve/2xjWSKpqNobQoM7uGipqkOBFLPTxRSm+liAxHutZ8+kd8U
-        /wCat8GPmvvaq62+O3dKby35R4Ks3LNtTJbO3xtDLHCY+ohpq2ugi3Zt3DRVUVLJUxF/G7lRIv8AiB7r
-        XVg7S/09+Hz49bwfs64+Uf15vb6f4X/3r37rflTy6LsZRrb8epvz9fV791Ufz6zLL9Of9seffut9Z1lP
-        1v8A8Qffut9SFm+n/G7/AO39+8utdSFm/P8AsPrb/ePfuvVx1nWa3N/+J9649ap1mWf/AHv+t/8Aefx7
-        2Ot/b1nWf8A/7zf/AJH799vXsdSFm45Nufr/AMj9+weHXs9Zln/xt/sSLf19+698vPrOs/8AUk/77+vv
-        32de/ZTrMs/+w/rz9ffuPDr3yHWZaj6c/wCtYj37r3WZZ/8AEH/Y+/dep1mWoH9f95Nvfuvf4esq1P4J
-        /wB5t+f6W9+691lFR/U/7z/xS3v3XuGOswqP8ef9f/iLe/eVetdZFqLEc/X/AB/4p73nr2fLrIKn8g/7
-        z/xXi/uv29b65ipP9f8Aef8Ae/e+vY6yCp+nP+8/n3r5de65fc/7Uef6/m3+w97691zFT/iP9uP+JPv3
-        XqnrkKr68/77/effuvf4eu/uf6H/AIn36vXq+vXf3X5v/sf98Pfj17r33Y/qP9v/AMa9+PWuiz7v+HPx
-        G3/uqfe+9/jN0Xuvd9XUPV1u5M91fs3JZevq5TeWqyNbVYiSavqZD+qSYyO35Pv3Vvn0YPC4zC7bxdFg
-        9u4rGYLC4yBKXHYjD0NLjcbQ00ShY6ejoaKKClpoY1FgqKAPfvl1r7enP7oW+vP+v798uvf4Oqkv56s3
-        l/lRfMJD6r7M2lcckW/0nbIveySXA/1v9cj6j3WqZr18yZ449Dftp+lv7K/0P+Hv3W+t+Tsj5/ZX4Afy
-        IvifvvYQoz3L2L011t1d1IaxYaimweey226iqyu9amhkstdBtHB0U1RFEQyPXPTJIDEz+/de60P917r3
-        Rvzc+e3tvfcOY3bvDdOUq81uTc+4a+oymbzmWr5nqKzIZLIVTyT1NRPNISSTYXsAAAPfuvdX7f8ACZeZ
-        F/mQ5JQ6GRPj72ISgZdYDZjaIBK6wwH+wP8AsPz7r32dfQaNT+bn+l/+I97+XXj6Y66+5Gm/51f4/wBP
-        9b3rPXvl0Xg1A1sPp6m5/wCQj9bj37reesqz/wCIP9PfutH+fUhZxxz/AK1vzz9OPfuHXj1nWo/x/wCI
-        964Z69jy6zrUD3uvXqft6zLULf6/7b37r1PTrOKi/wDT/W/3x9+4de6zLUc8n/e/94Hv3Xus61PH1B/2
-        H/E/19+/b17/AAdZlqePqD/t/wDinv1etH9nWZam4+v+9/8AI/futjrKKm35/wB7/wCKe/CvXusy1P8A
-        j/sf99f349a6yrU/7V/vfv3W+sgqR+Tf/b+/de6zCq/2of8AE/7zf37r3WQVX9Sf9sf+K+/f4OvdcxVD
-        /fG/v1PPy699nWQVQH/Ffr/vdvfq/s631kFVz+r/AHv6/wC829+8utZ65ir/AMef6/1/1/z79/g69Xrl
-        95xyef8Aff63vwp17ieuYq/6n6/6/H/I/futdd/d/wCt/wAT/r/Ue/UI638+uX3f+IB9+Br1uvXf3f8A
-        tVv9v799nWuu/vLfkf7z/wAT79/h69Tr33n+N/8AW9+8+tU6993/AI3/ANv/AMV9+635V66NX/tX+wuf
-        fuveWeuvvP8Aah/rX9++XXuPVT388ioEn8qn5goSp/35e0zyQBcdnbHI/VIg1X+nN7/QMeD7r3XzQn/Q
-        3/BW/wB6Pv3Xutmn+bTt3OV38on+UFu+n877b29tCLb+VA1fbQZjcnXeIqcOz8FVlng27Vqp4uEP19+6
-        91rLNwpsGPH0TTrt+dGr067fS/F/fuvdfTw/lrdHfDXavxc+OHaXxn6z62pYsx09gGo+zcVtzDyb8y9V
-        mMfSHfK7g3g1K24arLVW6KSojyUM0/7dTAYSirEqL7z60erIDVH+v+8+/dbqOuvuvTa4/V/sf0+/VxXz
-        690X9qj1tz/aP54+p/r73jrw+fWVaj+p/wBsf99f3rr32dZlqP6G/wDh+f8AbfT377evdZlqb/77n/iP
-        futY6zLUj+v5/r/yP37q3yHDrMKn+ptf6c/8SffutV/Z1mWpP+v79+fXqY6yrU/4/wC8/T/W+nveOvcM
-        dZ1qv6H8cm/vR691kWq+nP8Asb39+68eswquOD/xA/3j37r3WUVX5v8A77/D+nv3W88Osoqvzf8A3n/i
-        CffvPrXWQVd/7X+tz9f+Ke/de+zrKKq355/1x79x611zFX/Q/wCvz+P9gPe89b6yCr/x/wBb3rh9vXus
-        grP6t/tz/wAU9+631zFX/j/rc2/5H79Xr3DrmKwfg/7Hj37I6159chWf4/7z/wAU9+z177OuQrB/X/ef
-        +N+/H5dep1z+8/xP+Hv1Ovdd/eX+p/x+o/3q319+6912Kw/1P+3H/Ee/de65fef7V/vP/FfeuPXuvfd/
-        7V/vP/Gve+vddfef7Uf9uPe+HW+u/vB/qj/t/wDjfvWetZ66+8/2r/effuveXXX3h/1Vv9jyffuvdVVf
-        zuqoN/Kx+X4Z9N9k7VAu5W5bs7Y6heFcksTYCwB/JA5HuvdfNff9Df8ABW/3o+/de6+hRt74nYb5rfyO
-        +legskabH5/K/Grrzc/XOcrI1C7f7G2zhos3tDLGSSnqHhoa2qQ0lY8a+R8dWzqhBcH37HXuvnwVVLU0
-        NVV0FbF4K2gq6qgrYdQbw1lFPJS1UOtfS4iqImW44Nrj37r3W5r/AMJfPkXuPP8AV/yE+Mmdr5q3DdYZ
-        7b3ZmwIp5JJmxGL7AbJY/d2Ip2klYQYtdw4aCsihRVVamvqXPMnv3Xutqs1f+P8Avv8AYe/Vr17r33h0
-        31f2v68fT/W9+698vPoBzU+tueNTfn/av9h79jr3WQVQsbG/+3P/ABr377eHXusq1X+P++/3n34+nXus
-        y1X/ABr/AH3Fvfqde4cOsq1Q/r/vN/fuvdZlqfpyP9bk+/de4dZRVf7D/b/8V96+XXq+vWRav/H/AGB/
-        4oOfe+HXusoquPr/AL3/ALYc+9de6yrV/wCP+vb/AIn3v7Tnr2esgqv9q/2BFv8Akfv3Xusoq/8AH/Yi
-        /v3Xv8HWQVf09X4/x/42PfuvcD1kFWP9UP8Abke/daFOsgqx/X/Yn/fX96889er1zFZ/tX+8+9+Wet9Z
-        BWf4/wC8e/de65is/qb/AO8e/fPr3ljrmKwf6of7x7917h1yFYB+f945/wB59+69mvXYreeD/sf+N+90
-        8uvdchW/4/7z7117PXL7wf6r/bk/8V9+68Ou/vAPyP8AY/74+/Z691y+8/of9tz/ALyPfutde+9/xH+2
-        59+62OHXf3v+P++/3n34fLr3ljr33v8Ajf8Ar7917yp59e+8/wAf9459+8+vHPXvvP6H/ePfs+fW+uvv
-        T+GHv3y6qadaS/8Awoi+W3yFi+R25PiVTdj5Cl+PeR2N1lvOv6+pcdiKWDIZ0tXVjPX5qnoIs7kMcchQ
-        xVH2k1RJTieJWCgqAPdb61ssJt/N7szWH2ptnG1eZ3JufK4/bu3sRQRNPXZXOZqrhxuKx1HAgLS1NZXV
-        KRoo+rN7917q9757fM/5wfB7tTcfwV6v+QO5to9W9fdFfHzYM2BxlPgK2pw88nx/2LQ7sXae6qvDvuHb
-        9Pl6+Wea1JUR/byyl4DHJeRvde6oO55JJYkklmJZmJNyzMxLMxPJJ5J9+691uSf8Jkvj1uXaXW/fPye3
-        BQ1WOw/a2SwHXHXjVCPD/G8HsWoyVfuvPU8UiqZsXLuPIw0VPOvokmoKlQTo9+69jraZNZ/iP9gffuvD
-        165fe/tf4+T/AGP6ffqHjjrfQItUWkfm1nb8j/VH/W9+6112Knn68f48/wDFffsefXs9ZhVH+v8Atzz/
-        AL37917z6yCq/qb+/ZHXqHy6yCq/xv79Xz691lWrtxc8f7H3759e6yLV/wC+v/vj7914jrIKr+rfT/H/
-        AJF79WnXqdZBV/48fX6/8V9+wevD+fWUVZt9fp/vv6c+/fbx68M9ZFqz/X/effuvdZBWW+p/2/8Avh79
-        9nXuPDrmKy/5/wBhcf8AEn3uh691zFZ/tX+3I/3j6X964de65ir/AKE/7G//ABU+/de65is5+v8Avv8A
-        iffuvV6yCsv+f97/AOI96+3r3+Hrl95+dRP+3/43738uvfbx65fefm5/3i/+9e/deHXMVp/r/vI+n+2+
-        nv3Xj1395f8AP+29+4dex1yFYf6/7C//ABX3759e67+9/BP+w+o/4i/v3Hr3XL738E8f77+g9+p17PXv
-        vb/Q/wC8/wDG/fqde4DPXf3v+P8AvP8AxTn37r3Xvvfxf/Y8e/de+3rs1tvz/vP/ABHvw49e66Nb/j/v
-        Pv3Xjx6997/j/vP/ACL37r2euvvf8T/tx/yP340691rbfzTv5M/fXzm+Tv8Ap56v7W6h23h6rYu1tpVO
-        A38+9KDJ0tRt58l5aunqtv7d3PSVkFStaCqlaYqQQbn1H3Xuhl/lu/yQur/hfvLH929ubwoe8u78MPLs
-        +anwUmH2F1vWyRSwVGU29ja6rra/O7iMMpWHI1gh+1BLQ08ctpB7r2Oi/fzMv5H3cfzI+UG9vkp1P3Z1
-        nhX35jdnUWQ2T2Ji904hMPNtHaeI2ostFuTbNButsjHkIsQs5WSggMTSFQWABPuvdBN8Zf8AhNZjcLuf
-        H7i+Wfd2N3lgsbVw1T9bdQ0GWxmMz6wyxS/Z53fG4oaDMx4yo0NHPFRY6mneNvRURnn37h1759bS20tv
-        bX2DtfAbJ2TgcVtXaO1MRQYHbe3MHSQ4/EYXDYynjpaDHY+jhVY4Kamp41UD6m1ySSSfde88dP5rbi2o
-        /wDGv9v79TrZ4465fdjw21H/ADn01cfp/wBf37zr5fZ1rH5dA29SPI/P9tv+hj/gPfuvddrVfi4/23v3
-        Xusgqv6Ef7f/AIp79wPz63x65iq/N/8Ae/8AiT791o9ZhU/i/wDtvehnr3Hrl91/j/vP/G/ex/Lr3WT7
-        sX/V/wAV9762MdZFq/8AH/ifejjrXWT7u35/3m3vw9evV65iqtbn/Yf8jv79nr329c/u/wDarf7H37y6
-        9ny6yfd/48f7E+/fbx69jy65ir/x/wB9/sTf36vXuuQrP8QD+bX97/n17rmKv83H+3v/ALwePevPr3XM
-        Vg+l/wDff7zf37z68OuYrP8Aav8Aebf8U966959d/efm/wDh9f8Ajfvfy6959chWD63/AOJ/3s+/dbz5
-        9chV2/P+P++N/futfb13959eRf8A339fr79X9nW6+nXf3v8Ajb/Y8f7373jrQx1yFZ/j/sSf+N+/cOvc
-        evfef4/7yTf/AFvfuvdchWD/AFVj+fz/AL39PeuPXuvfefnUP9f377evdd/ef7UD/tj/AMT79/h69119
-        6f8AVAf05t7917h1773n6i/v3+Dr3XvvP8Rf37/B14Dr33v+I/x/3w9+HWj8+PXX3n+1D/b+/dbpTPXR
-        rP8AH/ef+N+/fLr3Hj1197/Q/wCwBP8AxPv3XuujWf4j/b/8V9+oB14fLriav/Ef7Ye/ceHXs9cfvP8A
-        H37/AAdeBp1k+8/a/wAPJ9f+Qf6e9U/Z16nn0Er1P7j8/wBt/wDoY/63u3Xvn14VB/23++/rb3rr3WQV
-        P+J/2wt79Tr3WQVN/wA/7c/8R79w69/g65CqH9f+J59+49ez59cxU/T1f77/AHj37HXvL5dcxU/1PP8A
-        r/76/v1f2de+zrmKo/kkf778D377OHW/Lrn91x9f8Ofr731rrmKr6/X/AB5Fv8Pev8HXv8PXMVR/qR/v
-        v9697x1rz65ir/x/4j/eTe/vXW+uQq/8bf7H/in19+8vl148OuQqzf6n/b+/fPr3XP7v/H/ff429+FOv
-        dcvu7fk+/V6959dir/x/2PHHvR6959c/u/xex/1/e89aHQUb++QPTvV2UosFv/sjau2M7kKCTLUuCr8l
-        E2cfDwNOk+ckw9KKjJU+CgemkV62SJKRGRlMgII9ppry1t2CTOquRWhOaetONPnw6PNt5c37eIGutstJ
-        5rZXCF1U6NZpRNRopc1BCA6jXh0vttby25vPA4rdW0c/h90bZztHFkcJuHb+Ro8vhctQzi8NZjsnQTVF
-        HWU0luHjdlNvr7ejkjlQSxMGjYVBBqCPkRg9Ft5Z3e33MllfRSQ3sTFXjdSjow4hlYAgj0Ir1GXfe3JN
-        4z7AXKKd3022qXeE+H+2rA6bcrspW4WlyX3hpxj2WXJ4+aLxiUzApcoFKsfeLGZPBB/UC6qfImlf2jq3
-        0V2LEbkU/wASMpiDVHxhQ5WldWFYGtKZ41r0p/vD+WP++/2/u/SQ9IbdPa2x9k7h2DtXdW4qbC5/s/O1
-        +2dh0NVBWsNwZ7HYSv3FV4yKrgppaGinGIxkzx/cywLNIqxRl5nSNmZLiGF0jkYB5WIUepAJpX7AeP2c
-        ejKy2jcdxtbq9sojJa2UQkmYEdiM6xhiCQSNbqDpBIrqNFBIWlTlYKOmnq6qeKmpaWGWpqamaRI4YKeC
-        NpZpppHYJHHFGpZmJAAF/bxIGTgDovRWdwiAlyaAepPADpK9e9l7R7U2VtrsXYWaTcGzN4Yqnze280lJ
-        kKBcli6rV4KpaLLUlBkqYSaT6JoY5B+VHtqGaK5hW4gOqJxUHhUH7c9Ld12u/wBl3GbadzjMW4W8hSRC
-        VbSw4iqllNPUEj59LL7y/wCT/sDc/wDFPbvSAfPr33n+P+8n/ivv3n17r33h/wAf9uPp79149e+7+nqP
-        +8f8V9+p17j177s/1/3v3qo68euvuz/quP8AX/4r735dex177v8AxP8Atx7917z64/eH/Vcf64/4j37r
-        fTbkty4vD/YHKZKjx38TyVJh8b95URU/32Vriy0eOo/IymesqTG2iNbswU2FgfeiypTUQKmn5+nTkUE0
-        +rwkZtClmoK0UcWPoB5npx+7P9f96/4r73TpoddfeH+v+w/3319+Pz69x64/d/7V/sfp/wAa9+49eFOs
-        v3g+3/Uf89/h/qP9f375efW/ninQZPP+44BH63v/AK2o+9dV+fXQn+v+93/3w97+3rfXMT/Q3v795de/
-        wdcvuOfr/vZ/31vfvLr2OuYqLfn/AJH79149chUc/Xn/AG/v2PLh1rrmKn/G/wDrf1/1/fut/b1yFRf8
-        /wC+/wBY+/V8vPr3l1yFR/iP9e/vx69g9chU/wCPP+v/AL63v3Dr1OuQqf8AHn/Hn/eOffuvHrkKn/Hj
-        8c8D/b/Tn37J691zFTb8j/b/APG/fuvGn59cvuv8f+Kf8b9+HXvLrsVP+P8AxN/979+rTrXXIVP++/43
-        b37r1eu/uj/X/ff4Xt731sfz65Cqtxf/AHq/vRp175+fRe4MVNs2o7i3909icP2r2Dv7ftNW7nx24t9U
-        +1afG1e19l4HZ9LtGn3DR7X3RPi8fhItvRSx0MtM+iqyVVOWBlIZCEMJlntQJJ3epBalKKF0g0agFOFO
-        JJ8+hS10m4LYbZvzyWW12tsVjaOHxCwkmklMpQyRB2YyEawwqkaJ+EUKD0Pmt/7updu9N0mZr+ltwbl3
-        l8lO2O/6TZVLt+nzOw9xYrsnbSnqDYdXko9zYuOhy1f2VBkazcUVPI+QpYXqKcUcuTUUhXZNPIFtATDI
-        zyvLppVSHHYtaihL1L0yBUULYHnMsG1WMk+/sg3G1ht9vtrIzGQrPG1vIPqpgvhtVFtzGluWAjZgjmRY
-        D4mDc+E78607l79ptkdhbg7S3QPi7sus6nrdwUGy5exsLT1na25afP4COqnxu3tnbuzePUVNTt6TJwgz
-        1M0VLWmZIxJLqSO+t7ucQyNJJ9OpSoXWO9qjgFYjimrzw1eJtZ3fK+77BtT7jaxWVp++ZlugjTfTvS2i
-        MbkBpJokbC3AjOFBeLSTpUZukN+tP21jtuYLufsvcWPr9oboqt5dTfJDbtTsvtLFZnD123IMTvDr+iyf
-        XGy8hnMADUVVNmBTTVmHQ1VHNTPHq0zKrObVdCNJpGUqdSSjS4IIoygqpI4hqVXKkU8w/wAx7d4exNd3
-        O32kUi3EQiubCQTWzI6yFop2W4mVJMK0WoJN2yK4anY3/NHYFV2pvD44bXw9bDid3Qbj7Q3N17npQ7Db
-        fZGz+tMpujYOffwSQVBpcdurE0rVMaSRmoozLCWCSNf27W5uJYIlIEoZyp9HVCVP5ECvqKjz6d9vt3TZ
-        LLdrydWewMNvHOgx4lvLcJHPHkEVaNm0kg6X0uBVR1P392z/ALMH030/19t6Crw+a+UNU+1d94lZY/4n
-        sHYm1I5JfkljMo9PVN9pksNDj6nZhlild6XN5mmYagp9+mufrrWKGOoe5Olh5qq/2oNDxFDHjgzDqm2b
-        P/VXmC/3S6Kvb7MviQvnTPNKabeyVXKuWW8AYAPBC4wSOi2dW7/pML1R8fuu37O7Q2ZtTA9Mfx47B+N3
-        Wu7+xe0Mxka/d25cThajcw2Z1j2PVbI2DjsfgZkxp/3HDK1/nVpmjomjZBbzrHbQQeJKkSxV0xIzuakg
-        V0oxVQAacNRrntp0Ld32ya83rdd1FnZXF7LuBTx9wuYre3ULHG7iMzXNuJp2aRTIP1PCQqdNZAwXGB+T
-        nY+xOoI/kDuzcG8Ny9d9Tdsdn9Sdk7Q3ntnbe2OzN07GXsBdt7A31BiVottiPtfas0lFDUYpRRrmsbPU
-        f5IuUECM6u4Tw2n18rO0EUro6sqh2XXRWpjvGO3GpScaqDouuOUNp3PmA8qWMdvDu19Y21zbyxSSSW8c
-        30/iTwl6yf4tINZEp1+DIqVk8DW3R3OhctvzPbEg332Fn6LJZTsmpTfWHwGFmxVdtvYO0s5jqB9r7MwO
-        axdPGdzJRYeKKprspLLUCuylVUyU7R0ZpoIjmy8Z4PGnYFpDqAFKKpA0qCONBktmrEkUFAI65mj2223I
-        7btcTJDZr4Lu4YSTyozeJM6Mf09TEqkYC6IlRXDSa3YJMViuye98/wBo7in7o3/1ZtfbO+9ydcdabd6x
-        Oz6OanOxqmPCZ7e28qrc2191tuLNZXeFHWikoJFTFwYmGASU8s80sgSKlxePI5meONXKIE0404LNVWqS
-        1aDhppUEk9Hk11tHLdtZWi7dbXt7LbRz3D3HjEHxhrSGIRyRaEWIpqcHxTKX0uqqo6ZNv1Pd/ae8v9FW
-        9u0KrZC9Sde7Ey3Yu4umxjMZkO0d+7xym96KmEWQ3JtvMPtjaeNwGzqfIT02MWGeXJ5N6b7n7eiZKisf
-        1lzL9PLIUEUalilAXZiw8waKAoNBmppWgypuv6ubNtx3rbrMXBvruZII7rUy20ESwtWkcieJKzytGGkq
-        ojjD6NcgKBruze3fkG89mfHTF9yZbH5tPkjt7ak/bP8Ad3alRuzP9Jbp+NHdnZr43L0k+2RtCLsHDZrr
-        2qhp6+moREXpaCqngfyVEEqaWW9WZLFZSG+oC66LqMZhkehxp1AoaEDyUkcQTrbrHleTbbrmybb0e3Gz
-        vKLbxJREl5HuNnbalIk8UwPHcKWRnr3SojDSjq5dj7t7i6M3RuPAba7N3v2Nt/afXuD+Q82M3fj8BuDd
-        9RtTYO+6PB9x7EocvicDi6zL0u6dh5lq3EwtBJXUuboVWOoNPOII73El1ZyMscjyRqglowBbSrUkUUAr
-        qU1UUqGHGhp0l2iz2HmS0huLu0t7S6nunsdUbPHEJZ4We0mZXdlQxTpplbUEaFySgdS7Sfkv8nN37Xzj
-        TdV7hEO2Nr7H2E+4c/jdo5HsGlk3V8j+xtubC6uylNgcDRZHObqTYe0KTcG5psNjENdkppMTEP2Klg/t
-        x3CWJv8AFm/TVFqQpbulcKhAAJbSupyoyewcD17k7k+wv7cLvUf+OT3M/ho0yQHwrC3knuVZ5CqRePKY
-        LdZpD4cYFw2WQUR23e+pds7y2Fi9h9p/Jrtqn3hV5fbO+8f3T8f+yNoUWKZtpbkzeN7IwG68x0T1nt3b
-        lZjtx4qlparFmpGOnoKx2gplmgQs3He+HNGkElzKHJVvEidQO0kOGMSAGoAK1oQcCo6X3vLP1O3XVxul
-        psti0AWSE2d/bzM36saNbvEt9cySKY2Zlk0mQOgDOVY0i4/Yva+7Oofi3v8A7O+RXaGU35v7d3TWdzkW
-        2otgYPaOEqdx4SbK08G28HLsWvMdThYZxEayokmlrJvJNIoV0ii8sF1LaW01zPIZ3aMnTpCior2jT5ep
-        48fl1653TYrHf962zZdqs02u2hukj8QzvKwjfTWRxMvx0rpUAIKKMgs3eZ73g3FvLs7Hbu7v+TnXqbE3
-        Xlut9iYzpr4+dgb2x4Oy3p8Xk+wd3buwvx97C2lvbdG5Nx0tUzUEUy4ehoBHA9GKvzS+6veB5ZFkmuYw
-        jFFEcTsO3BYsInVmJriukCgpWp6vbctNbbfZTWO37LdG5hW4ma73C3hY+LVlgjie/t5YYkjK95XxXfU4
-        k8PSvT/1Nvjuv5Jbn2Umb7K3l1htLHdOU+c31hNq7QXr3cm9t84nt7s7ruiz1LT79wGR3hsLZ27cfsmT
-        KvjisGTEUtDF540WpWpctZbzcJE1SPHEIqsAukswd1B7gWRWC6qYb4c8apN+sOXeUbO5FvaW95fvflIX
-        km+ojhha1t5yhaB1hnmiaYReJUxVEp0MShSyT7r/AB/3nn/e/Z/1Ef2dZfuv8ntfjzf4/wCo/wBf37rf
-        SBkm/ckF/wDdjj/H9R9+x1rIx1xEv+P+8/8AEe/V8+vH59chN/sP8fr/AMV9+6912Jv8fp7917rmJv8A
-        Ef4m/v32dar12J+fqLf77/X9+zT59bz+fXPz/wC+/H/Fffj178+u/OePx/X375der139wbfn/ehb/b+/
-        de65Cc/1/wB9/sffq+vXuuX3H+P+9f8AFPfuvddio/x/3r37PXs+XXL7gj8/7C9r/wCPv1OveWeuX3B/
-        r/t+ffvn59a+3r33H+v/AMT791sdcvuP8f8Aff7b37r3XvuP8f8Aff7b37rfDrl9wb/W/wDsR/xHvfWu
-        i5ZbqPsXDbr3XuXp3t+l2HRb+zlJuPd21N4dfQdkbdjzseOoMRk81s9afdmxsvtnJ52gxlOalJqvIY81
-        MZnWkWSWYyFzWtwkrSWkwQOwLKy6xWlCV7lKkgCuSK5pUmout+YNouLGGz5gsGuZLWJo4pYZ/p5NJYsq
-        zVimSRUZm00SOTSdBk0qmlhj+NcuCott53ZPZmbxHcu38zvjP13bO5MFh90S72quy5sVU74xG9tqUbbZ
-        x9XtPKVG3cQaWhxtRi3xyYSgSCdVhfy0/dxQK8MhF2pY6yA2rXTUGXt7TRaBStNK0OMqTzmtzLNa7jZx
-        ycvSxwoLWN3iEItwwheGQ+IRKokl1PIsoczSllJYaUnJ8R8luXcO/N89kd2bp3Xvze+2+usRSZrDba2/
-        tfEde5bqbsBOzev8t13t6c7iTHUmD3hTx1r0uUqcu9XOZfNO8cviRv8AdLPI81zMzTSKgqAFCGNtalBm
-        lGzRi1TxNMdLxz/FZ2lrtuz7dBBtdrNcMUeSSV7hLuD6adLiQeHqLw1jDRLCFWmlQw1EUNsdTdhTb+2v
-        2B232xj9/wBdsGn3RT7IxG1euabrrDUcu66Smxldl9xefdW9srns5Bh4Hp4jBVY7Hj7iWRqNpBC0KiO1
-        n8dZrqUOyatIVNA7sVPcxJpjBAye3hQlvN/2kbXNtew2L2sdyYjM8twbhz4RLBY6RQokZchiGWSTtUCS
-        moMI26NiU25989X74mydRSVPWWQ3XkKSgjp0lhyrbp2tXbXmjqJmkV6ZaSGtMylQ2pl0mwN/aiSESTRz
-        E/2ZJ+2oI/y9FFnur2e3Xm3KgZbxI1LVyvhyLICB51K0Py6QmwOgttdfdw9odv4/MZeuquxI4Fxu2KwU
-        4wOwGyFRFlt/vtRIlWaE9l7opKbLZfWT5a+nWQWLPdiCwjhu5LtWJMnBfJa5bT/pzRm9SK9Gm6c13m67
-        BZbBLHGiWldUgr4k+kaYPFzQ/TRlooaAURiDilA42j8aN49VRUP+h3uSn2vVVW1ds7R3g27euKXfFHmq
-        PaOQ3RW4fMYKkg3ZtWbbW4Vh3ZPBK8k+SoZY4YWNJrV2kYj26W2p9JKFOlVbUmqoUsQR3LQ93qRwx0a3
-        vOW3b0XHMG3mZRPLLF4VwYWRpViDK5MUokjrEGAAjcFm/UoQBI60+J+P2JunF5ncPZu8uz8Lt3cu5uxN
-        vbe3pR4G0fbW+UP98OzM/W4ehx1Pn81JJU1v8IpxSU1Fg0yNQtPEWEDwet9rEDh3keRFYsA1Pjb4nNKV
-        PHSKALU0HCnt557l3Szkt7SztrK4mhjgkeEv/uLD/ZW6B2YomE8VizPMY0Lt8YcberetqPqak3Ht/b+X
-        rZtlZHclbuDae06mGIUfX9PmAlXmdtbeqkbzNtiTOvUVtHSSLpxwqnp4SKZIYolltbLbKyIT4RYlV/hr
-        xA+VakDyrQYoAG963qXfHhurqNRuKwhJZRWs5TCSSDh4mjSjsP7TSHarlmYPcz072Nid0btznTfc0XXG
-        J7Cy67h3ftncHXdD2JQ0e5JMZDi8juPr+abce2TtTK5yOjp562OtTM42WsiM60iSTVDSsPaXCys9pN4a
-        yGrArrANKErkaScE11CuaVJqa2/MO0TWMFtzBtxu57WPRFJHOYGKaiyxzjw5PFVKsEKGGQKdJkKqgVmj
-        +N9dsf8Au7luiuyKjrzeWI2XT7Ez+Y3hteDsvC9j4ilzWY3LSZTf+DjzWyspkt14/cu5ctXU1fRZTHlJ
-        svVrJHLDIsaU/dzQ6XspNEwTSSy6wwqSCwqpJBLEEMPiNajpSecYdxMtvzNaC7217kzokUpt3t2KJGyw
-        OUmRY2jjiRkeKSqwx0KsCS67a+O1Hhs1tLeGZ3vnN1b8xPcuW7r3ZurJUNDTPu/PV/Te/OksbgKbF0bJ
-        Q7W2ptfaW84kx9LTeRkGPVpnmqKipqZLR7cFkSV3ZphMZCTTJMbRgU4Kqq2APTJJJJZu+cJZ7a42+3to
-        oNsk25LOONSx8JFu4LxpCx7pZZZYSZGag/UIQIiIiijU7AoartWm7RqK+SSeDrfLdbvgpKWGShqaHLbj
-        xO4pq6WZ2LNIr4sQ+IoUZJCSeLe1JgBufqCciMrT5Eg1/l0RpusqbKdmRQEN2s+up1ArG0YFPTurX1HQ
-        EbQ+IGx9p9Q726li3Nueqj3VvSg3pht4LJSx7p2NWbKqdtf6F6fbVTWRZKlEXTeM2Pg6fFrURzU8xx5e
-        eFlqJo2RRbTDFaPaBmozhg2KqVp4dK1/swqha1GMjJ6FF/7g7lfb/bb8YYQ0Fu0Lxd3hziYSfWGQKVb/
-        ABtpp2lKlWXxKIw0KQoKfpTsHc24MDmu5u4qXf8AR7Jq8tk9jbf2v15D11iaXcGT25lNqQ7p3gv97d2V
-        W7c7icNna5KVIZMZjI5aozGiM0VPJC4tnPJIr3cocISVCroFSCtW7m1EAmnwrmumoBCR+ZdqtLSW15e2
-        9rWS5VFneWf6hyiyLKY4f0ohEjuiFiRLKQunxdLOrKrL9PzVPUvX3XGE3lV4DNdY0+wH2pvNcLQZMxZf
-        YFFR0FHW5Lb1dL9nXUWUpaeSOophPG6pOfHMjqrh1rQm2S3Ryrx6aNQHK+o86+Y+eD0gg5gRd8ut2ubd
-        Zbe8M/iRa2XtnJJCuBUFSQVahFVGpSCR0k6rprtLBZjc1f1X3hS7Fxu/cwu5d4YfM9Y47e8FHuqqw9Jj
-        M/uLruWfdGCTaVVuKpoY6+enyMWfoFrjJItOPNKrNG0uEZjaTBFc1YFA1DShKdw014kHWK+WT0vj5i2W
-        5t4Yt721rma1j8OJ0uGhJjDlkS4Ajk8URglFMZgk0UBftWiy6z6XwHVeTp63A5bLVlLT9d7d2F48w6Vm
-        Rr5sNufeW7MnurL5UGJq/ObmzO9KierIhij82p1AD6Uet7RLZqxkn9MLnjgsxYnzJLEnot3jmO53uEpc
-        oisbuSbsBCqHjiiWNVzpSNIVVMk0oDwqRrNR/iQP9h7Vj+fQe4DrN9yft/1H/Pf4f6j/AF7e/UHW6HpL
-        S/52X/lo/wD0MffutdY/fuvdd3P9T7917r1z/X37r3XtR/r7917rvUf98f8AfH37r3Xes/4e/de678h/
-        3x9+69/g695D/T/effuvddiU3v8A8T7917rvzH/H/ePfuvddiY8/77/iPfuvfZ135f8AH/ff7H37/B17
-        y6783+P+8H37PXuveYf7D+nvw69TrsTf7f8A17e/HjXr3Xfn/wAf95P/ABv37r1Ou/N/j/tm9+698+u/
-        P/iR79/h68OPXvPf+n145H+9+/dez1357fU8/wCPNv8Aifeznr2evCb/AB/33+x5HvXnjr3HrsTm/wDr
-        +/fbx6317z/1IHv3WscB17z/AJ4/4j/e/fuHXqV68Kj/AB/33+w9+69w695/6nn+n++t78TXh1vr3n5+
-        v+9e/de8q9die4+v/E/8a9+619nXvOP+N/Qf7179nrWTw66+4vbm/wDsf+N+/U63jrwnPIuf9geffuvc
-        OPXXm5+v+3/4p79g8Ovf4eu/N9bfn/Wt/sPfuvfb1157/n/Dg/7x7917j115jz/sPz/viPfuvE9dGY/1
-        Hvwx17B49SPKftr3/wB32+o/453+vv3n8+vefXj+t/8Ai2/Vv1fT6+/de69/56/fuvde/wDPX7917r3/
-        AJ6/fuvde/8APX7917r3/nr9+6917/z1+/de69/56/fuvde/89fv3Xuvf+ev37r3Xv8Az1+/de69/wCe
-        v37r3Xv/AD1+/de69/56/fuvde/89fv3Xuvf+ev37r3Xv/PX7917r3/nr9+6917/AM9fv3Xuvf8Anr9+
-        6917/wA9fv3Xuvf+ev37z695de/89fvx49e8uuQ+v/Lt+n4+v/Ivfj1vrj/56/e/Pz6112Pof+LZ/sPp
-        /sfdTx68OPXL8t/xbPofr9fofexw635dcR/5DP8AYe/HrXl1yX6N/wAWz6fj/Y+/enHrXn1xP/kM/wBj
-        72Ot9e/A/wCLZ9f9h/sP8ffj1ocevf1/4tn++/r7r1vz66/89fu3Xuvf+ev34de67/8APZ9f99/sfevP
-        rXXj9f8Al2f7H6+/deHXR+h/4tf+w+v+w9+6317/AHV/y7/85/0T7917r//Z
+        iVBORw0KGgoAAAANSUhEUgAAASwAAACCCAIAAABzfmIIAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAsiElEQVR4Xu2dB3gU1drHF8tV
+        EHu59373er0WQBAuICT0EghppHeqEHpRQcBCLxIUqdJVVIr0QEhoorQAQqQlpEN6D+l1W3bz/TdnMpmc
+        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
+        QmCC
 </value>
   </data>
 </root>

+ 176 - 4
Exporters/3ds Max/Max2Babylon/Forms/LightPropertiesForm.Designer.cs

@@ -40,11 +40,26 @@
             this.chkAutoAnimate = new System.Windows.Forms.CheckBox();
             this.groupBox4 = new System.Windows.Forms.GroupBox();
             this.chkNoExport = new System.Windows.Forms.CheckBox();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.grpBlurInfo = new System.Windows.Forms.GroupBox();
+            this.nupBlurBoxOffset = new System.Windows.Forms.NumericUpDown();
+            this.label3 = new System.Windows.Forms.Label();
+            this.label6 = new System.Windows.Forms.Label();
+            this.nupBlurScale = new System.Windows.Forms.NumericUpDown();
+            this.cbCameraType = new System.Windows.Forms.ComboBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.nupBias = new System.Windows.Forms.NumericUpDown();
+            this.label1 = new System.Windows.Forms.Label();
             this.groupBox3.SuspendLayout();
             this.grpAutoAnimate.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.nupTo)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.nupFrom)).BeginInit();
             this.groupBox4.SuspendLayout();
+            this.groupBox1.SuspendLayout();
+            this.grpBlurInfo.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupBlurBoxOffset)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupBlurScale)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupBias)).BeginInit();
             this.SuspendLayout();
             // 
             // butOK
@@ -52,7 +67,7 @@
             this.butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
             this.butOK.DialogResult = System.Windows.Forms.DialogResult.OK;
             this.butOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butOK.Location = new System.Drawing.Point(93, 252);
+            this.butOK.Location = new System.Drawing.Point(93, 454);
             this.butOK.Name = "butOK";
             this.butOK.Size = new System.Drawing.Size(75, 23);
             this.butOK.TabIndex = 1;
@@ -65,7 +80,7 @@
             this.butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
             this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
             this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butCancel.Location = new System.Drawing.Point(174, 252);
+            this.butCancel.Location = new System.Drawing.Point(174, 454);
             this.butCancel.Name = "butCancel";
             this.butCancel.Size = new System.Drawing.Size(75, 23);
             this.butCancel.TabIndex = 2;
@@ -77,7 +92,7 @@
             this.groupBox3.Controls.Add(this.grpAutoAnimate);
             this.groupBox3.Controls.Add(this.chkAutoAnimate);
             this.groupBox3.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.groupBox3.Location = new System.Drawing.Point(12, 77);
+            this.groupBox3.Location = new System.Drawing.Point(12, 284);
             this.groupBox3.Name = "groupBox3";
             this.groupBox3.Size = new System.Drawing.Size(319, 156);
             this.groupBox3.TabIndex = 5;
@@ -188,13 +203,153 @@
             this.chkNoExport.ThreeState = true;
             this.chkNoExport.UseVisualStyleBackColor = true;
             // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.grpBlurInfo);
+            this.groupBox1.Controls.Add(this.nupBias);
+            this.groupBox1.Controls.Add(this.label6);
+            this.groupBox1.Controls.Add(this.label1);
+            this.groupBox1.Controls.Add(this.cbCameraType);
+            this.groupBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox1.Location = new System.Drawing.Point(12, 77);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(319, 201);
+            this.groupBox1.TabIndex = 6;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Shadows";
+            // 
+            // grpBlurInfo
+            // 
+            this.grpBlurInfo.Controls.Add(this.nupBlurBoxOffset);
+            this.grpBlurInfo.Controls.Add(this.label3);
+            this.grpBlurInfo.Controls.Add(this.nupBlurScale);
+            this.grpBlurInfo.Controls.Add(this.label2);
+            this.grpBlurInfo.Enabled = false;
+            this.grpBlurInfo.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.grpBlurInfo.Location = new System.Drawing.Point(21, 108);
+            this.grpBlurInfo.Name = "grpBlurInfo";
+            this.grpBlurInfo.Size = new System.Drawing.Size(292, 87);
+            this.grpBlurInfo.TabIndex = 13;
+            this.grpBlurInfo.TabStop = false;
+            this.grpBlurInfo.Text = "Blur info";
+            // 
+            // nupBlurBoxOffset
+            // 
+            this.nupBlurBoxOffset.Location = new System.Drawing.Point(89, 50);
+            this.nupBlurBoxOffset.Maximum = new decimal(new int[] {
+            3,
+            0,
+            0,
+            0});
+            this.nupBlurBoxOffset.Minimum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            this.nupBlurBoxOffset.Name = "nupBlurBoxOffset";
+            this.nupBlurBoxOffset.Size = new System.Drawing.Size(120, 20);
+            this.nupBlurBoxOffset.TabIndex = 13;
+            this.nupBlurBoxOffset.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(6, 52);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(77, 13);
+            this.label3.TabIndex = 14;
+            this.label3.Text = "Blur box offset:";
+            // 
+            // label6
+            // 
+            this.label6.AutoSize = true;
+            this.label6.Location = new System.Drawing.Point(21, 55);
+            this.label6.Name = "label6";
+            this.label6.Size = new System.Drawing.Size(34, 13);
+            this.label6.TabIndex = 8;
+            this.label6.Text = "Type:";
+            // 
+            // nupBlurScale
+            // 
+            this.nupBlurScale.Location = new System.Drawing.Point(89, 24);
+            this.nupBlurScale.Maximum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            0});
+            this.nupBlurScale.Minimum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            this.nupBlurScale.Name = "nupBlurScale";
+            this.nupBlurScale.Size = new System.Drawing.Size(120, 20);
+            this.nupBlurScale.TabIndex = 11;
+            this.nupBlurScale.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            // 
+            // cbCameraType
+            // 
+            this.cbCameraType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cbCameraType.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.cbCameraType.FormattingEnabled = true;
+            this.cbCameraType.Items.AddRange(new object[] {
+            "Hard shadows",
+            "Poisson Sampling",
+            "Variance",
+            "Blurred Variance"});
+            this.cbCameraType.Location = new System.Drawing.Point(24, 71);
+            this.cbCameraType.Name = "cbCameraType";
+            this.cbCameraType.Size = new System.Drawing.Size(289, 21);
+            this.cbCameraType.TabIndex = 7;
+            this.cbCameraType.SelectedIndexChanged += new System.EventHandler(this.cbCameraType_SelectedIndexChanged);
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(6, 26);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(56, 13);
+            this.label2.TabIndex = 12;
+            this.label2.Text = "Blur scale:";
+            // 
+            // nupBias
+            // 
+            this.nupBias.DecimalPlaces = 5;
+            this.nupBias.Location = new System.Drawing.Point(57, 26);
+            this.nupBias.Maximum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            this.nupBias.Name = "nupBias";
+            this.nupBias.Size = new System.Drawing.Size(120, 20);
+            this.nupBias.TabIndex = 9;
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(21, 28);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(30, 13);
+            this.label1.TabIndex = 10;
+            this.label1.Text = "Bias:";
+            // 
             // LightPropertiesForm
             // 
             this.AcceptButton = this.butOK;
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.CancelButton = this.butCancel;
-            this.ClientSize = new System.Drawing.Size(343, 287);
+            this.ClientSize = new System.Drawing.Size(343, 489);
+            this.Controls.Add(this.groupBox1);
             this.Controls.Add(this.groupBox4);
             this.Controls.Add(this.groupBox3);
             this.Controls.Add(this.butCancel);
@@ -212,6 +367,13 @@
             ((System.ComponentModel.ISupportInitialize)(this.nupFrom)).EndInit();
             this.groupBox4.ResumeLayout(false);
             this.groupBox4.PerformLayout();
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.grpBlurInfo.ResumeLayout(false);
+            this.grpBlurInfo.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupBlurBoxOffset)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupBlurScale)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupBias)).EndInit();
             this.ResumeLayout(false);
 
         }
@@ -230,5 +392,15 @@
         private System.Windows.Forms.CheckBox chkAutoAnimate;
         private System.Windows.Forms.GroupBox groupBox4;
         private System.Windows.Forms.CheckBox chkNoExport;
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.Label label6;
+        private System.Windows.Forms.ComboBox cbCameraType;
+        private System.Windows.Forms.NumericUpDown nupBlurScale;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.NumericUpDown nupBias;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.GroupBox grpBlurInfo;
+        private System.Windows.Forms.NumericUpDown nupBlurBoxOffset;
+        private System.Windows.Forms.Label label3;
     }
 }

+ 15 - 0
Exporters/3ds Max/Max2Babylon/Forms/LightPropertiesForm.cs

@@ -31,6 +31,11 @@ namespace Max2Babylon
             Tools.PrepareCheckBox(chkLoop, lights, "babylonjs_autoanimateloop");
             Tools.PrepareNumericUpDown(nupFrom, lights, "babylonjs_autoanimate_from");
             Tools.PrepareNumericUpDown(nupTo, lights, "babylonjs_autoanimate_to", 100.0f);
+
+            Tools.PrepareNumericUpDown(nupBias, lights, "babylonjs_shadows_bias", 0.00005f);
+            Tools.PrepareNumericUpDown(nupBlurScale, lights, "babylonjs_shadows_blurScale", 2);
+            Tools.PrepareNumericUpDown(nupBlurBoxOffset, lights, "babylonjs_shadows_blurBoxOffset", 1);
+            Tools.PrepareComboBox(cbCameraType, lights[0], "babylonjs_shadows_type", "Blurred Variance");
         }
 
         private void butOK_Click(object sender, EventArgs e)
@@ -40,11 +45,21 @@ namespace Max2Babylon
             Tools.UpdateCheckBox(chkLoop, lights, "babylonjs_autoanimateloop");
             Tools.UpdateNumericUpDown(nupFrom, lights, "babylonjs_autoanimate_from");
             Tools.UpdateNumericUpDown(nupTo, lights, "babylonjs_autoanimate_to");
+
+            Tools.UpdateNumericUpDown(nupBias, lights, "babylonjs_shadows_bias");
+            Tools.UpdateNumericUpDown(nupBlurScale, lights, "babylonjs_shadows_blurScale");
+            Tools.UpdateNumericUpDown(nupBlurBoxOffset, lights, "babylonjs_shadows_blurBoxOffset");
+            Tools.UpdateComboBox(cbCameraType, lights, "babylonjs_shadows_type");
         }
 
         private void chkAutoAnimate_CheckedChanged(object sender, EventArgs e)
         {
             grpAutoAnimate.Enabled = chkAutoAnimate.Checked;
         }
+
+        private void cbCameraType_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            grpBlurInfo.Enabled = (cbCameraType.SelectedIndex == 3);
+        }
     }
 }

+ 503 - 49
Exporters/3ds Max/Max2Babylon/Forms/ObjectPropertiesForm.Designer.cs

@@ -49,14 +49,41 @@
             this.label1 = new System.Windows.Forms.Label();
             this.chkAutoAnimate = new System.Windows.Forms.CheckBox();
             this.groupBox4 = new System.Windows.Forms.GroupBox();
+            this.nupRestitution = new System.Windows.Forms.NumericUpDown();
+            this.label7 = new System.Windows.Forms.Label();
+            this.nupFriction = new System.Windows.Forms.NumericUpDown();
+            this.label5 = new System.Windows.Forms.Label();
             this.label6 = new System.Windows.Forms.Label();
             this.cbImpostor = new System.Windows.Forms.ComboBox();
             this.nupMass = new System.Windows.Forms.NumericUpDown();
             this.label4 = new System.Windows.Forms.Label();
-            this.nupFriction = new System.Windows.Forms.NumericUpDown();
-            this.label5 = new System.Windows.Forms.Label();
-            this.nupRestitution = new System.Windows.Forms.NumericUpDown();
-            this.label7 = new System.Windows.Forms.Label();
+            this.groupBox5 = new System.Windows.Forms.GroupBox();
+            this.cmdFileBrowse = new System.Windows.Forms.Button();
+            this.grpDirectional = new System.Windows.Forms.GroupBox();
+            this.nupConeOuterGain = new System.Windows.Forms.NumericUpDown();
+            this.lblConeOuterGain = new System.Windows.Forms.Label();
+            this.nupConeOuterAngle = new System.Windows.Forms.NumericUpDown();
+            this.lblConeOuterAngle = new System.Windows.Forms.Label();
+            this.nupConeInnerAngle = new System.Windows.Forms.NumericUpDown();
+            this.lblConeInnerAngle = new System.Windows.Forms.Label();
+            this.chkDirectional = new System.Windows.Forms.CheckBox();
+            this.nupPlaybackRate = new System.Windows.Forms.NumericUpDown();
+            this.lblPlaybackRate = new System.Windows.Forms.Label();
+            this.nupVolume = new System.Windows.Forms.NumericUpDown();
+            this.lblVolume = new System.Windows.Forms.Label();
+            this.grpSpatialSound = new System.Windows.Forms.GroupBox();
+            this.cbDistanceModel = new System.Windows.Forms.ComboBox();
+            this.lblDistanceModel = new System.Windows.Forms.Label();
+            this.nupRefDistance = new System.Windows.Forms.NumericUpDown();
+            this.lblRefDistance = new System.Windows.Forms.Label();
+            this.nupRolloff = new System.Windows.Forms.NumericUpDown();
+            this.lblRolloff = new System.Windows.Forms.Label();
+            this.nupMaxDistance = new System.Windows.Forms.NumericUpDown();
+            this.lblMaxDistance = new System.Windows.Forms.Label();
+            this.txtSound = new System.Windows.Forms.TextBox();
+            this.checkBox1 = new System.Windows.Forms.CheckBox();
+            this.chkAutoPlay = new System.Windows.Forms.CheckBox();
+            this.ofdOpenSound = new System.Windows.Forms.OpenFileDialog();
             this.groupBox1.SuspendLayout();
             this.groupBox2.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.nupAlphaIndex)).BeginInit();
@@ -65,9 +92,20 @@
             ((System.ComponentModel.ISupportInitialize)(this.nupTo)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.nupFrom)).BeginInit();
             this.groupBox4.SuspendLayout();
-            ((System.ComponentModel.ISupportInitialize)(this.nupMass)).BeginInit();
-            ((System.ComponentModel.ISupportInitialize)(this.nupFriction)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.nupRestitution)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupFriction)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupMass)).BeginInit();
+            this.groupBox5.SuspendLayout();
+            this.grpDirectional.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupConeOuterGain)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupConeOuterAngle)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupConeInnerAngle)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupPlaybackRate)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupVolume)).BeginInit();
+            this.grpSpatialSound.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupRefDistance)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupRolloff)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupMaxDistance)).BeginInit();
             this.SuspendLayout();
             // 
             // groupBox1
@@ -98,7 +136,7 @@
             this.butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
             this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
             this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butCancel.Location = new System.Drawing.Point(174, 599);
+            this.butCancel.Location = new System.Drawing.Point(508, 457);
             this.butCancel.Name = "butCancel";
             this.butCancel.Size = new System.Drawing.Size(75, 23);
             this.butCancel.TabIndex = 6;
@@ -110,7 +148,7 @@
             this.butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
             this.butOK.DialogResult = System.Windows.Forms.DialogResult.OK;
             this.butOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butOK.Location = new System.Drawing.Point(93, 599);
+            this.butOK.Location = new System.Drawing.Point(415, 457);
             this.butOK.Name = "butOK";
             this.butOK.Size = new System.Drawing.Size(75, 23);
             this.butOK.TabIndex = 5;
@@ -130,14 +168,14 @@
             this.groupBox2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
             this.groupBox2.Location = new System.Drawing.Point(12, 77);
             this.groupBox2.Name = "groupBox2";
-            this.groupBox2.Size = new System.Drawing.Size(319, 179);
+            this.groupBox2.Size = new System.Drawing.Size(319, 126);
             this.groupBox2.TabIndex = 2;
             this.groupBox2.TabStop = false;
             this.groupBox2.Text = "Misc.";
             // 
             // nupAlphaIndex
             // 
-            this.nupAlphaIndex.Location = new System.Drawing.Point(89, 143);
+            this.nupAlphaIndex.Location = new System.Drawing.Point(89, 95);
             this.nupAlphaIndex.Maximum = new decimal(new int[] {
             1000,
             0,
@@ -150,7 +188,7 @@
             // label3
             // 
             this.label3.AutoSize = true;
-            this.label3.Location = new System.Drawing.Point(18, 145);
+            this.label3.Location = new System.Drawing.Point(18, 97);
             this.label3.Name = "label3";
             this.label3.Size = new System.Drawing.Size(65, 13);
             this.label3.TabIndex = 6;
@@ -172,7 +210,7 @@
             // 
             this.chkShowSubMeshesBoundingBox.AutoSize = true;
             this.chkShowSubMeshesBoundingBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.chkShowSubMeshesBoundingBox.Location = new System.Drawing.Point(21, 120);
+            this.chkShowSubMeshesBoundingBox.Location = new System.Drawing.Point(21, 74);
             this.chkShowSubMeshesBoundingBox.Name = "chkShowSubMeshesBoundingBox";
             this.chkShowSubMeshesBoundingBox.Size = new System.Drawing.Size(184, 17);
             this.chkShowSubMeshesBoundingBox.TabIndex = 3;
@@ -184,7 +222,7 @@
             // 
             this.chkShowBoundingBox.AutoSize = true;
             this.chkShowBoundingBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.chkShowBoundingBox.Location = new System.Drawing.Point(21, 97);
+            this.chkShowBoundingBox.Location = new System.Drawing.Point(21, 51);
             this.chkShowBoundingBox.Name = "chkShowBoundingBox";
             this.chkShowBoundingBox.Size = new System.Drawing.Size(117, 17);
             this.chkShowBoundingBox.TabIndex = 2;
@@ -196,7 +234,7 @@
             // 
             this.chkOptimize.AutoSize = true;
             this.chkOptimize.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.chkOptimize.Location = new System.Drawing.Point(21, 74);
+            this.chkOptimize.Location = new System.Drawing.Point(182, 51);
             this.chkOptimize.Name = "chkOptimize";
             this.chkOptimize.Size = new System.Drawing.Size(131, 17);
             this.chkOptimize.TabIndex = 1;
@@ -208,7 +246,7 @@
             // 
             this.chkPickable.AutoSize = true;
             this.chkPickable.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.chkPickable.Location = new System.Drawing.Point(21, 51);
+            this.chkPickable.Location = new System.Drawing.Point(182, 28);
             this.chkPickable.Name = "chkPickable";
             this.chkPickable.Size = new System.Drawing.Size(64, 17);
             this.chkPickable.TabIndex = 0;
@@ -221,7 +259,7 @@
             this.groupBox3.Controls.Add(this.grpAutoAnimate);
             this.groupBox3.Controls.Add(this.chkAutoAnimate);
             this.groupBox3.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.groupBox3.Location = new System.Drawing.Point(12, 262);
+            this.groupBox3.Location = new System.Drawing.Point(12, 209);
             this.groupBox3.Name = "groupBox3";
             this.groupBox3.Size = new System.Drawing.Size(319, 156);
             this.groupBox3.TabIndex = 4;
@@ -320,13 +358,57 @@
             this.groupBox4.Controls.Add(this.nupMass);
             this.groupBox4.Controls.Add(this.label4);
             this.groupBox4.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.groupBox4.Location = new System.Drawing.Point(12, 424);
+            this.groupBox4.Location = new System.Drawing.Point(12, 371);
             this.groupBox4.Name = "groupBox4";
-            this.groupBox4.Size = new System.Drawing.Size(319, 155);
+            this.groupBox4.Size = new System.Drawing.Size(319, 156);
             this.groupBox4.TabIndex = 7;
             this.groupBox4.TabStop = false;
             this.groupBox4.Text = "Physics";
             // 
+            // nupRestitution
+            // 
+            this.nupRestitution.DecimalPlaces = 2;
+            this.nupRestitution.Location = new System.Drawing.Point(89, 122);
+            this.nupRestitution.Maximum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            this.nupRestitution.Name = "nupRestitution";
+            this.nupRestitution.Size = new System.Drawing.Size(120, 20);
+            this.nupRestitution.TabIndex = 11;
+            // 
+            // label7
+            // 
+            this.label7.AutoSize = true;
+            this.label7.Location = new System.Drawing.Point(18, 122);
+            this.label7.Name = "label7";
+            this.label7.Size = new System.Drawing.Size(60, 13);
+            this.label7.TabIndex = 12;
+            this.label7.Text = "Restitution:";
+            // 
+            // nupFriction
+            // 
+            this.nupFriction.DecimalPlaces = 2;
+            this.nupFriction.Location = new System.Drawing.Point(89, 96);
+            this.nupFriction.Maximum = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            this.nupFriction.Name = "nupFriction";
+            this.nupFriction.Size = new System.Drawing.Size(120, 20);
+            this.nupFriction.TabIndex = 9;
+            // 
+            // label5
+            // 
+            this.label5.AutoSize = true;
+            this.label5.Location = new System.Drawing.Point(18, 96);
+            this.label5.Name = "label5";
+            this.label5.Size = new System.Drawing.Size(44, 13);
+            this.label5.TabIndex = 10;
+            this.label5.Text = "Friction:";
+            // 
             // label6
             // 
             this.label6.AutoSize = true;
@@ -373,49 +455,379 @@
             this.label4.TabIndex = 6;
             this.label4.Text = "Mass:";
             // 
-            // nupFriction
+            // groupBox5
+            // 
+            this.groupBox5.Controls.Add(this.cmdFileBrowse);
+            this.groupBox5.Controls.Add(this.grpDirectional);
+            this.groupBox5.Controls.Add(this.chkDirectional);
+            this.groupBox5.Controls.Add(this.nupPlaybackRate);
+            this.groupBox5.Controls.Add(this.lblPlaybackRate);
+            this.groupBox5.Controls.Add(this.nupVolume);
+            this.groupBox5.Controls.Add(this.lblVolume);
+            this.groupBox5.Controls.Add(this.grpSpatialSound);
+            this.groupBox5.Controls.Add(this.txtSound);
+            this.groupBox5.Controls.Add(this.checkBox1);
+            this.groupBox5.Controls.Add(this.chkAutoPlay);
+            this.groupBox5.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox5.Location = new System.Drawing.Point(337, 12);
+            this.groupBox5.Name = "groupBox5";
+            this.groupBox5.Size = new System.Drawing.Size(319, 399);
+            this.groupBox5.TabIndex = 8;
+            this.groupBox5.TabStop = false;
+            this.groupBox5.Text = "Sound";
+            // 
+            // cmdFileBrowse
+            // 
+            this.cmdFileBrowse.Anchor = System.Windows.Forms.AnchorStyles.Right;
+            this.cmdFileBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.cmdFileBrowse.Location = new System.Drawing.Point(277, 26);
+            this.cmdFileBrowse.Name = "cmdFileBrowse";
+            this.cmdFileBrowse.Size = new System.Drawing.Size(37, 22);
+            this.cmdFileBrowse.TabIndex = 14;
+            this.cmdFileBrowse.Text = "...";
+            this.cmdFileBrowse.UseVisualStyleBackColor = true;
+            this.cmdFileBrowse.Click += new System.EventHandler(this.cmdFileBrowse_Click);
+            // 
+            // grpDirectional
+            // 
+            this.grpDirectional.Controls.Add(this.nupConeOuterGain);
+            this.grpDirectional.Controls.Add(this.lblConeOuterGain);
+            this.grpDirectional.Controls.Add(this.nupConeOuterAngle);
+            this.grpDirectional.Controls.Add(this.lblConeOuterAngle);
+            this.grpDirectional.Controls.Add(this.nupConeInnerAngle);
+            this.grpDirectional.Controls.Add(this.lblConeInnerAngle);
+            this.grpDirectional.Enabled = false;
+            this.grpDirectional.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.grpDirectional.Location = new System.Drawing.Point(22, 288);
+            this.grpDirectional.Name = "grpDirectional";
+            this.grpDirectional.Size = new System.Drawing.Size(292, 102);
+            this.grpDirectional.TabIndex = 13;
+            this.grpDirectional.TabStop = false;
+            // 
+            // nupConeOuterGain
+            // 
+            this.nupConeOuterGain.DecimalPlaces = 2;
+            this.nupConeOuterGain.Increment = new decimal(new int[] {
+            1,
+            0,
+            0,
+            65536});
+            this.nupConeOuterGain.Location = new System.Drawing.Point(105, 69);
+            this.nupConeOuterGain.Maximum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            0});
+            this.nupConeOuterGain.Name = "nupConeOuterGain";
+            this.nupConeOuterGain.Size = new System.Drawing.Size(121, 20);
+            this.nupConeOuterGain.TabIndex = 5;
             // 
-            this.nupFriction.DecimalPlaces = 2;
-            this.nupFriction.Location = new System.Drawing.Point(89, 96);
-            this.nupFriction.Maximum = new decimal(new int[] {
+            // lblConeOuterGain
+            // 
+            this.lblConeOuterGain.AutoSize = true;
+            this.lblConeOuterGain.Location = new System.Drawing.Point(8, 71);
+            this.lblConeOuterGain.Name = "lblConeOuterGain";
+            this.lblConeOuterGain.Size = new System.Drawing.Size(85, 13);
+            this.lblConeOuterGain.TabIndex = 6;
+            this.lblConeOuterGain.Text = "Cone outer gain:";
+            // 
+            // nupConeOuterAngle
+            // 
+            this.nupConeOuterAngle.DecimalPlaces = 2;
+            this.nupConeOuterAngle.Location = new System.Drawing.Point(105, 43);
+            this.nupConeOuterAngle.Maximum = new decimal(new int[] {
+            360,
+            0,
+            0,
+            0});
+            this.nupConeOuterAngle.Name = "nupConeOuterAngle";
+            this.nupConeOuterAngle.Size = new System.Drawing.Size(121, 20);
+            this.nupConeOuterAngle.TabIndex = 3;
+            this.nupConeOuterAngle.Value = new decimal(new int[] {
+            360,
+            0,
+            0,
+            0});
+            // 
+            // lblConeOuterAngle
+            // 
+            this.lblConeOuterAngle.AutoSize = true;
+            this.lblConeOuterAngle.Location = new System.Drawing.Point(8, 45);
+            this.lblConeOuterAngle.Name = "lblConeOuterAngle";
+            this.lblConeOuterAngle.Size = new System.Drawing.Size(91, 13);
+            this.lblConeOuterAngle.TabIndex = 4;
+            this.lblConeOuterAngle.Text = "Cone outer angle:";
+            // 
+            // nupConeInnerAngle
+            // 
+            this.nupConeInnerAngle.DecimalPlaces = 2;
+            this.nupConeInnerAngle.Location = new System.Drawing.Point(105, 17);
+            this.nupConeInnerAngle.Maximum = new decimal(new int[] {
+            360,
+            0,
+            0,
+            0});
+            this.nupConeInnerAngle.Name = "nupConeInnerAngle";
+            this.nupConeInnerAngle.Size = new System.Drawing.Size(121, 20);
+            this.nupConeInnerAngle.TabIndex = 1;
+            this.nupConeInnerAngle.Value = new decimal(new int[] {
+            360,
+            0,
+            0,
+            0});
+            // 
+            // lblConeInnerAngle
+            // 
+            this.lblConeInnerAngle.AutoSize = true;
+            this.lblConeInnerAngle.Location = new System.Drawing.Point(7, 19);
+            this.lblConeInnerAngle.Name = "lblConeInnerAngle";
+            this.lblConeInnerAngle.Size = new System.Drawing.Size(90, 13);
+            this.lblConeInnerAngle.TabIndex = 2;
+            this.lblConeInnerAngle.Text = "Cone inner angle:";
+            // 
+            // chkDirectional
+            // 
+            this.chkDirectional.AutoSize = true;
+            this.chkDirectional.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkDirectional.Location = new System.Drawing.Point(22, 265);
+            this.chkDirectional.Name = "chkDirectional";
+            this.chkDirectional.Size = new System.Drawing.Size(73, 17);
+            this.chkDirectional.TabIndex = 12;
+            this.chkDirectional.Text = "Directional";
+            this.chkDirectional.UseVisualStyleBackColor = true;
+            this.chkDirectional.CheckedChanged += new System.EventHandler(this.chkDirectional_CheckedChanged);
+            // 
+            // nupPlaybackRate
+            // 
+            this.nupPlaybackRate.DecimalPlaces = 2;
+            this.nupPlaybackRate.Increment = new decimal(new int[] {
+            1,
+            0,
+            0,
+            65536});
+            this.nupPlaybackRate.Location = new System.Drawing.Point(126, 107);
+            this.nupPlaybackRate.Maximum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            0});
+            this.nupPlaybackRate.Name = "nupPlaybackRate";
+            this.nupPlaybackRate.Size = new System.Drawing.Size(120, 20);
+            this.nupPlaybackRate.TabIndex = 10;
+            this.nupPlaybackRate.Value = new decimal(new int[] {
             1,
             0,
             0,
             0});
-            this.nupFriction.Name = "nupFriction";
-            this.nupFriction.Size = new System.Drawing.Size(120, 20);
-            this.nupFriction.TabIndex = 9;
             // 
-            // label5
+            // lblPlaybackRate
             // 
-            this.label5.AutoSize = true;
-            this.label5.Location = new System.Drawing.Point(18, 96);
-            this.label5.Name = "label5";
-            this.label5.Size = new System.Drawing.Size(44, 13);
-            this.label5.TabIndex = 10;
-            this.label5.Text = "Friction:";
+            this.lblPlaybackRate.AutoSize = true;
+            this.lblPlaybackRate.Location = new System.Drawing.Point(18, 109);
+            this.lblPlaybackRate.Name = "lblPlaybackRate";
+            this.lblPlaybackRate.Size = new System.Drawing.Size(75, 13);
+            this.lblPlaybackRate.TabIndex = 9;
+            this.lblPlaybackRate.Text = "Playback rate:";
             // 
-            // nupRestitution
+            // nupVolume
             // 
-            this.nupRestitution.DecimalPlaces = 2;
-            this.nupRestitution.Location = new System.Drawing.Point(89, 122);
-            this.nupRestitution.Maximum = new decimal(new int[] {
+            this.nupVolume.DecimalPlaces = 2;
+            this.nupVolume.Increment = new decimal(new int[] {
+            1,
+            0,
+            0,
+            65536});
+            this.nupVolume.Location = new System.Drawing.Point(126, 81);
+            this.nupVolume.Maximum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            0});
+            this.nupVolume.Name = "nupVolume";
+            this.nupVolume.Size = new System.Drawing.Size(120, 20);
+            this.nupVolume.TabIndex = 8;
+            this.nupVolume.Value = new decimal(new int[] {
             1,
             0,
             0,
             0});
-            this.nupRestitution.Name = "nupRestitution";
-            this.nupRestitution.Size = new System.Drawing.Size(120, 20);
-            this.nupRestitution.TabIndex = 11;
             // 
-            // label7
+            // lblVolume
+            // 
+            this.lblVolume.AutoSize = true;
+            this.lblVolume.Location = new System.Drawing.Point(18, 83);
+            this.lblVolume.Name = "lblVolume";
+            this.lblVolume.Size = new System.Drawing.Size(45, 13);
+            this.lblVolume.TabIndex = 7;
+            this.lblVolume.Text = "Volume:";
+            // 
+            // grpSpatialSound
+            // 
+            this.grpSpatialSound.Controls.Add(this.cbDistanceModel);
+            this.grpSpatialSound.Controls.Add(this.lblDistanceModel);
+            this.grpSpatialSound.Controls.Add(this.nupRefDistance);
+            this.grpSpatialSound.Controls.Add(this.lblRefDistance);
+            this.grpSpatialSound.Controls.Add(this.nupRolloff);
+            this.grpSpatialSound.Controls.Add(this.lblRolloff);
+            this.grpSpatialSound.Controls.Add(this.nupMaxDistance);
+            this.grpSpatialSound.Controls.Add(this.lblMaxDistance);
+            this.grpSpatialSound.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.grpSpatialSound.Location = new System.Drawing.Point(20, 139);
+            this.grpSpatialSound.Name = "grpSpatialSound";
+            this.grpSpatialSound.Size = new System.Drawing.Size(292, 116);
+            this.grpSpatialSound.TabIndex = 6;
+            this.grpSpatialSound.TabStop = false;
+            // 
+            // cbDistanceModel
+            // 
+            this.cbDistanceModel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cbDistanceModel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.cbDistanceModel.FormattingEnabled = true;
+            this.cbDistanceModel.Items.AddRange(new object[] {
+            "linear",
+            "inverse",
+            "exponential"});
+            this.cbDistanceModel.Location = new System.Drawing.Point(105, 15);
+            this.cbDistanceModel.Name = "cbDistanceModel";
+            this.cbDistanceModel.Size = new System.Drawing.Size(123, 21);
+            this.cbDistanceModel.TabIndex = 11;
+            // 
+            // lblDistanceModel
+            // 
+            this.lblDistanceModel.AutoSize = true;
+            this.lblDistanceModel.Location = new System.Drawing.Point(6, 16);
+            this.lblDistanceModel.Name = "lblDistanceModel";
+            this.lblDistanceModel.Size = new System.Drawing.Size(83, 13);
+            this.lblDistanceModel.TabIndex = 7;
+            this.lblDistanceModel.Text = "Distance model:";
+            // 
+            // nupRefDistance
+            // 
+            this.nupRefDistance.DecimalPlaces = 2;
+            this.nupRefDistance.Increment = new decimal(new int[] {
+            1,
+            0,
+            0,
+            65536});
+            this.nupRefDistance.Location = new System.Drawing.Point(105, 90);
+            this.nupRefDistance.Maximum = new decimal(new int[] {
+            1000,
+            0,
+            0,
+            0});
+            this.nupRefDistance.Name = "nupRefDistance";
+            this.nupRefDistance.Size = new System.Drawing.Size(121, 20);
+            this.nupRefDistance.TabIndex = 5;
+            this.nupRefDistance.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
             // 
-            this.label7.AutoSize = true;
-            this.label7.Location = new System.Drawing.Point(18, 122);
-            this.label7.Name = "label7";
-            this.label7.Size = new System.Drawing.Size(60, 13);
-            this.label7.TabIndex = 12;
-            this.label7.Text = "Restitution:";
+            // lblRefDistance
+            // 
+            this.lblRefDistance.AutoSize = true;
+            this.lblRefDistance.Location = new System.Drawing.Point(7, 93);
+            this.lblRefDistance.Name = "lblRefDistance";
+            this.lblRefDistance.Size = new System.Drawing.Size(70, 13);
+            this.lblRefDistance.TabIndex = 6;
+            this.lblRefDistance.Text = "Ref distance:";
+            // 
+            // nupRolloff
+            // 
+            this.nupRolloff.DecimalPlaces = 2;
+            this.nupRolloff.Increment = new decimal(new int[] {
+            1,
+            0,
+            0,
+            65536});
+            this.nupRolloff.Location = new System.Drawing.Point(105, 64);
+            this.nupRolloff.Maximum = new decimal(new int[] {
+            1000,
+            0,
+            0,
+            0});
+            this.nupRolloff.Name = "nupRolloff";
+            this.nupRolloff.Size = new System.Drawing.Size(121, 20);
+            this.nupRolloff.TabIndex = 3;
+            this.nupRolloff.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            // 
+            // lblRolloff
+            // 
+            this.lblRolloff.AutoSize = true;
+            this.lblRolloff.Location = new System.Drawing.Point(7, 67);
+            this.lblRolloff.Name = "lblRolloff";
+            this.lblRolloff.Size = new System.Drawing.Size(40, 13);
+            this.lblRolloff.TabIndex = 4;
+            this.lblRolloff.Text = "Rolloff:";
+            // 
+            // nupMaxDistance
+            // 
+            this.nupMaxDistance.DecimalPlaces = 2;
+            this.nupMaxDistance.Location = new System.Drawing.Point(105, 38);
+            this.nupMaxDistance.Maximum = new decimal(new int[] {
+            100000,
+            0,
+            0,
+            0});
+            this.nupMaxDistance.Name = "nupMaxDistance";
+            this.nupMaxDistance.Size = new System.Drawing.Size(121, 20);
+            this.nupMaxDistance.TabIndex = 1;
+            this.nupMaxDistance.Value = new decimal(new int[] {
+            100,
+            0,
+            0,
+            0});
+            // 
+            // lblMaxDistance
+            // 
+            this.lblMaxDistance.AutoSize = true;
+            this.lblMaxDistance.Location = new System.Drawing.Point(6, 41);
+            this.lblMaxDistance.Name = "lblMaxDistance";
+            this.lblMaxDistance.Size = new System.Drawing.Size(73, 13);
+            this.lblMaxDistance.TabIndex = 2;
+            this.lblMaxDistance.Text = "Max distance:";
+            // 
+            // txtSound
+            // 
+            this.txtSound.Location = new System.Drawing.Point(21, 28);
+            this.txtSound.Name = "txtSound";
+            this.txtSound.Size = new System.Drawing.Size(249, 20);
+            this.txtSound.TabIndex = 5;
+            // 
+            // checkBox1
+            // 
+            this.checkBox1.AutoSize = true;
+            this.checkBox1.Checked = true;
+            this.checkBox1.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.checkBox1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.checkBox1.Location = new System.Drawing.Point(94, 54);
+            this.checkBox1.Name = "checkBox1";
+            this.checkBox1.Size = new System.Drawing.Size(47, 17);
+            this.checkBox1.TabIndex = 4;
+            this.checkBox1.Text = "Loop";
+            this.checkBox1.UseVisualStyleBackColor = true;
+            // 
+            // chkAutoPlay
+            // 
+            this.chkAutoPlay.AutoSize = true;
+            this.chkAutoPlay.Checked = true;
+            this.chkAutoPlay.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.chkAutoPlay.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkAutoPlay.Location = new System.Drawing.Point(21, 54);
+            this.chkAutoPlay.Name = "chkAutoPlay";
+            this.chkAutoPlay.Size = new System.Drawing.Size(67, 17);
+            this.chkAutoPlay.TabIndex = 3;
+            this.chkAutoPlay.Text = "Auto play";
+            this.chkAutoPlay.UseVisualStyleBackColor = true;
+            // 
+            // ofdOpenSound
+            // 
+            this.ofdOpenSound.Filter = "Sound files|*.wav;*.mp3";
             // 
             // ObjectPropertiesForm
             // 
@@ -423,7 +835,8 @@
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.CancelButton = this.butCancel;
-            this.ClientSize = new System.Drawing.Size(343, 634);
+            this.ClientSize = new System.Drawing.Size(667, 536);
+            this.Controls.Add(this.groupBox5);
             this.Controls.Add(this.groupBox4);
             this.Controls.Add(this.groupBox3);
             this.Controls.Add(this.groupBox2);
@@ -448,9 +861,23 @@
             ((System.ComponentModel.ISupportInitialize)(this.nupFrom)).EndInit();
             this.groupBox4.ResumeLayout(false);
             this.groupBox4.PerformLayout();
-            ((System.ComponentModel.ISupportInitialize)(this.nupMass)).EndInit();
-            ((System.ComponentModel.ISupportInitialize)(this.nupFriction)).EndInit();
             ((System.ComponentModel.ISupportInitialize)(this.nupRestitution)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupFriction)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupMass)).EndInit();
+            this.groupBox5.ResumeLayout(false);
+            this.groupBox5.PerformLayout();
+            this.grpDirectional.ResumeLayout(false);
+            this.grpDirectional.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupConeOuterGain)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupConeOuterAngle)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupConeInnerAngle)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupPlaybackRate)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupVolume)).EndInit();
+            this.grpSpatialSound.ResumeLayout(false);
+            this.grpSpatialSound.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupRefDistance)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupRolloff)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.nupMaxDistance)).EndInit();
             this.ResumeLayout(false);
 
         }
@@ -486,5 +913,32 @@
         private System.Windows.Forms.Label label7;
         private System.Windows.Forms.NumericUpDown nupFriction;
         private System.Windows.Forms.Label label5;
+        private System.Windows.Forms.GroupBox groupBox5;
+        private System.Windows.Forms.TextBox txtSound;
+        private System.Windows.Forms.CheckBox checkBox1;
+        private System.Windows.Forms.CheckBox chkAutoPlay;
+        private System.Windows.Forms.OpenFileDialog ofdOpenSound;
+        private System.Windows.Forms.GroupBox grpSpatialSound;
+        private System.Windows.Forms.NumericUpDown nupMaxDistance;
+        private System.Windows.Forms.Label lblMaxDistance;
+        private System.Windows.Forms.NumericUpDown nupPlaybackRate;
+        private System.Windows.Forms.Label lblPlaybackRate;
+        private System.Windows.Forms.NumericUpDown nupVolume;
+        private System.Windows.Forms.Label lblVolume;
+        private System.Windows.Forms.NumericUpDown nupRefDistance;
+        private System.Windows.Forms.Label lblRefDistance;
+        private System.Windows.Forms.NumericUpDown nupRolloff;
+        private System.Windows.Forms.Label lblRolloff;
+        private System.Windows.Forms.Label lblDistanceModel;
+        private System.Windows.Forms.GroupBox grpDirectional;
+        private System.Windows.Forms.NumericUpDown nupConeOuterGain;
+        private System.Windows.Forms.Label lblConeOuterGain;
+        private System.Windows.Forms.NumericUpDown nupConeOuterAngle;
+        private System.Windows.Forms.Label lblConeOuterAngle;
+        private System.Windows.Forms.NumericUpDown nupConeInnerAngle;
+        private System.Windows.Forms.Label lblConeInnerAngle;
+        private System.Windows.Forms.CheckBox chkDirectional;
+        private System.Windows.Forms.ComboBox cbDistanceModel;
+        private System.Windows.Forms.Button cmdFileBrowse;
     }
 }

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

@@ -33,6 +33,23 @@ namespace Max2Babylon
             Tools.UpdateNumericUpDown(nupFriction, objects, "babylonjs_friction");
             Tools.UpdateNumericUpDown(nupRestitution, objects, "babylonjs_restitution");
             Tools.UpdateComboBox(cbImpostor, objects, "babylonjs_impostor");
+
+            Tools.UpdateCheckBox(chkAutoPlay, objects, "babylonjs_sound_autoplay");
+            Tools.UpdateCheckBox(chkLoop, objects, "babylonjs_sound_loop");
+            Tools.UpdateNumericUpDown(nupVolume, objects, "babylonjs_sound_volume");
+            Tools.UpdateNumericUpDown(nupPlaybackRate, objects, "babylonjs_sound_playbackrate");
+
+            Tools.UpdateComboBox(cbDistanceModel, objects, "babylonjs_sound_distancemodel");
+            Tools.UpdateNumericUpDown(nupMaxDistance, objects, "babylonjs_sound_maxdistance");
+            Tools.UpdateNumericUpDown(nupRolloff, objects, "babylonjs_sound_rolloff");
+            Tools.UpdateNumericUpDown(nupRefDistance, objects, "babylonjs_sound_refdistance");
+
+            Tools.UpdateCheckBox(chkDirectional, objects, "babylonjs_sound_directional");
+            Tools.UpdateNumericUpDown(nupConeInnerAngle, objects, "babylonjs_sound_coneinnerangle");
+            Tools.UpdateNumericUpDown(nupConeOuterAngle, objects, "babylonjs_sound_coneouterangle");
+            Tools.UpdateNumericUpDown(nupConeOuterGain, objects, "babylonjs_sound_coneoutergain");
+
+            Tools.UpdateTextBox(txtSound, objects, "babylonjs_sound_filename");
         }
 
         private void ObjectPropertiesForm_Load(object sender, EventArgs e)
@@ -65,11 +82,41 @@ namespace Max2Babylon
             Tools.PrepareNumericUpDown(nupRestitution, objects, "babylonjs_restitution", 0.2f);
 
             Tools.PrepareComboBox(cbImpostor, objects[0], "babylonjs_impostor", "None");
+
+            Tools.PrepareCheckBox(chkAutoPlay, objects, "babylonjs_sound_autoplay", 1);
+            Tools.PrepareCheckBox(chkLoop, objects, "babylonjs_sound_loop", 1);
+            Tools.PrepareNumericUpDown(nupVolume, objects, "babylonjs_sound_volume", 1.0f);
+            Tools.PrepareNumericUpDown(nupPlaybackRate, objects, "babylonjs_sound_playbackrate", 1.0f);
+
+            Tools.PrepareComboBox(cbDistanceModel, objects[0], "babylonjs_sound_distancemodel", "linear");
+            Tools.PrepareNumericUpDown(nupMaxDistance, objects, "babylonjs_sound_maxdistance", 100.0f);
+            Tools.PrepareNumericUpDown(nupRolloff, objects, "babylonjs_sound_rolloff", 1.0f);
+            Tools.PrepareNumericUpDown(nupRefDistance, objects, "babylonjs_sound_refdistance", 1.0f);
+
+            Tools.PrepareCheckBox(chkDirectional, objects, "babylonjs_sound_directional", 0);
+            Tools.PrepareNumericUpDown(nupConeInnerAngle, objects, "babylonjs_sound_coneinnerangle", 360.00f);
+            Tools.PrepareNumericUpDown(nupConeOuterAngle, objects, "babylonjs_sound_coneouterangle", 360.00f);
+            Tools.PrepareNumericUpDown(nupConeOuterGain, objects, "babylonjs_sound_coneoutergain", 1.0f);
+
+            Tools.PrepareTextBox(txtSound, objects[0], "babylonjs_sound_filename");
         }
 
         private void chkAutoAnimate_CheckedChanged(object sender, EventArgs e)
         {
             grpAutoAnimate.Enabled = chkAutoAnimate.Checked;
         }
+
+        private void chkDirectional_CheckedChanged(object sender, EventArgs e)
+        {
+            grpDirectional.Enabled = chkDirectional.Checked;
+        }
+
+        private void cmdFileBrowse_Click(object sender, EventArgs e)
+        {
+            if (ofdOpenSound.ShowDialog() == DialogResult.OK)
+            {
+                txtSound.Text = ofdOpenSound.FileName;
+            }
+        }
     }
 }

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

@@ -117,4 +117,7 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <metadata name="ofdOpenSound.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
 </root>

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

@@ -35,8 +35,18 @@
             this.butOK = new System.Windows.Forms.Button();
             this.groupBox2 = new System.Windows.Forms.GroupBox();
             this.chkQuaternions = new System.Windows.Forms.CheckBox();
+            this.groupBox3 = new System.Windows.Forms.GroupBox();
+            this.cmdBrowse = new System.Windows.Forms.Button();
+            this.txtSound = new System.Windows.Forms.TextBox();
+            this.chkLoop = new System.Windows.Forms.CheckBox();
+            this.chkAutoPlay = new System.Windows.Forms.CheckBox();
+            this.ofdOpenSound = new System.Windows.Forms.OpenFileDialog();
+            this.nupVolume = new System.Windows.Forms.NumericUpDown();
+            this.lblVolume = new System.Windows.Forms.Label();
             this.groupBox1.SuspendLayout();
             this.groupBox2.SuspendLayout();
+            this.groupBox3.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupVolume)).BeginInit();
             this.SuspendLayout();
             // 
             // groupBox1
@@ -75,7 +85,7 @@
             this.butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
             this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
             this.butCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butCancel.Location = new System.Drawing.Point(174, 355);
+            this.butCancel.Location = new System.Drawing.Point(174, 351);
             this.butCancel.Name = "butCancel";
             this.butCancel.Size = new System.Drawing.Size(75, 23);
             this.butCancel.TabIndex = 4;
@@ -87,7 +97,7 @@
             this.butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
             this.butOK.DialogResult = System.Windows.Forms.DialogResult.OK;
             this.butOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
-            this.butOK.Location = new System.Drawing.Point(93, 355);
+            this.butOK.Location = new System.Drawing.Point(93, 351);
             this.butOK.Name = "butOK";
             this.butOK.Size = new System.Drawing.Size(75, 23);
             this.butOK.TabIndex = 3;
@@ -117,13 +127,111 @@
             this.chkQuaternions.Text = "Export quaternions instead of Euler angles";
             this.chkQuaternions.UseVisualStyleBackColor = true;
             // 
+            // groupBox3
+            // 
+            this.groupBox3.Controls.Add(this.nupVolume);
+            this.groupBox3.Controls.Add(this.lblVolume);
+            this.groupBox3.Controls.Add(this.cmdBrowse);
+            this.groupBox3.Controls.Add(this.txtSound);
+            this.groupBox3.Controls.Add(this.chkLoop);
+            this.groupBox3.Controls.Add(this.chkAutoPlay);
+            this.groupBox3.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.groupBox3.Location = new System.Drawing.Point(12, 198);
+            this.groupBox3.Name = "groupBox3";
+            this.groupBox3.Size = new System.Drawing.Size(319, 136);
+            this.groupBox3.TabIndex = 6;
+            this.groupBox3.TabStop = false;
+            this.groupBox3.Text = "Sound";
+            // 
+            // cmdBrowse
+            // 
+            this.cmdBrowse.Anchor = System.Windows.Forms.AnchorStyles.Right;
+            this.cmdBrowse.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.cmdBrowse.Location = new System.Drawing.Point(276, 28);
+            this.cmdBrowse.Name = "cmdBrowse";
+            this.cmdBrowse.Size = new System.Drawing.Size(37, 23);
+            this.cmdBrowse.TabIndex = 6;
+            this.cmdBrowse.Text = "...";
+            this.cmdBrowse.UseVisualStyleBackColor = true;
+            this.cmdBrowse.Click += new System.EventHandler(this.cmdBrowse_Click);
+            // 
+            // txtSound
+            // 
+            this.txtSound.Location = new System.Drawing.Point(21, 28);
+            this.txtSound.Name = "txtSound";
+            this.txtSound.Size = new System.Drawing.Size(249, 20);
+            this.txtSound.TabIndex = 5;
+            // 
+            // chkLoop
+            // 
+            this.chkLoop.AutoSize = true;
+            this.chkLoop.Checked = true;
+            this.chkLoop.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.chkLoop.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkLoop.Location = new System.Drawing.Point(21, 77);
+            this.chkLoop.Name = "chkLoop";
+            this.chkLoop.Size = new System.Drawing.Size(47, 17);
+            this.chkLoop.TabIndex = 4;
+            this.chkLoop.Text = "Loop";
+            this.chkLoop.UseVisualStyleBackColor = true;
+            // 
+            // chkAutoPlay
+            // 
+            this.chkAutoPlay.AutoSize = true;
+            this.chkAutoPlay.Checked = true;
+            this.chkAutoPlay.CheckState = System.Windows.Forms.CheckState.Checked;
+            this.chkAutoPlay.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.chkAutoPlay.Location = new System.Drawing.Point(21, 54);
+            this.chkAutoPlay.Name = "chkAutoPlay";
+            this.chkAutoPlay.Size = new System.Drawing.Size(67, 17);
+            this.chkAutoPlay.TabIndex = 3;
+            this.chkAutoPlay.Text = "Auto play";
+            this.chkAutoPlay.UseVisualStyleBackColor = true;
+            // 
+            // ofdOpenSound
+            // 
+            this.ofdOpenSound.Filter = "Sound files|*.wav;*.mp3";
+            // 
+            // nupVolume
+            // 
+            this.nupVolume.DecimalPlaces = 2;
+            this.nupVolume.Increment = new decimal(new int[] {
+            1,
+            0,
+            0,
+            65536});
+            this.nupVolume.Location = new System.Drawing.Point(150, 108);
+            this.nupVolume.Maximum = new decimal(new int[] {
+            10,
+            0,
+            0,
+            0});
+            this.nupVolume.Name = "nupVolume";
+            this.nupVolume.Size = new System.Drawing.Size(120, 20);
+            this.nupVolume.TabIndex = 10;
+            this.nupVolume.Value = new decimal(new int[] {
+            1,
+            0,
+            0,
+            0});
+            // 
+            // lblVolume
+            // 
+            this.lblVolume.AutoSize = true;
+            this.lblVolume.Location = new System.Drawing.Point(18, 110);
+            this.lblVolume.Name = "lblVolume";
+            this.lblVolume.Size = new System.Drawing.Size(45, 13);
+            this.lblVolume.TabIndex = 9;
+            this.lblVolume.Text = "Volume:";
+            // 
             // ScenePropertiesForm
             // 
             this.AcceptButton = this.butOK;
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.CancelButton = this.butCancel;
-            this.ClientSize = new System.Drawing.Size(343, 390);
+            this.ClientSize = new System.Drawing.Size(343, 386);
+            this.Controls.Add(this.groupBox3);
             this.Controls.Add(this.groupBox2);
             this.Controls.Add(this.butCancel);
             this.Controls.Add(this.butOK);
@@ -137,6 +245,9 @@
             this.groupBox1.PerformLayout();
             this.groupBox2.ResumeLayout(false);
             this.groupBox2.PerformLayout();
+            this.groupBox3.ResumeLayout(false);
+            this.groupBox3.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.nupVolume)).EndInit();
             this.ResumeLayout(false);
 
         }
@@ -150,5 +261,13 @@
         private System.Windows.Forms.Button butOK;
         private System.Windows.Forms.GroupBox groupBox2;
         private System.Windows.Forms.CheckBox chkQuaternions;
+        private System.Windows.Forms.GroupBox groupBox3;
+        private System.Windows.Forms.CheckBox chkLoop;
+        private System.Windows.Forms.CheckBox chkAutoPlay;
+        private System.Windows.Forms.Button cmdBrowse;
+        private System.Windows.Forms.TextBox txtSound;
+        private System.Windows.Forms.OpenFileDialog ofdOpenSound;
+        private System.Windows.Forms.NumericUpDown nupVolume;
+        private System.Windows.Forms.Label lblVolume;
     }
 }

+ 23 - 1
Exporters/3ds Max/Max2Babylon/Forms/ScenePropertiesForm.cs

@@ -1,5 +1,7 @@
 using System;
+using System.Collections.Generic;
 using System.Windows.Forms;
+using Autodesk.Max;
 
 namespace Max2Babylon
 {
@@ -14,12 +16,32 @@ namespace Max2Babylon
         {
             Tools.UpdateVector3Control(gravityControl, Loader.Core.RootNode, "babylonjs_gravity");
             Tools.UpdateCheckBox(chkQuaternions, Loader.Core.RootNode, "babylonjs_exportquaternions");
+
+            Tools.UpdateCheckBox(chkAutoPlay, Loader.Core.RootNode, "babylonjs_sound_autoplay");
+            Tools.UpdateCheckBox(chkLoop, Loader.Core.RootNode, "babylonjs_sound_loop");
+            Tools.UpdateNumericUpDown(nupVolume, new List<IINode> { Loader.Core.RootNode }, "babylonjs_sound_volume");
+
+            Tools.UpdateTextBox(txtSound, new List<IINode> { Loader.Core.RootNode }, "babylonjs_sound_filename");
         }
 
         private void ScenePropertiesForm_Load(object sender, EventArgs e)
         {
-            Tools.PrepareVector3Control(gravityControl, Loader.Core.RootNode, "babylonjs_gravity", 0, -0.9f, 0);
+            Tools.PrepareVector3Control(gravityControl, Loader.Core.RootNode, "babylonjs_gravity", 0, -0.9f);
             Tools.PrepareCheckBox(chkQuaternions, Loader.Core.RootNode, "babylonjs_exportquaternions", 1);
+
+            Tools.PrepareCheckBox(chkAutoPlay, Loader.Core.RootNode, "babylonjs_sound_autoplay", 1);
+            Tools.PrepareCheckBox(chkLoop, Loader.Core.RootNode, "babylonjs_sound_loop", 1);
+            Tools.PrepareNumericUpDown(nupVolume, new List<IINode>{Loader.Core.RootNode}, "babylonjs_sound_volume", 1.0f);
+
+            Tools.PrepareTextBox(txtSound, Loader.Core.RootNode, "babylonjs_sound_filename");
+        }
+
+        private void cmdBrowse_Click(object sender, EventArgs e)
+        {
+            if (ofdOpenSound.ShowDialog() == DialogResult.OK)
+            {
+                txtSound.Text = ofdOpenSound.FileName;
+            }
         }
     }
 }

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

@@ -117,4 +117,7 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <metadata name="ofdOpenSound.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
 </root>

+ 5 - 5
Exporters/3ds Max/Max2Babylon/GlobalUtility.cs

@@ -54,7 +54,7 @@ namespace Max2Babylon
                 actionTable = Loader.Global.ActionTable.Create(idActionTable, 0, ref actionTableName);
                 actionTable.AppendOperation(new BabylonExportActionItem());
                 actionTable.AppendOperation(new BabylonPropertiesActionItem());
-                actionTable.AppendOperation(new BabylonActionsBuilderActionItem());
+               // actionTable.AppendOperation(new BabylonActionsBuilderActionItem());
                 actionCallback = new BabylonActionCallback();
 
                 actionManager.RegisterActionTable(actionTable);
@@ -107,11 +107,11 @@ namespace Max2Babylon
                 menuItemBabylon = Loader.Global.IMenuItem;
                 menuItemBabylon.Title = "Babylon Properties";
                 menuItemBabylon.ActionItem = actionTable[1];
-                menu.AddItem(menuItemBabylon, -1);
+                //menu.AddItem(menuItemBabylon, -1);
 
-                menuItemBabylon = Loader.Global.IMenuItem;
-                menuItemBabylon.Title = "Babylon Actions Builder";
-                menuItemBabylon.ActionItem = actionTable[2];
+                //menuItemBabylon = Loader.Global.IMenuItem;
+                //menuItemBabylon.Title = "Babylon Actions Builder";
+                //menuItemBabylon.ActionItem = actionTable[2];
                 menu.AddItem(menuItemBabylon, -1);
 
                 menuItem = Loader.Global.IMenuItem;

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

@@ -675,6 +675,12 @@ namespace Max2Babylon
             }
         }
 
+        public static void PrepareTextBox(TextBox textBox, IINode node, string propertyName, string defaultValue = "")
+        {
+            var state = node.GetStringProperty(propertyName, defaultValue);
+            textBox.Text = state;
+        }
+
         public static void PrepareComboBox(ComboBox comboBox, IINode node, string propertyName, string defaultValue)
         {
             comboBox.SelectedItem = node.GetStringProperty(propertyName, defaultValue);
@@ -700,6 +706,19 @@ namespace Max2Babylon
             }
         }
 
+        public static void UpdateTextBox(TextBox textBox, List<IINode> nodes, string propertyName)
+        {
+            foreach (var node in nodes)
+            {
+                var value = textBox.Text;
+#if MAX2015
+                node.SetUserPropString(propertyName, value);
+#else
+                node.SetUserPropString(ref propertyName, ref value);
+#endif
+            }
+        }
+
         public static void PrepareNumericUpDown(NumericUpDown nup, List<IINode> nodes, string propertyName, float defaultState = 0)
         {
             nup.Value = (decimal)nodes[0].GetFloatProperty(propertyName, defaultState);

+ 29 - 2
babylon.2.1-alpha.debug.js

@@ -1902,8 +1902,8 @@ var __extends = this.__extends || function (d, b) {
             var tmp23 = SIMD.float32x4.shuffle(f, zero, 0, 1, 4, 5);
             var a0 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
             var a1 = SIMD.float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7);
-            var tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7);
-            var tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7);
+            tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7);
+            tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7);
             var a2 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
             var a3 = SIMD.float32x4(0.0, 0.0, 0.0, 1.0);
             // cc.kmMat4Translation(translate, -pEye.x, -pEye.y, -pEye.z);
@@ -2746,6 +2746,33 @@ var __extends = this.__extends || function (d, b) {
         Matrix.LookAtLHToRef = Matrix.LookAtLHToRefSIMD;
         Vector3.TransformCoordinatesToRef = Vector3.TransformCoordinatesToRefSIMD;
         Vector3.TransformCoordinatesFromFloatsToRef = Vector3.TransformCoordinatesFromFloatsToRefSIMD;
+        Object.defineProperty(BABYLON.Vector3.prototype, "x", {
+            get: function () {
+                return this._data[0];
+            },
+            set: function (value) {
+                if (!this._data) {
+                    this._data = new Float32Array(3);
+                }
+                this._data[0] = value;
+            }
+        });
+        Object.defineProperty(BABYLON.Vector3.prototype, "y", {
+            get: function () {
+                return this._data[1];
+            },
+            set: function (value) {
+                this._data[1] = value;
+            }
+        });
+        Object.defineProperty(BABYLON.Vector3.prototype, "z", {
+            get: function () {
+                return this._data[2];
+            },
+            set: function (value) {
+                this._data[2] = value;
+            }
+        });
     }
 })(BABYLON || (BABYLON = {}));
 //# sourceMappingURL=babylon.math.js.mapvar BABYLON;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2 - 2
babylon.2.1-alpha.js