Browse Source

Getting things ready for action builder
More SIMD goodness

David Catuhe 10 năm trước cách đây
mục cha
commit
e6e9df65ff
50 tập tin đã thay đổi với 3935 bổ sung857 xóa
  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%;
 }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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
-        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
-        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;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
babylon.2.1-alpha.js